зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1117242 - Part 2: SavedFrame accessors should always check principals. r=jandem
This commit is contained in:
Родитель
272bbfc82d
Коммит
2b2ed10b4d
|
@ -967,9 +967,30 @@ SaveStack(JSContext *cx, unsigned argc, jsval *vp)
|
|||
maxFrameCount = d;
|
||||
}
|
||||
|
||||
Rooted<JSObject*> stack(cx);
|
||||
if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
|
||||
JSCompartment *targetCompartment = cx->compartment();
|
||||
if (args.length() >= 2) {
|
||||
if (!args[1].isObject()) {
|
||||
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
|
||||
JSDVG_SEARCH_STACK, args[0], JS::NullPtr(),
|
||||
"not an object", NULL);
|
||||
return false;
|
||||
}
|
||||
RootedObject obj(cx, UncheckedUnwrap(&args[1].toObject()));
|
||||
if (!obj)
|
||||
return false;
|
||||
targetCompartment = obj->compartment();
|
||||
}
|
||||
|
||||
RootedObject stack(cx);
|
||||
{
|
||||
AutoCompartment ac(cx, targetCompartment);
|
||||
if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stack && !cx->compartment()->wrap(cx, &stack))
|
||||
return false;
|
||||
|
||||
args.rval().setObjectOrNull(stack);
|
||||
return true;
|
||||
}
|
||||
|
@ -2396,13 +2417,15 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
|||
" SavedStacks cache."),
|
||||
|
||||
JS_FN_HELP("saveStack", SaveStack, 0, 0,
|
||||
"saveStack()",
|
||||
" Capture a stack.\n"),
|
||||
"saveStack([maxDepth [, compartment]])",
|
||||
" Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
|
||||
" of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
|
||||
" with the given object's compartment."),
|
||||
|
||||
JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
|
||||
"enableTrackAllocations()",
|
||||
" Start capturing the JS stack at every allocation. Note that this sets an "
|
||||
" object metadata callback that will override any other object metadata "
|
||||
" Start capturing the JS stack at every allocation. Note that this sets an\n"
|
||||
" object metadata callback that will override any other object metadata\n"
|
||||
" callback that may be set."),
|
||||
|
||||
JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// With arrows representing child-to-parent links, create a SavedFrame stack
|
||||
// like this:
|
||||
//
|
||||
// high.a -> low.b
|
||||
//
|
||||
// in `low`'s compartment and give `low` a reference to this stack. Assert the
|
||||
// stack's youngest frame's properties doesn't leak information about `high.a`
|
||||
// that `low` shouldn't have access to, and instead returns information about
|
||||
// `low.b`.
|
||||
|
||||
var low = newGlobal({ principal: 0 });
|
||||
var high = newGlobal({ principal: 0xfffff });
|
||||
|
||||
low.high = high;
|
||||
high.low = low;
|
||||
|
||||
high.eval("function a() { return saveStack(0, low); }");
|
||||
low.eval("function b() { return high.a(); }")
|
||||
|
||||
var stack = low.b();
|
||||
|
||||
assertEq(stack.functionDisplayName, "b");
|
||||
assertEq(stack.parent, null);
|
|
@ -0,0 +1,15 @@
|
|||
// Test what happens when a compartment gets a SavedFrame that it doesn't have
|
||||
// the principals to access any of its frames.
|
||||
|
||||
var low = newGlobal({ principal: 0 });
|
||||
var high = newGlobal({ principal: 0xfffff });
|
||||
|
||||
low.high = high;
|
||||
high.low = low;
|
||||
|
||||
high.eval("function a() { return saveStack(1, low); }");
|
||||
var stack = low.eval("high.a();")
|
||||
|
||||
assertEq(stack.functionDisplayName, null);
|
||||
assertEq(stack.parent, null);
|
||||
assertEq(stack.toString(), "");
|
|
@ -309,21 +309,42 @@ SavedFrame::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* static */ SavedFrame *
|
||||
SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
|
||||
// Return the first SavedFrame in the chain that starts with |frame| whose
|
||||
// principals are subsumed by |principals|, according to |subsumes|. If there is
|
||||
// no such frame, return nullptr.
|
||||
static SavedFrame *
|
||||
GetFirstSubsumedFrame(JSContext *cx, SavedFrame *frame)
|
||||
{
|
||||
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
if (!subsumes)
|
||||
return frame;
|
||||
|
||||
JSPrincipals *principals = cx->compartment()->principals;
|
||||
if (!principals)
|
||||
return frame;
|
||||
|
||||
while (frame && !subsumes(principals, frame->getPrincipals()))
|
||||
frame = frame->getParent();
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName,
|
||||
MutableHandleSavedFrame frame)
|
||||
{
|
||||
const Value &thisValue = args.thisv();
|
||||
|
||||
if (!thisValue.isObject()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject &thisObject = thisValue.toObject();
|
||||
if (!thisObject.is<SavedFrame>()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
SavedFrame::class_.name, fnName, thisObject.getClass()->name);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for SavedFrame.prototype, which has the same class as SavedFrame
|
||||
|
@ -332,10 +353,13 @@ SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
|
|||
if (thisObject.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
SavedFrame::class_.name, fnName, "prototype object");
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return &thisObject.as<SavedFrame>();
|
||||
// The caller might not have the principals to see this frame's data, so get
|
||||
// the first one they _do_ have access to.
|
||||
frame.set(GetFirstSubsumedFrame(cx, &thisObject.as<SavedFrame>()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the SavedFrame * from the current this value and handle any errors that
|
||||
|
@ -346,19 +370,24 @@ SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
|
|||
// - unsigned argc
|
||||
// - Value *vp
|
||||
// - const char *fnName
|
||||
// - Value defaultVal
|
||||
// These parameters will be defined after calling this macro:
|
||||
// - CallArgs args
|
||||
// - Rooted<SavedFrame *> frame (will be non-null)
|
||||
#define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame) \
|
||||
CallArgs args = CallArgsFromVp(argc, vp); \
|
||||
RootedSavedFrame frame(cx, checkThis(cx, args, fnName)); \
|
||||
if (!frame) \
|
||||
return false
|
||||
#define THIS_SAVEDFRAME(cx, argc, vp, fnName, defaultVal, args, frame) \
|
||||
CallArgs args = CallArgsFromVp(argc, vp); \
|
||||
RootedSavedFrame frame(cx); \
|
||||
if (!checkThis(cx, args, fnName, &frame)) \
|
||||
return false; \
|
||||
if (!frame) { \
|
||||
args.rval().set(defaultVal); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get source)", NullValue(), args, frame);
|
||||
args.rval().setString(frame->getSource());
|
||||
return true;
|
||||
}
|
||||
|
@ -366,7 +395,7 @@ SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp)
|
|||
/* static */ bool
|
||||
SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get line)", NullValue(), args, frame);
|
||||
uint32_t line = frame->getLine();
|
||||
args.rval().setNumber(line);
|
||||
return true;
|
||||
|
@ -375,7 +404,7 @@ SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp)
|
|||
/* static */ bool
|
||||
SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get column)", NullValue(), args, frame);
|
||||
uint32_t column = frame->getColumn();
|
||||
args.rval().setNumber(column);
|
||||
return true;
|
||||
|
@ -384,7 +413,7 @@ SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp)
|
|||
/* static */ bool
|
||||
SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", NullValue(), args, frame);
|
||||
RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if (name)
|
||||
args.rval().setString(name);
|
||||
|
@ -396,30 +425,21 @@ SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
|
|||
/* static */ bool
|
||||
SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
|
||||
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
JSPrincipals *principals = cx->compartment()->principals;
|
||||
|
||||
do
|
||||
frame = frame->getParent();
|
||||
while (frame && principals && subsumes &&
|
||||
!subsumes(principals, frame->getPrincipals()));
|
||||
|
||||
args.rval().setObjectOrNull(frame);
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", NullValue(), args, frame);
|
||||
args.rval().setObjectOrNull(GetFirstSubsumedFrame(cx, frame->getParent()));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "toString", StringValue(cx->runtime()->emptyString), args, frame);
|
||||
StringBuffer sb(cx);
|
||||
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
JSPrincipals *principals = cx->compartment()->principals;
|
||||
DebugOnly<JSSubsumesOp> subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
DebugOnly<JSPrincipals *> principals = cx->compartment()->principals;
|
||||
|
||||
do {
|
||||
if (principals && subsumes && !subsumes(principals, frame->getPrincipals()))
|
||||
continue;
|
||||
MOZ_ASSERT_IF(principals && subsumes, (*subsumes)(principals, frame->getPrincipals()));
|
||||
if (frame->isSelfHosted())
|
||||
continue;
|
||||
|
||||
|
@ -435,7 +455,7 @@ SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
} while ((frame = frame->getParent()));
|
||||
} while ((frame = GetFirstSubsumedFrame(cx, frame->getParent())));
|
||||
|
||||
JSString *str = sb.finishString();
|
||||
if (!str)
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
class SavedFrame;
|
||||
typedef JS::Handle<SavedFrame*> HandleSavedFrame;
|
||||
typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
|
||||
typedef JS::Rooted<SavedFrame*> RootedSavedFrame;
|
||||
|
||||
class SavedFrame : public NativeObject {
|
||||
friend class SavedStacks;
|
||||
|
||||
|
@ -82,13 +87,10 @@ class SavedFrame : public NativeObject {
|
|||
bool parentMoved();
|
||||
void updatePrivateParent();
|
||||
|
||||
static SavedFrame *checkThis(JSContext *cx, CallArgs &args, const char *fnName);
|
||||
static bool checkThis(JSContext *cx, CallArgs &args, const char *fnName,
|
||||
MutableHandleSavedFrame frame);
|
||||
};
|
||||
|
||||
typedef JS::Handle<SavedFrame*> HandleSavedFrame;
|
||||
typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
|
||||
typedef JS::Rooted<SavedFrame*> RootedSavedFrame;
|
||||
|
||||
struct SavedFrame::HashPolicy
|
||||
{
|
||||
typedef SavedFrame::Lookup Lookup;
|
||||
|
|
Загрузка…
Ссылка в новой задаче