New issue
Advanced search Search tips

Issue 752328 link

Starred by 2 users

Issue metadata

Status: Fixed
Owner:
Closed: Aug 2017
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Linux , Windows , Mac
Pri: 2
Type: Bug-Regression



Sign in to add a comment

Hashing of Window objects is broken in WeakMap/Map

Reported by hdd...@gmail.com, Aug 3 2017

Issue description

UserAgent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0

Steps to reproduce the problem:
1. Allow popups
2. Open https://jsfiddle.net/769a0qmu
3. Open the Developer Tools Console
4. Run the demo (may take a few attempts)

The code in the demo:

var map = new WeakMap();
var popup = window.open("https://google.com");
map.set(popup, "data");
window.setTimeout(function() {
  console.log(map.get(popup));
  map.set(popup, "modified");
  console.log(map.get(popup));
}, 3000);

What is the expected behavior?
A new tab will open with Google Search, and the console will display:

    data
    modified

What went wrong?
A new tab opens with Google Search as expected, but the console displays:

    undefined
    modified

In Chrome 62.0.3175.2 (canary) the output is different, but also wrong:

    data
    data

In Firefox the output is correct.

Did this work before? N/A 

Chrome version: 60.0.3112.78  Channel: stable
OS Version: 10
Flash Version: 26.0.0.137

