Bug 187211 - Builtins and host functions should get their own structures.
Summary: Builtins and host functions should get their own structures.
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Mark Lam
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2018-06-29 17:43 PDT by Mark Lam
Modified: 2018-07-02 10:52 PDT (History)
8 users (show)

See Also:


Attachments
proposed patch. (10.13 KB, patch)
2018-06-30 10:28 PDT, Mark Lam
saam: review+
Details | Formatted Diff | Diff
proposed patch. (13.37 KB, patch)
2018-07-01 23:37 PDT, Mark Lam
saam: review+
ews-watchlist: commit-queue-
Details | Formatted Diff | Diff
Archive of layout-test-results from ews206 for win-future (12.82 MB, application/zip)
2018-07-02 01:58 PDT, EWS Watchlist
no flags Details
Archive of layout-test-results from ews200 for win-future (12.87 MB, application/zip)
2018-07-02 03:35 PDT, EWS Watchlist
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Mark Lam 2018-06-29 17:43:21 PDT
<rdar://problem/41646336>
Comment 1 Saam Barati 2018-06-29 17:44:20 PDT
Why?
Comment 2 Mark Lam 2018-06-29 17:52:10 PDT
(In reply to Saam Barati from comment #1)
> Why?

Because normal JSFunctions can start out with the same structure as a builtin.  When in strict mode, the structure for a builtin function will be the same as a normal empty function.

If the program invokes hasOwnProperty("prototype") on the builtin first and we cache the result, we will end up caching a false value.  However, when the program subsequently invokes hasOwnProperty("prototype") on a normal empty function (e.g. foo = function() {}), it will see the same structure and return the false result in the cache.  However, the expected result in this case should be true.
Comment 3 Saam Barati 2018-06-29 17:52:59 PDT
(In reply to Mark Lam from comment #2)
> (In reply to Saam Barati from comment #1)
> > Why?
> 
> Because normal JSFunctions can start out with the same structure as a
> builtin.  When in strict mode, the structure for a builtin function will be
> the same as a normal empty function.
> 
> If the program invokes hasOwnProperty("prototype") on the builtin first and
> we cache the result, we will end up caching a false value.  However, when
> the program subsequently invokes hasOwnProperty("prototype") on a normal
> empty function (e.g. foo = function() {}), it will see the same structure
> and return the false result in the cache.  However, the expected result in
> this case should be true.

they shouldn't have the same structure then.
Comment 4 Saam Barati 2018-06-29 17:53:17 PDT
What you're describing is a bug that is not just in this cache, but all our ICs
Comment 5 Mark Lam 2018-06-29 17:56:21 PDT
(In reply to Saam Barati from comment #4)
> What you're describing is a bug that is not just in this cache, but all our
> ICs

I'll investigate further.
Comment 6 Mark Lam 2018-06-30 10:28:59 PDT
Created attachment 344016 [details]
proposed patch.
Comment 7 Saam Barati 2018-06-30 17:36:14 PDT
Comment on attachment 344016 [details]
proposed patch.

View in context: https://bugs.webkit.org/attachment.cgi?id=344016&action=review

Nice. r=me

> JSTests/stress/regress-187211.js:76
> +}

Can we also add a test for getById ICs? I’m guessing they will run into the same problem of producing the wrong result

> Source/JavaScriptCore/runtime/JSFunction.cpp:73
> +    if (executable->isBuiltinFunction()) {

Might be worth an assert above that arrow functions aren’t Builtins? Or can they be if they’re nested in a builtin? Or maybe it’s moot because they don’t have a “prototype” property?
Comment 8 Mark Lam 2018-07-01 23:30:37 PDT
(In reply to Saam Barati from comment #7)
> Comment on attachment 344016 [details]
> proposed patch.
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=344016&action=review
> 
> Nice. r=me
> 
> > JSTests/stress/regress-187211.js:76
> > +}
> 
> Can we also add a test for getById ICs? I’m guessing they will run into the
> same problem of producing the wrong result

I think getById is not vulnerable, and I have not been able to write a test to get it to fail.  Looking at the code, getById caches an offset only if PropertySlot.isCacheable() is true.  In the case of the builtin function's prototype property, isCacheable() is false.

However, the HasOwnPropertyCache caches a result value if !(!slot.isCacheable() && !slot.isUnset()) i.e. it caches if slot.isCacheable() || slot.isUnset().  The second condition is what allow the HasOwnProperty result to be cached for the builtin function, and this is the source of the discrepancy that led to this bug.


> > Source/JavaScriptCore/runtime/JSFunction.cpp:73
> > +    if (executable->isBuiltinFunction()) {
> 
> Might be worth an assert above that arrow functions aren’t Builtins? Or can
> they be if they’re nested in a builtin? Or maybe it’s moot because they
> don’t have a “prototype” property?

My testing shows that there are builtin arrow functions.  I think you are right that arrow functions not having the "prototype" property makes the immediate issue moot.  However, I think it is fragile to leave it in this state: if anyone adds another lazily reified property, there's a chance that it can become an issue.

Hence, I've re-written the code to give builtin arrow functions a different structure as well.  I'll upload a patch shortly.
Comment 9 Mark Lam 2018-07-01 23:37:21 PDT
Created attachment 344079 [details]
proposed patch.
Comment 10 EWS Watchlist 2018-07-02 01:58:10 PDT
Comment on attachment 344079 [details]
proposed patch.

Attachment 344079 [details] did not pass win-ews (win):
Output: https://webkit-queues.webkit.org/results/8409883

New failing tests:
http/tests/security/canvas-remote-read-remote-video-localhost.html
Comment 11 EWS Watchlist 2018-07-02 01:58:21 PDT
Created attachment 344085 [details]
Archive of layout-test-results from ews206 for win-future

The attached test failures were seen while running run-webkit-tests on the win-ews.
Bot: ews206  Port: win-future  Platform: CYGWIN_NT-6.1-2.9.0-0.318-5-3-x86_64-64bit
Comment 12 EWS Watchlist 2018-07-02 03:35:13 PDT
Comment on attachment 344079 [details]
proposed patch.

Attachment 344079 [details] did not pass win-ews (win):
Output: https://webkit-queues.webkit.org/results/8410411

New failing tests:
http/tests/security/local-video-source-from-remote.html
Comment 13 EWS Watchlist 2018-07-02 03:35:24 PDT
Created attachment 344091 [details]
Archive of layout-test-results from ews200 for win-future

The attached test failures were seen while running run-webkit-tests on the win-ews.
Bot: ews200  Port: win-future  Platform: CYGWIN_NT-6.1-2.9.0-0.318-5-3-x86_64-64bit
Comment 14 Saam Barati 2018-07-02 06:23:22 PDT
Comment on attachment 344016 [details]
proposed patch.

View in context: https://bugs.webkit.org/attachment.cgi?id=344016&action=review

>>> JSTests/stress/regress-187211.js:76
>>> +}
>> 
>> Can we also add a test for getById ICs? I’m guessing they will run into the same problem of producing the wrong result
> 
> I think getById is not vulnerable, and I have not been able to write a test to get it to fail.  Looking at the code, getById caches an offset only if PropertySlot.isCacheable() is true.  In the case of the builtin function's prototype property, isCacheable() is false.
> 
> However, the HasOwnPropertyCache caches a result value if !(!slot.isCacheable() && !slot.isUnset()) i.e. it caches if slot.isCacheable() || slot.isUnset().  The second condition is what allow the HasOwnProperty result to be cached for the builtin function, and this is the source of the discrepancy that led to this bug.

Do Builtins have a prototype property or not? It seems like the GetById cache would break when given an unset slot

This is what GetById does, which I believe is the same as the own property cache

if (!slot.isCacheable() && !slot.isUnset())
                return GiveUpOnCache;
Comment 15 Mark Lam 2018-07-02 10:48:52 PDT
(In reply to Saam Barati from comment #14)
> Comment on attachment 344016 [details]
> proposed patch.
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=344016&action=review
> 
> >>> JSTests/stress/regress-187211.js:76
> >>> +}
> >> 
> >> Can we also add a test for getById ICs? I’m guessing they will run into the same problem of producing the wrong result
> > 
> > I think getById is not vulnerable, and I have not been able to write a test to get it to fail.  Looking at the code, getById caches an offset only if PropertySlot.isCacheable() is true.  In the case of the builtin function's prototype property, isCacheable() is false.
> > 
> > However, the HasOwnPropertyCache caches a result value if !(!slot.isCacheable() && !slot.isUnset()) i.e. it caches if slot.isCacheable() || slot.isUnset().  The second condition is what allow the HasOwnProperty result to be cached for the builtin function, and this is the source of the discrepancy that led to this bug.
> 
> Do Builtins have a prototype property or not? It seems like the GetById
> cache would break when given an unset slot
> 
> This is what GetById does, which I believe is the same as the own property
> cache
> 
> if (!slot.isCacheable() && !slot.isUnset())
>                 return GiveUpOnCache;

Thanks for the review.  I'll dig further to see what I was missing when I attempted to make the GetById test and failed.  Since we know that this patch is fundamentally correct, I'll move forward and land it first.  If I can come up with a test for GetById, I'll land that in a follow up.  Thanks.
Comment 16 Mark Lam 2018-07-02 10:52:01 PDT
Fix landed in r233426: <http://trac.webkit.org/r233426>.