зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1322273: Cut wrappers that point out of a browser compartment when nuking it. r=bholley
MozReview-Commit-ID: FLS3xRgih2u --HG-- extra : rebase_source : ffae5590afe3efcb70cdda6386f5b1d71123943c extra : absorb_source : 8b3db1348e793dabca338841dc18b6d9021c6ef8
This commit is contained in:
Родитель
fca1830f46
Коммит
42bebe8564
|
@ -9406,21 +9406,16 @@ public:
|
|||
nsAutoString addonId;
|
||||
if (NS_SUCCEEDED(pc->GetAddonId(addonId)) && !addonId.IsEmpty()) {
|
||||
// We want to nuke all references to the add-on compartment.
|
||||
js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(),
|
||||
js::SingleCompartment(cpt),
|
||||
win->IsInnerWindow() ? js::DontNukeWindowReferences
|
||||
: js::NukeWindowReferences);
|
||||
|
||||
// Now mark the compartment as nuked and non-scriptable.
|
||||
auto compartmentPrivate = xpc::CompartmentPrivate::Get(cpt);
|
||||
compartmentPrivate->wasNuked = true;
|
||||
compartmentPrivate->scriptability.Block();
|
||||
xpc::NukeAllWrappersForCompartment(cx, cpt,
|
||||
win->IsInnerWindow() ? js::DontNukeWindowReferences
|
||||
: js::NukeWindowReferences);
|
||||
} else {
|
||||
// We only want to nuke wrappers for the chrome->content case
|
||||
js::NukeCrossCompartmentWrappers(cx, BrowserCompartmentMatcher(),
|
||||
js::SingleCompartment(cpt),
|
||||
win->IsInnerWindow() ? js::DontNukeWindowReferences
|
||||
: js::NukeWindowReferences);
|
||||
: js::NukeWindowReferences,
|
||||
js::NukeIncomingReferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1130,12 +1130,17 @@ class RegExpGuard;
|
|||
extern JS_FRIEND_API(bool)
|
||||
RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared);
|
||||
|
||||
/* Implemented in jswrapper.cpp. */
|
||||
/* Implemented in CrossCompartmentWrapper.cpp. */
|
||||
typedef enum NukeReferencesToWindow {
|
||||
NukeWindowReferences,
|
||||
DontNukeWindowReferences
|
||||
} NukeReferencesToWindow;
|
||||
|
||||
typedef enum NukeReferencesFromTarget {
|
||||
NukeAllReferences,
|
||||
NukeIncomingReferences,
|
||||
} NukeReferencesFromTarget;
|
||||
|
||||
/*
|
||||
* These filters are designed to be ephemeral stack classes, and thus don't
|
||||
* do any rooting or holding of their members.
|
||||
|
@ -1178,7 +1183,8 @@ extern JS_FRIEND_API(bool)
|
|||
NukeCrossCompartmentWrappers(JSContext* cx,
|
||||
const CompartmentFilter& sourceFilter,
|
||||
const CompartmentFilter& targetFilter,
|
||||
NukeReferencesToWindow nukeReferencesToWindow);
|
||||
NukeReferencesToWindow nukeReferencesToWindow,
|
||||
NukeReferencesFromTarget nukeReferencesFromTarget);
|
||||
|
||||
/* Specify information about DOMProxy proxies in the DOM, for use by ICs. */
|
||||
|
||||
|
|
|
@ -513,20 +513,23 @@ JS_FRIEND_API(bool)
|
|||
js::NukeCrossCompartmentWrappers(JSContext* cx,
|
||||
const CompartmentFilter& sourceFilter,
|
||||
const CompartmentFilter& targetFilter,
|
||||
js::NukeReferencesToWindow nukeReferencesToWindow)
|
||||
js::NukeReferencesToWindow nukeReferencesToWindow,
|
||||
js::NukeReferencesFromTarget nukeReferencesFromTarget)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
JSRuntime* rt = cx->runtime();
|
||||
|
||||
EvictAllNurseries(rt);
|
||||
|
||||
// Iterate through scopes looking for system cross compartment wrappers
|
||||
// that point to an object that shares a global with obj.
|
||||
|
||||
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
|
||||
if (!sourceFilter.match(c))
|
||||
continue;
|
||||
|
||||
// If the compartment matches both the source and target filter, we may
|
||||
// want to cut both incoming and outgoing wrappers.
|
||||
bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
|
||||
targetFilter.match(c));
|
||||
|
||||
// Iterate the wrappers looking for anything interesting.
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
// Some cross-compartment wrappers are for strings. We're not
|
||||
|
@ -538,13 +541,15 @@ js::NukeCrossCompartmentWrappers(JSContext* cx,
|
|||
AutoWrapperRooter wobj(cx, WrapperValue(e));
|
||||
JSObject* wrapped = UncheckedUnwrap(wobj);
|
||||
|
||||
// We only skip nuking window references that point to a target
|
||||
// compartment, not the ones that belong to it.
|
||||
if (nukeReferencesToWindow == DontNukeWindowReferences &&
|
||||
IsWindowProxy(wrapped))
|
||||
MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (targetFilter.match(wrapped->compartment())) {
|
||||
if (MOZ_UNLIKELY(nukeAll) || targetFilter.match(wrapped->compartment())) {
|
||||
// We found a wrapper to nuke.
|
||||
e.removeFront();
|
||||
NukeCrossCompartmentWrapper(cx, wobj);
|
||||
|
|
|
@ -3020,14 +3020,8 @@ nsXPCComponents_Utils::NukeSandbox(HandleValue obj, JSContext* cx)
|
|||
NS_ENSURE_TRUE(IsWrapper(wrapper), NS_ERROR_INVALID_ARG);
|
||||
RootedObject sb(cx, UncheckedUnwrap(wrapper));
|
||||
NS_ENSURE_TRUE(IsSandbox(sb), NS_ERROR_INVALID_ARG);
|
||||
NukeCrossCompartmentWrappers(cx, AllCompartments(),
|
||||
SingleCompartment(GetObjectCompartment(sb)),
|
||||
NukeWindowReferences);
|
||||
|
||||
// Now mark the compartment as nuked and non-scriptable.
|
||||
auto compartmentPrivate = xpc::CompartmentPrivate::Get(sb);
|
||||
compartmentPrivate->wasNuked = true;
|
||||
compartmentPrivate->scriptability.Block();
|
||||
xpc::NukeAllWrappersForCompartment(cx, GetObjectCompartment(sb));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -580,6 +580,37 @@ CurrentWindowOrNull(JSContext* cx)
|
|||
return glob ? WindowOrNull(glob) : nullptr;
|
||||
}
|
||||
|
||||
// Nukes all wrappers into or out of the given compartment, and prevents new
|
||||
// wrappers from being created. Additionally marks the compartment as
|
||||
// unscriptable after wrappers have been nuked.
|
||||
//
|
||||
// Note: This should *only* be called for browser or extension compartments.
|
||||
// Wrappers between web compartments must never be cut in web-observable
|
||||
// ways.
|
||||
void
|
||||
NukeAllWrappersForCompartment(JSContext* cx, JSCompartment* compartment,
|
||||
js::NukeReferencesToWindow nukeReferencesToWindow)
|
||||
{
|
||||
// First, nuke all wrappers into or out of the target compartment. Once
|
||||
// the compartment is marked as nuked, WrapperFactory will refuse to
|
||||
// create new live wrappers for it, in either direction. This means that
|
||||
// we need to be sure that we don't have any existing cross-compartment
|
||||
// wrappers which may be replaced with dead wrappers during unrelated
|
||||
// wrapper recomputation *before* we set that bit.
|
||||
js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(),
|
||||
js::SingleCompartment(compartment),
|
||||
nukeReferencesToWindow,
|
||||
js::NukeAllReferences);
|
||||
|
||||
// At this point, we should cross-compartment wrappers for the nuked
|
||||
// compartment. Set the wasNuked bit so WrapperFactory will return a
|
||||
// DeadObjectProxy when asked to create a new wrapper for it, and mark as
|
||||
// unscriptable.
|
||||
auto compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
|
||||
compartmentPrivate->wasNuked = true;
|
||||
compartmentPrivate->scriptability.Block();
|
||||
}
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
static void
|
||||
|
|
|
@ -400,6 +400,9 @@ bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
|
|||
|
||||
nsIPrincipal* GetCompartmentPrincipal(JSCompartment* compartment);
|
||||
|
||||
void NukeAllWrappersForCompartment(JSContext* cx, JSCompartment* compartment,
|
||||
js::NukeReferencesToWindow nukeReferencesToWindow = js::NukeWindowReferences);
|
||||
|
||||
void SetLocationForGlobal(JSObject* global, const nsACString& location);
|
||||
void SetLocationForGlobal(JSObject* global, nsIURI* locationURI);
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ support-files =
|
|||
[test_getWebIDLCaller.html]
|
||||
skip-if = (debug == false || os == "android")
|
||||
[test_nac.xhtml]
|
||||
[test_nukeContentWindow.html]
|
||||
[test_sameOriginPolicy.html]
|
||||
[test_sandbox_fetch.html]
|
||||
support-files =
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1322273
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1322273</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322273">Mozilla Bug 1322273</a>
|
||||
|
||||
<iframe id="subframe"></iframe>
|
||||
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
add_task(function* () {
|
||||
let frame = $('subframe');
|
||||
frame.src = "data:text/html,";
|
||||
yield new Promise(resolve => frame.addEventListener("load", resolve, {once: true}));
|
||||
|
||||
let win = frame.contentWindow;
|
||||
|
||||
win.eval("obj = {}");
|
||||
win.obj.foo = {bar: "baz"};
|
||||
|
||||
let obj = win.obj;
|
||||
|
||||
let system = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal()
|
||||
let sandbox = SpecialPowers.Cu.Sandbox(system);
|
||||
|
||||
sandbox.obj = obj;
|
||||
|
||||
let isWrapperDead = SpecialPowers.Cu.evalInSandbox(`(${
|
||||
function isWrapperDead() {
|
||||
return Components.utils.isDeadWrapper(obj);
|
||||
}
|
||||
})`,
|
||||
sandbox);
|
||||
|
||||
is(isWrapperDead(), false, "Sandbox wrapper for content window should not be dead");
|
||||
is(obj.foo.bar, "baz", "Content wrappers into and out of content window should be alive");
|
||||
|
||||
// Remove the frame, which should nuke the content window.
|
||||
info("Remove the content frame");
|
||||
frame.remove();
|
||||
|
||||
// Give the nuke wrappers task a chance to run.
|
||||
yield new Promise(SimpleTest.executeSoon);
|
||||
|
||||
is(isWrapperDead(), true, "Sandbox wrapper for content window should be dead");
|
||||
is(obj.foo.bar, "baz", "Content wrappers into and out of content window should be alive");
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче