Commit 9cd456c
Fix .prototype pollution on JSC by passing JSClass to JSObjectMakeConstructor (#177)
[Created by Copilot on behalf of @bghgary]
## Context
Closes #172.
JSC's `JSObjectMakeConstructor` defaults the constructor's `.prototype`
property to the global `Object.prototype` when invoked with a null
`JSClassRef`, then installs it with the `ReadOnly` attribute. That gives
us two distinct bugs:
1. **Pollution.** Every napi-defined class shares `Object.prototype`.
Writing to `Foo.prototype.x` mutates `Object.prototype.x`; `{}
instanceof Foo` returns `true` for every class.
2. **Silent failure on retry.** The `ReadOnly` bit means
`JSObjectSetProperty(constructor, "prototype", ...)` cannot overwrite
the default afterward — the write is silently dropped (or throws in
strict mode).
## Fix
Pass the existing per-constructor `JSClassRef` — the one we already
create for the sentinel — into `JSObjectMakeConstructor`. WebKit then
assigns a fresh per-class `JSCallbackObject` (built from the class's
auto-generated `prototypeClass`) as `.prototype`, sidestepping both bugs
at the source.
The vestigial "extra prototype" hack in `ConstructorInfo::Create` — kept
since #101 with a TODO to remove — goes away. Two ancillary call sites
needed a small update because they were reading the `[[Prototype]]`
internal slot when they meant the `.prototype` property:
- `CallAsConstructor` chains `instance.__proto__` from
`constructor.prototype` (the property), not `constructor.__proto__` (the
slot = `Function.prototype`).
- `napi_define_class` installs instance properties on
`constructor.prototype` via `napi_get_named_property`.
`napi_get_prototype` keeps its N-API-spec contract of returning the
`[[Prototype]]` slot and is unchanged.
## Tests
Added a `napi class prototype isolation` describe block using `Blob` (a
napi-defined class available on every engine). Six invariants — all
standard JS semantics that V8/Chakra already satisfy and that JSC fails
today:
- `Blob.prototype !== Object.prototype`
- `Object.getPrototypeOf(Blob.prototype) === Object.prototype`
- `Blob.prototype.constructor === Blob`
- `Object.getPrototypeOf(new Blob([])) === Blob.prototype` (and
`instanceof` true)
- `({} instanceof Blob) === false`
- Writes to `Blob.prototype` do not appear on `{}`
## Lifecycle note
`JSCallbackConstructor` does not invoke the JSClass `finalize` callback;
only `JSCallbackObject` does. The auto-prototype WebKit builds from
`_class->prototype()` uses a separate `prototypeClass` that explicitly
clears `finalize`. So sharing `_class` between the constructor, the
sentinel, and the auto-prototype carries no double-free risk — only the
sentinel ever calls `Finalize` on our `info` pointer.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent 81b01d2 commit 9cd456c
2 files changed
Lines changed: 76 additions & 15 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
322 | 322 | | |
323 | 323 | | |
324 | 324 | | |
325 | | - | |
326 | | - | |
327 | | - | |
328 | | - | |
329 | | - | |
330 | | - | |
331 | | - | |
332 | | - | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
337 | | - | |
338 | 325 | | |
339 | 326 | | |
340 | 327 | | |
341 | 328 | | |
342 | 329 | | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
343 | 341 | | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
344 | 352 | | |
345 | 353 | | |
346 | 354 | | |
| |||
380 | 388 | | |
381 | 389 | | |
382 | 390 | | |
383 | | - | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
384 | 396 | | |
385 | 397 | | |
386 | 398 | | |
| |||
933 | 945 | | |
934 | 946 | | |
935 | 947 | | |
936 | | - | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
937 | 952 | | |
938 | 953 | | |
939 | 954 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1268 | 1268 | | |
1269 | 1269 | | |
1270 | 1270 | | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
| 1307 | + | |
| 1308 | + | |
| 1309 | + | |
| 1310 | + | |
| 1311 | + | |
| 1312 | + | |
| 1313 | + | |
| 1314 | + | |
| 1315 | + | |
| 1316 | + | |
1271 | 1317 | | |
1272 | 1318 | | |
1273 | 1319 | | |
| |||
0 commit comments