Bug 568698 - Instead of fixing two globals in the jetpack process, allow jetpack to create sandboxes in which to load user code and implementation modules, r=bent

This commit is contained in:
Benjamin Smedberg 2010-07-08 09:40:07 -07:00
Родитель 0b81e9a8e4
Коммит b3de5c62a7
9 изменённых файлов: 172 добавлений и 143 удалений

Просмотреть файл

@ -71,7 +71,7 @@ public:
> MapType;
OpaqueSeenType() {
NS_ASSERTION(map.init(1), "Failed to initialize map");
(void) map.init(1);
}
bool ok() { return map.initialized(); }
@ -452,24 +452,18 @@ JetpackActorCommon::RecvMessage(JSContext* cx,
JSObject* implGlobal = JS_GetGlobalObject(cx);
js::AutoValueRooter rval(cx);
const uint32 savedOptions =
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
for (PRUint32 i = 0; i < snapshot.Length(); ++i) {
Variant* vp = results ? results->AppendElement() : NULL;
rval.set(JSVAL_VOID);
if (!JS_CallFunctionValue(cx, implGlobal, snapshot[i], argc, argv,
rval.addr())) {
// If a receiver throws, we drop the exception on the floor.
JS_ClearPendingException(cx);
(void) JS_ReportPendingException(cx);
if (vp)
*vp = void_t();
} else if (vp && !jsval_to_Variant(cx, rval.value(), vp))
*vp = void_t();
}
JS_SetOptions(cx, savedOptions);
return true;
}

Просмотреть файл

