Closed
Bug 363891
Opened 18 years ago
Closed 18 years ago
Indirect use of eval can run code with the wrong principal.
Categories
(Core :: JavaScript Engine, defect)
Core
JavaScript Engine
Tracking
()
RESOLVED
FIXED
People
(Reporter: jst, Assigned: mrbkap)
References
Details
(Keywords: arch, testcase, verified1.8.1.13, Whiteboard: [sg:critical])
Attachments
(2 files, 3 obsolete files)
5.73 KB,
patch
|
brendan
:
review+
|
Details | Diff | Splinter Review |
4.57 KB,
patch
|
Details | Diff | Splinter Review |
Attachment #248695 -
Flags: review?(brendan)
Comment 2•18 years ago
|
||
Man, I want to ban indirect eval (except for w.eval(s) for a window object w, or whatever the global class is in a non-DOM embedding).
/be
Updated•18 years ago
|
Assignee: general → jst
Whiteboard: [sg:critical]
Comment 5•18 years ago
|
||
is the patch any good, or should we be going with brendan's idea in comment 2 ?
Keywords: arch
Reporter | ||
Comment 6•18 years ago
|
||
The patch is no good (or not good enough), so we'll need to go with brendan's idea.
IOW, we'll want to limit eval to being callable on global objects, and ideally also to being only callable directly (i.e. window.foo = window.eval; window.foo() should fail, at least from chrome). Brendan says the JS 2 spec already limits eval() similarly to this.
I'll be out of the country for a week starting tomorrow and won't be of much help on this issue right now. mrbkap or brendan, can you guys have a stab at this problem?
Assignee | ||
Comment 7•18 years ago
|
||
I could take a stab at this on Friday.
Assignee | ||
Comment 8•18 years ago
|
||
So, does this do the trick?
Assignee | ||
Comment 9•18 years ago
|
||
(In reply to comment #6)
> IOW, we'll want to limit eval to being callable on global objects, and ideally
> also to being only callable directly (i.e. window.foo = window.eval;
> window.foo() should fail, at least from chrome). Brendan says the JS 2 spec
> already limits eval() similarly to this.
So, what about cases like bug 320172, where eval is being used (mildly incorrectly) as an argument to Array.forEach. Granted, this is an edge case, but it is out there in the wild (or, at least, it was). If we do want to enforce this, then I think the easiest way would be to disallow using "eval" in a non-call context.
Comment 10•18 years ago
|
||
Comment on attachment 261181 [details] [diff] [review]
More like this
>+ uintN flags;
Block local and initialized.
> fp = cx->fp;
> caller = JS_GetScriptedCaller(cx, fp);
> JS_ASSERT(!caller || caller->pc);
> indirectCall = (caller && *caller->pc != JSOP_EVAL);
We talked about fixing the emitter to choose JSOP_EVAL for o.eval as well as eval (pn_arity == PN_NAME instead of pn_op == JSOP_NAME before the pn_atom == evalAtom test). We must allow o.eval if o is a window object, which the OBJ_GET_PARENT(cx, obj) != NULL below allows -- but that also allows w.evil = eval, which we don't want to support.
So use JSOP_EVAL as the first filter, to throw out any calls to a function or method named anything but eval.
The second filter is to make any obj_eval activation whose |obj| (|this|) parameter is not a global object be an indirectCall.
mrbkap pointed out the good use-case of Array.map(arraylike, eval). To make that work, we must stop using JS_GetScriptedCaller, and add special-case code to recognize good native trampoline frames such as the one for array_extra.
/be
Attachment #261181 -
Flags: review?(brendan) → review-
Assignee | ||
Comment 11•18 years ago
|
||
So, this fixes brendan's comments, but see the big XXX. We can't allow seemingly benign Array.map(..., eval) because it undoes the advantage of the rest of the fix. brendan says he'll take the issue up with ECMA.
Attachment #248695 -
Attachment is obsolete: true
Attachment #261181 -
Attachment is obsolete: true
Attachment #248695 -
Flags: review?(brendan)
Comment 12•18 years ago
|
||
This bug reminds about a similar problem with Java and its ClassLoader (Java version of eval). There was bug at least until JDK 1.4 that effectively allowed to gain all permissions as long as code was allowed to use ClassLoader. The reason was that the runtime did not impose dynamic restrictions according to the current stack on the code generated using ClassLoader. So when the generated code was called with only system code on the stack, it run with all permissions.
Updated•18 years ago
|
Flags: wanted1.8.1.x?
Flags: wanted1.8.0.x?
Reporter | ||
Comment 13•18 years ago
|
||
Since we're making eval() not callable off of a non-global object, shouldn't we then also make it so that obj.eval is only defined on global objects as well? I mean what's the point of exposing that function (which IE never exposed) if it's never callable? If we'd remove eval from non-global objects then an unqualified eval() call scoped at a non-global object would continue to work, whereas if we don't do that we'd find eval() in the current scope and we'd fail to do the eval, no?
Assignee | ||
Comment 14•18 years ago
|
||
Ah, true. I wonder if we should resolve it in JS_ResolveStandardClass and take it off of Object.prototype.
Comment 15•18 years ago
|
||
Take it out of Object.prototype, make it a global function only.
/be
Assignee | ||
Comment 16•18 years ago
|
||
With this patch applied:
js> ["print('a')", "print('b')"].forEach(eval)
a
b
js> this.eval
function eval() {
[native code]
}
js> ({}).eval
js> eval.call({}, "")
typein:3: EvalError: function eval must be called directly, and not by way of a function of another name
Attachment #261207 -
Attachment is obsolete: true
Attachment #266004 -
Flags: review?(brendan)
Comment 17•18 years ago
|
||
Comment on attachment 266004 [details] [diff] [review]
Best fix yet
>Index: jsapi.c
>===================================================================
>RCS file: /cvsroot/mozilla/js/src/jsapi.c,v
>retrieving revision 3.322
>diff -p -U8 -r3.322 jsapi.c
>--- jsapi.c 18 May 2007 20:55:06 -0000 3.322
>+++ jsapi.c 24 May 2007 22:50:35 -0000
>@@ -1395,16 +1395,17 @@ static JSStdName object_prototype_names[
> {js_InitObjectClass, EAGER_ATOM(parent), NULL},
> {js_InitObjectClass, EAGER_ATOM(count), NULL},
> #if JS_HAS_TOSOURCE
> {js_InitObjectClass, EAGER_ATOM(toSource), NULL},
> #endif
> {js_InitObjectClass, EAGER_ATOM(toString), NULL},
> {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL},
> {js_InitObjectClass, EAGER_ATOM(valueOf), NULL},
>+ {js_InitObjectClass, EAGER_ATOM(eval), NULL},
Why add this to object_prototype_names if this patch removes Object.prototype.eval? The entry in standard_class_names should suffice.
>+ /*
>+ * Note: This restriction allows for Array.map(array, eval), but does not
>+ * allow the user to pass a this parameter that isn't a global object.
>+ */
>+ if (indirectCall || OBJ_GET_PARENT(c, obj)) {
>+ uintN flags = (OBJ_GET_PARENT(cx, obj) != NULL)
Nit: house style eschews != NULL tests except when nesting assignment in a loop condition. Outer parens in the ?: condition can be avoided too, thereby.
Looks great otherwise. We are close to the promised land... r=me with these changes.
/be
Attachment #266004 -
Flags: review?(brendan) → review+
Assignee | ||
Comment 18•18 years ago
|
||
(In reply to comment #17)
> Why add this to object_prototype_names if this patch removes
> Object.prototype.eval? The entry in standard_class_names should suffice.
Oops, I didn't see that when I wrote the patch.
Assignee | ||
Comment 19•18 years ago
|
||
So, should we try this for a6? Or should this wait until after 1.9 has branched (based on bug 380236 comment 14)?
Assignee | ||
Comment 20•18 years ago
|
||
Assignee | ||
Updated•18 years ago
|
Attachment #266637 -
Attachment is patch: true
Attachment #266637 -
Attachment mime type: application/octet-stream → text/plain
Assignee | ||
Comment 21•18 years ago
|
||
James, Neil, do you know if any extensions are depending on indirect eval on something that isn't the window object (Brendan and I seem to remember Venkman using the 2-parameter form, but don't remember if it also uses the indirect form)?
Comment 22•18 years ago
|
||
(In reply to comment #21)
>James, Neil, do you know if any extensions are depending on indirect eval on
>something that isn't the window object (Brendan and I seem to remember Venkman
>using the 2-parameter form, but don't remember if it also uses the indirect form)?
Are you perhaps thinking of evalInTargetScope in venkman-static.js?
Assignee | ||
Comment 23•18 years ago
|
||
Argh, this got wacky. Jeff, this bug ended up being about removing indirect eval, we can use bug 382509 as a cover bug. Perhaps the discussion about Venkman should move there.
Updated•18 years ago
|
Flags: blocking1.9+
Assignee | ||
Comment 24•18 years ago
|
||
This was checked into trunk.
Status: ASSIGNED → RESOLVED
Closed: 18 years ago
Resolution: --- → FIXED
Comment 25•18 years ago
|
||
Does this have too much potential for extension-breaking, or could we safely take this on the 1.8 branch?
Flags: wanted1.8.1.x?
Flags: wanted1.8.1.x+
Flags: wanted1.8.0.x?
Flags: wanted1.8.0.x+
Flags: blocking1.8.1.5?
Flags: blocking1.8.0.13?
Assignee | ||
Comment 26•18 years ago
|
||
I think I've seen spurious errors caused by this checkin (including one in chrome code!), which makes me wary to check this in on branches.
Comment 27•18 years ago
|
||
alright, let's wait at least another cycle to see what the trunk fall-out is and then reconsider taking it on the branch.
Flags: blocking1.8.1.5?
Flags: blocking1.8.0.13?
Comment 28•17 years ago
|
||
Still don't see regressions from this on trunk, can we take it now? 1.8.1.9?
Flags: blocking1.8.1.9?
Updated•17 years ago
|
Flags: blocking1.8.1.12? → blocking1.8.1.12+
Comment 29•17 years ago
|
||
The branch has waited a long time for this, is there any objection to me just checking it in?
Comment 30•17 years ago
|
||
After 2.0.0.11 I'm too scared to check this in without explicit approval from the patch author, will have to wait yet again.
Flags: blocking1.8.1.12+ → blocking1.8.1.13+
Comment 34•17 years ago
|
||
All right. Marking as verified for this release then.
Keywords: fixed1.8.1.13 → verified1.8.1.13
Updated•17 years ago
|
Group: security
Updated•12 years ago
|
Flags: in-testsuite-
You need to log in
before you can comment on or make changes to this bug.
Comment 1
•