RESOLVED FIXED173754
Web Inspector: Crash generating object preview for ArrayIterator
https://bugs.webkit.org/show_bug.cgi?id=173754
Summary Web Inspector: Crash generating object preview for ArrayIterator
Joseph Pecoraro
Reported 2017-06-22 22:05:28 PDT
Summary: Crash generating object preview for ArrayIterator Test: <script> (function() { try { var iter = [7][Symbol.iterator](); debugger; iter['return'] = function() {}; Array.from(iter, function(){ throw 2; }); } catch(e) { /* empty */ } })(); </script> Steps to reproduce: 1. Inspect test page 2. Reload (to trigger debugger) 3. Step through code => Crash after the `iter["return"]` line. Notes: - JSInjectedScriptHost::iteratorEntries clones the ArrayIterator and reuses the structure but doesn't fill in all properties that are important for the iterable operations (namely the `return` function it claims to implement).
Attachments
[PATCH] Proposed Fix (8.87 KB, patch)
2017-06-22 22:12 PDT, Joseph Pecoraro
joepeck: review-
[PATCH] Proposed Fix (50.42 KB, patch)
2017-06-23 19:24 PDT, Joseph Pecoraro
no flags
[PATCH] Proposed Fix (50.39 KB, patch)
2017-06-23 19:25 PDT, Joseph Pecoraro
saam: review+
Joseph Pecoraro
Comment 1 2017-06-22 22:05:39 PDT
Joseph Pecoraro
Comment 2 2017-06-22 22:12:45 PDT
Created attachment 313688 [details] [PATCH] Proposed Fix
Saam Barati
Comment 3 2017-06-22 23:37:59 PDT
Comment on attachment 313688 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=313688&action=review > Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp:530 > + clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName())); When is this called? What if the iterator object gets mutated by user code? What I’m worried about is a user adds random properties to this object, and then we still end up with properties that are zero filled that we didn’t expect to exist. If this is even possible, I think the best way to proceed would be to just start off the result with the empty structure, and add the properties allowing the object’s structure to transition.
Joseph Pecoraro
Comment 4 2017-06-23 11:24:30 PDT
Comment on attachment 313688 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=313688&action=review >> Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp:530 >> + clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName())); > > When is this called? What if the iterator object gets mutated by user code? What I’m worried about is a user adds random properties to this object, and then we still end up with properties that are zero filled that we didn’t expect to exist. If this is even possible, I think the best way to proceed would be to just start off the result with the empty structure, and add the properties allowing the object’s structure to transition. This is called only in this file. This object itself should not be exposed to user code. It is created here, iterated up to 5 times, and closed. The idea here is that Web Inspector wants to peek and show the next 5 elements in an iterator. Since "Array Iterator" and other native iterators are guaranteed to have side-effect free iteration we can peek upcoming values by cloning the iterator instance, walk the clone forward a few values, and then getting rid of the clone. Array Iterator, unlike Map Iterator, Set Iterator, etc, is different in that it is implemented in JavaScript built-ins. But we can identify one with PrivateNames. This is our implementation of cloning an Array Iterator. If you see a way that user code can access this object then we might have to abandon this approach or seek an alternative. Maybe that is possible if someone mutates the ArrayIterator prototype? Namely `[].values().__proto__.next`? Starting off with an empty structure won't get us the necessary `next` function.
Joseph Pecoraro
Comment 5 2017-06-23 12:05:15 PDT
Comment on attachment 313688 [details] [PATCH] Proposed Fix After talking with Saam going to go a safer route: - Only peek iterators when not observable (no user code) - Do a better job cloning ArrayIterators
Joseph Pecoraro
Comment 6 2017-06-23 19:24:22 PDT
Created attachment 313767 [details] [PATCH] Proposed Fix
Joseph Pecoraro
Comment 7 2017-06-23 19:25:47 PDT
Created attachment 313768 [details] [PATCH] Proposed Fix
Joseph Pecoraro
Comment 8 2017-06-24 00:23:41 PDT
Comment on attachment 313768 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=313768&action=review > Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp:557 > + // FIXME: Reflect.enumerate iteration can be observable, but it is also removed from ECMAScript. I am removing this in: <https://webkit.org/b/173806> Remove Reflect.enumerate
Saam Barati
Comment 9 2017-06-24 17:22:12 PDT
Comment on attachment 313768 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=313768&action=review r=me > Source/JavaScriptCore/ChangeLog:9 > + When Inspector generates an object preview for an ArrayIterator instance it made I like how this patch turned out > Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp:527 > + JSObject* clone = constructEmptyObject(exec, ArrayIteratorPrototype::create(vm, globalObject, ArrayIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype()))); Does the global object not cache this structure?
Joseph Pecoraro
Comment 10 2017-06-27 10:24:34 PDT
> > Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp:527 > > + JSObject* clone = constructEmptyObject(exec, ArrayIteratorPrototype::create(vm, globalObject, ArrayIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype()))); > > Does the global object not cache this structure? It doesn't. Want me to add it?
Joseph Pecoraro
Comment 11 2017-06-27 10:44:29 PDT
Saam Barati
Comment 12 2017-06-27 16:28:02 PDT
(In reply to Joseph Pecoraro from comment #10) > > > Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp:527 > > > + JSObject* clone = constructEmptyObject(exec, ArrayIteratorPrototype::create(vm, globalObject, ArrayIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype()))); > > > > Does the global object not cache this structure? > > It doesn't. Want me to add it? I'm not sure what you went with. It's probably better in the long run, but not essential for this code.
Note You need to log in before you can comment on or make changes to this bug.