Mark S. Miller (2015-04-29T18:29:30.000Z)
I do indeed need to expose DefensivePromise under the global name
"Promise", replacing the builtin. Other than itself being frozen and making
frozen promises, it should in all other ways conform exactly to the promise
spec while still guaranteeing this invariant.




> ```
>   constructor(x) {
>     super(x);
>     Object.freeze(this);
>     if (this.constructor==DefensivePromise && this.then ===
> DefensivePromise.prototype.then && /*) {
>       goodPromises.add(this);
>     }
>   }
> ```
> You probably need to make the test a little bit more careful, in case
> `then` is a malicious getter or something like that.
>

Attack:


class BadPromise {}

BadPromise.prototype.constructor = DefensivePromise;

const bp = Reflect.construct(DefensivePromise, [], BadPromise);

// Passed your checks, and so bp is frozen and added to goodPromises.
// But IIUC bp.[[Prototype]] is BadPromise.prototype.

BadPromise.prototype.then = r => {r(), r()};  // borrowed from your attack.


bp is now an attack object which can be used in a plan interference attack,
by inappropriately synchronously calling r.
I also like the point implicit in your attack that I also need the
invariant that

DefensivePromise.resolve(anything).then(anycallback)

should all callback at most once.




> But my point is that you can do all this in userland, you don't need to
> expose its complexity or bake any of it into the Promise spec.
>  --scott
>


I suspect you are correct. But let's reserve judgement until you, I, or
someone succeeds at writing userland code immune to attack.


-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150429/9e039449/attachment-0001.html>
d at domenic.me (2015-05-11T16:45:07.474Z)
I do indeed need to expose DefensivePromise under the global name
"Promise", replacing the builtin. Other than itself being frozen and making
frozen promises, it should in all other ways conform exactly to the promise
spec while still guaranteeing this invariant.




> ```
>   constructor(x) {
>     super(x);
>     Object.freeze(this);
>     if (this.constructor==DefensivePromise && this.then ===
> DefensivePromise.prototype.then && /*) {
>       goodPromises.add(this);
>     }
>   }
> ```
> You probably need to make the test a little bit more careful, in case
> `then` is a malicious getter or something like that.
>

Attack:

```js
class BadPromise {}

BadPromise.prototype.constructor = DefensivePromise;

const bp = Reflect.construct(DefensivePromise, [], BadPromise);

// Passed your checks, and so bp is frozen and added to goodPromises.
// But IIUC bp.[[Prototype]] is BadPromise.prototype.

BadPromise.prototype.then = r => {r(), r()};  // borrowed from your attack.
```

bp is now an attack object which can be used in a plan interference attack,
by inappropriately synchronously calling r.
I also like the point implicit in your attack that I also need the
invariant that

```js
DefensivePromise.resolve(anything).then(anycallback)
```

should all callback at most once.




> But my point is that you can do all this in userland, you don't need to
> expose its complexity or bake any of it into the Promise spec.

I suspect you are correct. But let's reserve judgement until you, I, or
someone succeeds at writing userland code immune to attack.