@ -44,6 +44,8 @@
#include "jsarray.h"
#include <stdio.h>
namespace mozilla {
namespace jetpack {
@ -55,18 +57,6 @@ JetpackChild::~JetpackChild()
{
}
#define IMPL_PROP_FLAGS (JSPROP_SHARED | \
JSPROP_ENUMERATE | \
JSPROP_READONLY | \
JSPROP_PERMANENT)
const JSPropertySpec
JetpackChild::sImplProperties[] = {
{ "jetpack", 0, IMPL_PROP_FLAGS, UserJetpackGetter, NULL },
{ 0, 0, 0, NULL, NULL }
};
#undef IMPL_PROP_FLAGS
#define IMPL_METHOD_FLAGS (JSFUN_FAST_NATIVE | \
JSPROP_ENUMERATE | \
JSPROP_READONLY | \
@ -80,6 +70,8 @@ JetpackChild::sImplMethods[] = {
JS_FN("unregisterReceivers", UnregisterReceivers, 1, IMPL_METHOD_FLAGS),
JS_FN("wrap", Wrap, 1, IMPL_METHOD_FLAGS),
JS_FN("createHandle", CreateHandle, 0, IMPL_METHOD_FLAGS),
JS_FN("createSandbox", CreateSandbox, 0, IMPL_METHOD_FLAGS),
JS_FN("evalInSandbox", EvalInSandbox, 2, IMPL_METHOD_FLAGS),
JS_FS_END
};
@ -92,7 +84,21 @@ JetpackChild::sGlobalClass = {
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
static void
ReportJetpackErrors(JSContext* cx, const char* message, JSErrorReport* report)
{
const char* filename = "<unknown>";
if (report && report->filename)
filename = report->filename;
int lineno = -1;
if (report)
lineno = report->lineno;
fprintf(stderr, "Jetpack JavaScript Error: %s:%i, %s\n",
filename, lineno, message);
}
bool
JetpackChild::Init(base::ProcessHandle aParentProcessHandle,
MessageLoop* aIOLoop,
@ -102,31 +108,20 @@ JetpackChild::Init(base::ProcessHandle aParentProcessHandle,
return false;
if (!(mRuntime = JS_NewRuntime(32L * 1024L * 1024L)) ||
!(mImplCx = JS_NewContext(mRuntime, 8192)) ||
!(mUserCx = JS_NewContext(mRuntime, 8192)))
!(mCx = JS_NewContext(mRuntime, 8192)))
return false;
{
JSAutoRequest request(mImplCx);
JS_SetContextPrivate(mImplCx, this);
JSObject* implGlobal =
JS_NewGlobalObject(mImplCx, const_cast<JSClass*>(&sGlobalClass));
if (!implGlobal ||
!JS_InitStandardClasses(mImplCx, implGlobal) ||
!JS_DefineProperties(mImplCx, implGlobal,
const_cast<JSPropertySpec*>(sImplProperties)) ||
!JS_DefineFunctions(mImplCx, implGlobal,
const_cast<JSFunctionSpec*>(sImplMethods)))
return false;
}
JS_SetErrorReporter(mCx, ReportJetpackErrors);
{
JSAutoRequest request(mUserCx);
JS_SetContextPrivate(mUserCx, this);
JSObject* userGlobal =
JS_NewGlobalObject(mUserCx, const_cast<JSClass*>(&sGlobalClass));
if (!userGlobal ||
!JS_InitStandardClasses(mUserCx, userGlobal))
JSAutoRequest request(mCx);
JS_SetContextPrivate(mCx, this);
JSObject* implGlobal =
JS_NewGlobalObject(mCx, const_cast<JSClass*>(&sGlobalClass));
if (!implGlobal ||
!JS_InitStandardClasses(mCx, implGlobal) ||
!JS_DefineFunctions(mCx, implGlobal,
const_cast<JSFunctionSpec*>(sImplMethods)))
return false;
}
@ -137,8 +132,7 @@ void
JetpackChild::CleanUp()
{
ClearReceivers();
JS_DestroyContext(mUserCx);
JS_DestroyContext(mImplCx);
JS_DestroyContext(mCx);
JS_DestroyRuntime(mRuntime);
JS_ShutDown();
}
@ -153,32 +147,21 @@ bool
JetpackChild::RecvSendMessage(const nsString& messageName,
const nsTArray<Variant>& data)
{
JSAutoRequest request(mImplCx);
return JetpackActorCommon::RecvMessage(mImplCx, messageName, data, NULL);
JSAutoRequest request(mCx);
return JetpackActorCommon::RecvMessage(mCx, messageName, data, NULL);
}
static bool
Evaluate(JSContext* cx, const nsCString& code)
bool
JetpackChild::RecvEvalScript(const nsString& code)
{
JSAutoRequest request(cx);
js::AutoValueRooter ignored(cx);
JS_EvaluateScript(cx, JS_GetGlobalObject(cx), code.get(),
code.Length(), "", 1, ignored.addr());
JSAutoRequest request(mCx);
js::AutoValueRooter ignored(mCx);
(void) JS_EvaluateUCScript(mCx, JS_GetGlobalObject(mCx), code.get(),
code.Length(), "", 1, ignored.addr());
return true;
}
bool
JetpackChild::RecvLoadImplementation(const nsCString& code)
{
return Evaluate(mImplCx, code);
}
bool
JetpackChild::RecvLoadUserScript(const nsCString& code)
{
return Evaluate(mUserCx, code);
}
PHandleChild*
JetpackChild::AllocPHandle()
{
@ -197,20 +180,10 @@ JetpackChild::GetThis(JSContext* cx)
{
JetpackChild* self =
static_cast<JetpackChild*>(JS_GetContextPrivate(cx));
JS_ASSERT(cx == self->mImplCx ||
cx == self->mUserCx);
JS_ASSERT(cx == self->mCx);
return self;
}
JSBool
JetpackChild::UserJetpackGetter(JSContext* cx, JSObject* obj, jsval id,
jsval* vp)
{
JSObject* userGlobal = JS_GetGlobalObject(GetThis(cx)->mUserCx);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(userGlobal));
return JS_TRUE;
}
struct MessageResult {
nsString msgName;
nsTArray<Variant> data;
@ -428,5 +401,49 @@ JetpackChild::CreateHandle(JSContext* cx, uintN argc, jsval* vp)
return JS_TRUE;
}
JSBool
JetpackChild::CreateSandbox(JSContext* cx, uintN argc, jsval* vp)
{
if (argc > 0) {
JS_ReportError(cx, "createSandbox takes zero arguments");
return JS_FALSE;
}
JSObject* obj = JS_NewGlobalObject(cx, const_cast<JSClass*>(&sGlobalClass));
if (!obj)
return JS_FALSE;
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
return JS_InitStandardClasses(cx, obj);
}
JSBool
JetpackChild::EvalInSandbox(JSContext* cx, uintN argc, jsval* vp)
{
if (argc != 2) {
JS_ReportError(cx, "evalInSandbox takes two arguments");
return JS_FALSE;
}
jsval* argv = JS_ARGV(cx, vp);
JSObject* obj;
if (!JSVAL_IS_OBJECT(argv[0]) ||
!(obj = JSVAL_TO_OBJECT(argv[0])) ||
&sGlobalClass != JS_GetClass(cx, obj) ||
obj == JS_GetGlobalObject(cx)) {
JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox.");
return JS_FALSE;
}
JSString* str = JS_ValueToString(cx, argv[1]);
if (!str)
return JS_FALSE;
js::AutoValueRooter ignored(cx);
return JS_EvaluateUCScript(cx, obj, JS_GetStringChars(str), JS_GetStringLength(str), "", 1,
ignored.addr());
}
} // namespace jetpack
} // namespace mozilla

Просмотреть файл

@ -69,22 +69,17 @@ protected:
NS_OVERRIDE virtual bool RecvSendMessage(const nsString& messageName,
const nsTArray<Variant>& data);
NS_OVERRIDE virtual bool RecvLoadImplementation(const nsCString& code);
NS_OVERRIDE virtual bool RecvLoadUserScript(const nsCString& code);
NS_OVERRIDE virtual bool RecvEvalScript(const nsString& script);
NS_OVERRIDE virtual PHandleChild* AllocPHandle();
NS_OVERRIDE virtual bool DeallocPHandle(PHandleChild* actor);
private:
JSRuntime* mRuntime;
JSContext *mImplCx, *mUserCx;
JSContext *mCx;
static JetpackChild* GetThis(JSContext* cx);
static const JSPropertySpec sImplProperties[];
static JSBool UserJetpackGetter(JSContext* cx, JSObject* obj, jsval idval,
jsval* vp);
static const JSFunctionSpec sImplMethods[];
static JSBool SendMessage(JSContext* cx, uintN argc, jsval *vp);
static JSBool CallMessage(JSContext* cx, uintN argc, jsval *vp);
@ -93,6 +88,8 @@ private:
static JSBool UnregisterReceivers(JSContext* cx, uintN argc, jsval *vp);
static JSBool Wrap(JSContext* cx, uintN argc, jsval *vp);
static JSBool CreateHandle(JSContext* cx, uintN argc, jsval *vp);
static JSBool CreateSandbox(JSContext* cx, uintN argc, jsval *vp);
static JSBool EvalInSandbox(JSContext* cx, uintN argc, jsval *vp);
static const JSClass sGlobalClass;

Просмотреть файл

@ -63,38 +63,6 @@ JetpackParent::~JetpackParent()
NS_IMPL_ISUPPORTS1(JetpackParent, nsIJetpack)
static nsresult
ReadFromURI(const nsAString& aURI,
nsCString* content)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri),
NS_ConvertUTF16toUTF8(aURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel;
NS_NewChannel(getter_AddRefs(channel), uri);
NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
nsCOMPtr<nsIInputStream> input;
rv = channel->Open(getter_AddRefs(input));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(input, "Channel opened successfully but stream was null?");
char buffer[256];
PRUint32 avail = 0;
input->Available(&avail);
if (avail) {
PRUint32 read = 0;
while (NS_SUCCEEDED(input->Read(buffer, sizeof(buffer), &read)) && read) {
content->Append(buffer, read);
read = 0;
}
}
return NS_OK;
}
NS_IMETHODIMP
JetpackParent::SendMessage(const nsAString& aMessageName)
{
@ -159,31 +127,12 @@ JetpackParent::UnregisterReceivers(const nsAString& aMessageName)
}
NS_IMETHODIMP
JetpackParent::LoadImplementation(const nsAString& aURI)
JetpackParent::EvalScript(const nsAString& aScript)
{
nsCString code;
nsresult rv = ReadFromURI(aURI, &code);
NS_ENSURE_SUCCESS(rv, rv);
if (!SendEvalScript(nsString(aScript)))
return NS_ERROR_FAILURE;
if (!code.IsEmpty() &&
!SendLoadImplementation(code))
rv = NS_ERROR_FAILURE;
return rv;
}
NS_IMETHODIMP
JetpackParent::LoadUserScript(const nsAString& aURI)
{
nsCString code;
nsresult rv = ReadFromURI(aURI, &code);
NS_ENSURE_SUCCESS(rv, rv);
if (!code.IsEmpty() &&
!SendLoadUserScript(code))
rv = NS_ERROR_FAILURE;
return rv;
return NS_OK;
}
bool