This bug was originally discovered by a user on Stack Overflow (https://stackoverflow.com/questions/45395639/key-equality-of-es6-map-behaves-oddly-in-chrome-content-script/). The original bug discovered was with Map#has and Map#get.

Demo: https://jsfiddle.net/hvqab05n/

Code:

    var map = new Map();
    var popup = window.open("https://google.com");
    map.set(popup, "data");
    window.setTimeout(function() {
        console.log(map.has(popup));
        console.log(map.get(popup));
    }, 3000);

Expected output:

    true
    data

Output on Chrome 60.0.3112.78:

    false
    undefined

In Chrome 62.0.3175.2 (canary) the output is correct.

The method Map#has was unwittingly fixed by the following commit in V8:

https://chromium.googlesource.com/v8/v8/+/19f7cc497abf309fe52017d7363798aa0a74f0f8 [Collections] Port Map.p.has to CSA/C++

The change was added to Chromium in

https://chromium.googlesource.com/chromium/src/+/249d58471e61e05bec81b1b1a757052d7a5d702a Update V8 to version 6.1.58.

This points to the JavaScript function GetHash() as the culprit. The function generates a random hash and saves it as a property. From MDN (https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Description), the Window object returned by open() initially contains about:blank, and the real URL is only loaded after the current script finishes executing. When the real URL is loaded, the same-origin policy restricts access to the Window object. My guess is that the saved hash becomes inaccessible and so a different hash is produced. The C++ version does not have this problem, but there are still somes uses of GetHash() in Chrome Canary (e.g. WeakMap#set).
 

Comment 1 Deleted

Components: Internals>Sandbox>SiteIsolation Blink>JavaScript>Runtime
Labels: -Type-Bug -Pri-2 Needs-Triage-M60 hasbisect-per-revision M-62 OS-Linux OS-Mac Pri-1 Type-Bug-Regression
Owner: dcheng@chromium.org
Status: Assigned (was: Unconfirmed)
Able to reproduce the issue on Windows 10, Ubuntu 14.04 and Mac 10.12.6 using chrome reported version #60.0.3112.78 and latest canary #62.0.3176.0.

Bisect Information:
=====================
Good build: 59.0.3049.0	 Revision(425838)
Bad Build : 59.0.3050.0	 Revision(426105)

Change Log URL: 
https://chromium.googlesource.com/chromium/src/+log/f70d58032706670f094ed7ae73c2bb13259fc0b6..a94ee9eb38e2003712667dfe217a5cb1a1763dd0

From the above change log suspecting below change
Review URL: https://codereview.chromium.org/2760793002

dcheng@ - Could you please check whether this is caused with respect to your change, if not please help us in assigning it to the right owner.

Note:
In Chrome 62.0.3175.2 (canary) the output is different, but also wrong: (as per reporter)

    data
    data

Thanks...!!
Cc: haraken@chromium.org jochen@chromium.org yukishiino@chromium.org
Components: -Blink
Cc: gsat...@chromium.org
Sathya, could you take a look at this issue?

I've verified that this is fixed on ToT. 

FWIW, Comment#2 seems to contradict the reporter though (who reported it to be working on Chrome 62.0.3175.2). 
The repro case (written in very first place of the original report) is still reproducible.  The repro case is as follows:

var map = new WeakMap();
var popup = window.open("https://google.com");
map.set(popup, "data");
window.setTimeout(function() {
  console.log(map.get(popup));
  map.set(popup, "modified");
  console.log(map.get(popup));
}, 3000);

The other case (map.has / map.get) seems working as expected, though.  The other case is as follows:

var map = new Map();
var popup = window.open("https://google.com");
map.set(popup, "data");
window.setTimeout(function() {
	console.log(map.has(popup));
  console.log(map.get(popup));
}, 3000);

Sathya, could you take another look?

FYI, I'm seeing the attached screenshot.

I've synched to git commit = e192006f532ff3a9a765098cbe0cbc2de008d514
chrome://chrome says "Version 62.0.3190.0 (Developer Build) (64-bit)"
Platform is GNU/Linux (Goobuntu)

-------- args.gn --------
is_debug = false
is_component_build = true
enable_nacl = false
use_goma = true
goma_dir = "(snip)"
-------------------------

weakmap.png
43.0 KB View Download
Ah, I was testing content_shell. content_shell seems to exhibit the correct behavior but not chrome. 

Do you know if there is any difference between chrome and content_shell in handling of the window object?

Comment 9 by adamk@chromium.org, Aug 18 2017

Cc: adamk@chromium.org

Comment 10 by adamk@chromium.org, Aug 18 2017

The issue in V8 seems to be that globals created for a remote context return an empty string for %_ClassOf(). This causes us to go down the wrong path when looking for hash codes for such objects.

In content_shell, %_ClassOf(popup) returns "global", as expected (I'm not yet clear on why chrome and content_shell differ here, hopefully dcheng can answer that)

Comment 11 by adamk@chromium.org, Aug 18 2017

From a chat with dcheng, if I pass "--site-per-process" to content_shell I get the chrome behavior. So it looks like this is always broken when NewRemoteContext is used.

Comment 12 by adamk@chromium.org, Aug 18 2017

Blockedon: v8:6404
Labels: -Pri-1 Pri-2
Here's what's going on inside V8. First, note from the original stack overflow report that this used to be broken in Map, but isn't anymore. Map and WeakMap used to use exactly the same logic to get the hash code for an object. That code looks like this (it's written in internal v8 JS):

```
function GetExistingHash(key) {
  if (IS_RECEIVER(key) && !IS_PROXY(key) && !IS_GLOBAL(key)) {
    var hash = GET_PRIVATE(key, hashCodeSymbol);
    return hash;
  }
  return %GenericHash(key);
}
```

Note the "IS_GLOBAL(key)". That's a macro which expands to "%_ClassOf(key) == 'global'". Since the RemoteContext-created global object returns empty string, it fails this check and so we incorrectly try to use the normal object mechanism for dealing with hashcodes, rather than the Global-specific logic (which is handled in the C++ runtime function %GenericHash().

Over the last couple of months, v8 has moved its implementation of the Map methods out of JS, which means they're no longer sharing the above logic with WeakMap. In particular, the decision about how to find the hash code always goes directly to %GenericHash, which uses a different mechanism (an instance type check) on the key to see how to handle the hash code. This method gets the right answer for the RemoteContext case, and thus uses the special "hash" field on JSGlobalProxy (https://cs.chromium.org/chromium/src/v8/src/objects.h?rcl=07259a9ceafa078c9bb7f9ee1bb6f2d67256cc80&l=5351), as intended.

All that said, there's the question of what to do to fix the WeakMap case. There's ongoing work inside v8 that will eventually make this go away (analogous to how Map got fixed), but that's probably on the order of weeks until it's fixed. If we'd like a fix sooner, we could try to hack things to make %_ClassOf return "global", but it's not yet clear to me how much work that'd be.

Given that this has been broken for two stable releases already, I'm inclined to just wait for it to be fixed on the v8 side. I've tentatively lowered priority and marked this as blocked on the v8 bug, but I'm open to doing something sooner if it seems worthwhile.
Status: Started (was: Assigned)
Discussed offline with adamk@. I think this is just an oversight of the remote context initialization path.

Comment 14 by adamk@chromium.org, Aug 24 2017

Blockedon: -v8:6404 v8:6413
This is one step closer to being fixed. Now all that should be required is to eliminate the IS_GLOBAL check in the code in the referenced V8 issue.

Comment 15 by adamk@chromium.org, Aug 24 2017

Status: Fixed (was: Started)
Actually this was fixed in 01c82f9cab010b390f9ad15de9edf1fee44b972e; local testing indicates that we now properly deal with the hash.

Still might be nice for someone to write a web platform test for it, but the actual fix is done.

Comment 16 by adamk@chromium.org, Aug 24 2017

Blockedon: -v8:6413

Sign in to add a comment