Flow: Logical AND and OR are totally unsound in functions (enables arbitrary coercion)

Created on 31 Jul 2018  Â·  3Comments  Â·  Source: facebook/flow

The type of a && b should be typeof a | typeof b, but appears to
actually be merely typeof a. This enables trivially breaking the type
system—any value can be coerced to the type of any truthy value:

// @flow
function f<A, B>(a: A, b: B): A {
  return a && b;
}
f(17, "broken").toFixed();  // runtime error, not caught statically

Some observations:

  • substituting (a && b: A) for a && b is valid;
  • if one removes the declared return type on f, then the proper
    error appears, so the inferred type is not clearly unsound;
  • inlining the function ((17 && "broken": number)) causes the proper
    error to appear.

This can be trivially extended to coerce a value to any inhabited
type
:

// @flow
function f<A, B>(a: A, b: B): A {
  return a && b;
}
function g<A, B>(a: A, b: B): A {
  return a || b;
}
function coerce<A, B>(a: A, b: B): A {
  return a ? f(a, b) : g(a, b);
}
coerce(17, "broken").toFixed();  // runtime error, not caught statically
soundness bug

Most helpful comment

@wchargin Thanks for the great bug report and sorry it took a long time for us to get to it!

All 3 comments

With a bit of thought, we can see that this yields coerce without a
single any-cast or any other unsafe features
:

// @flow

// `coerce` always returns its argument unchanged; it never throws.
function coerce<T, U>(t: T): U {
  function f<A, B>(a: A, b: B): A {
    return a && b;
  }
  class V {}
  const result: U | V = f(new V(), t);
  if (result instanceof V) {
    throw new Error("impossible; we will never get here");
  }
  return result;
}

// or, using initial/terminal types instead of generics...
function coerceToEmpty(t: mixed): empty {
  return coerce(t);
}

const wat1: number = coerce("hello");
const wat2: boolean = coerceToEmpty(123);
const wat3: empty = coerce(null);

This works on all Flow versions currently available on flow.org/try
(namely, v0.37.0 to v0.78.0).

Woohoo! Thanks, @mvitousek .

@wchargin Thanks for the great bug report and sorry it took a long time for us to get to it!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ctrlplusb picture ctrlplusb  Â·  3Comments

l2silver picture l2silver  Â·  3Comments

john-gold picture john-gold  Â·  3Comments

mjj2000 picture mjj2000  Â·  3Comments

marcelbeumer picture marcelbeumer  Â·  3Comments