Просмотреть файл

@ -78,8 +78,7 @@ both:
async PHandle();
child:
async LoadImplementation(nsCString code);
async LoadUserScript(nsCString code);
async EvalScript(nsString code);
parent:
sync CallMessage(nsString messageName,

Просмотреть файл

@ -53,8 +53,7 @@ interface nsIJetpack : nsISupports
in jsval aReceiver);
void unregisterReceivers(in AString aMessageName);
void loadImplementation(in AString aURI);
void loadUserScript(in AString aURI);
void evalScript(in AString aScript);
nsIVariant createHandle();

Просмотреть файл

@ -41,3 +41,43 @@ registerReceiver("drop methods", echo);
registerReceiver("exception coping", echo);
registerReceiver("duplicate receivers", echo);
function ok(c, msg)
{
sendMessage("test result", c, msg);
}
registerReceiver("test sandbox", function() {
var addon = createSandbox();
ok(typeof(addon) == "object", "typeof(addon)");
ok("Date" in addon, "addon.Date exists");
ok(addon.Date !== Date, "Date objects are different");
var fn = "var x; var c = 3; function doit() { x = 12; return 4; }";
evalInSandbox(addon, fn);
ok(addon.x === undefined, "x is undefined");
ok(addon.c == 3, "c is 3");
ok(addon.doit() == 4, "doit called successfully");
ok(addon.x == 12, "x is now 12");
var fn2 = "let function barbar{}";
try {
evalInSandbox(addon, fn2);
ok(false, "bad syntax should throw");
}
catch(e) {
ok(true, "bad syntax should throw");
}
var fn3 = "throw new Error('just kidding')";
try {
evalInSandbox(addon, fn3);
ok(false, "thrown error should be caught");
}
catch(e) {
ok(true, "thrown error should be caught");
}
sendMessage("sandbox done");
});

Просмотреть файл

@ -1,3 +1,6 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
var jps = Components.classes["@mozilla.org/jetpack/service;1"]
.getService(Components.interfaces.nsIJetpackService);
var jetpack = null;
@ -7,11 +10,32 @@ function createHandle() {
return jetpack.createHandle();
}
const PR_RDONLY = 0x1;
function read_file(f)
{
var fis = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
fis.init(f, PR_RDONLY, 0444, Ci.nsIFileInputStream.CLOSE_ON_EOF);
var lis = Cc["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Ci.nsIConverterInputStream);
lis.init(fis, "UTF-8", 1024, 0);
var data = "";
var r = {};
while (lis.readString(0x0FFFFFFF, r))
data += r.value;
return data;
}
function run_test() {
jetpack = jps.createJetpack();
run_handle_tests();
jetpack.loadImplementation("file://" + do_get_file("impl.js").path);
jetpack.evalScript(read_file(do_get_file("impl.js")));
var circ1 = {},
circ2 = {},
@ -162,6 +186,13 @@ function run_test() {
jetpack.registerReceiver("duplicate receivers",
function() { do_test_finished() });
jetpack.registerReceiver("test result", function(name, c, msg) {
dump("TEST-INFO | test_jetpack.js | remote check '" + msg + "' result: " + c + "\n");
do_check_true(c);
});
jetpack.registerReceiver("sandbox done", do_test_finished);
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
@ -183,8 +214,11 @@ function run_test() {
undefined, null, true, false, 1, 2, 999, 1/4, "oyez");
jetpack.sendMessage("drop methods", drop);
jetpack.sendMessage("exception coping");
jetpack.sendMessage("duplicate receivers");
jetpack.sendMessage("test sandbox");
do_register_cleanup(function() {
jetpack.destroy();
});

Просмотреть файл

@ -175,7 +175,7 @@ class XPCShellTests(object):
'-e', 'print("To start the test, type |_execute_test();|.");',
'-i']
else:
self.xpcsRunArgs = ['-e', '_execute_test();']
self.xpcsRunArgs = ['-e', '_execute_test(); quit(0);']
def getPipes(self):
"""