Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ufuzz failure #3736

Closed
alexlamsl opened this issue Feb 29, 2020 · 21 comments
Closed

ufuzz failure #3736

alexlamsl opened this issue Feb 29, 2020 · 21 comments

Comments

@alexlamsl
Copy link
Collaborator

// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    {
        var brake1 = 5;
        do {
            if (--b + typeof (b |= a)) {
                var brake3 = 5;
                do {
                    L26822: for (var brake4 = 5; [ typeof bar != "unknown", a++ + (a /= (c = 1 + c, 
                    (a = (true ^ -1) << (undefined << "")) % (3 & -4 || [ , 0 ].length === 2 | "a"))), (c = c + 1) + /[abc4]/g.exec((a || b || 5).toString()), --b + {
                        3: --b + (0 === 1 ? a : b),
                        Infinity: --b + a++,
                        var: b--,
                        "": --b + (0 === 1 ? a : b),
                        1.5: --b + a
                    }.c ] && brake4 > 0; --brake4) {
                        switch ((c = c + 1) + ++a) {
                          case b--:
                            break;

                          case (c = c + 1) + (typeof f2 == "function" && --_calls_ >= 0 && f2(false)):
                            if (--b + ((c = c + 1, -1 !== "foo") >> ((-0 ^ [ , 0 ].length === 2) != (null ^ -2)))) {
                                var expr7 = --b + [];
                                L26823: for (var key7 in expr7) {
                                    c = 1 + c;
                                    var c_2 = expr7[key7];
                                    L26824: for (var brake8 = 5; (c = 1 + c, c_2 && (c_2.foo *= (24..toString() == undefined) <= "" - -2 >= ((c_2 += -0 === -4) !== undefined << {}))) && brake8 > 0; --brake8) {
                                        c = 1 + c, (0 - true) / (c_2 = ("b", 22)) >> ((true === "") > 23..toString() / 1);
                                    }
                                }
                            } else {
                                var b = a++ + delete b, a_2 = -((24..toString() << 2) / (4 !== "b") - ((null & 3) !== (24..toString() ^ -1)));
                            }
                            var b_2 = (c = c + 1) + (typeof (c = 1 + c, (-4 >>> "foo" ^ "number" * "b") >>> (a_2 |= 38..toString() + 0 ^ (1 || [ , 0 ].length === 2))) || a || 3).toString();
                            break;

                          default:
                            for (var brake12 = 5; a++ + {
                                in: (c = 1 + c, c = c + 1, (23..toString() != -1) % ("function" * "b"))
                            } && brake12 > 0; --brake12) {
                                var expr13 = a++ + this;
                                L26825: for (var key13 in expr13) {
                                    c = 1 + c;
                                    var b_2 = expr13[key13];
                                    {
                                        var expr14 = (c = 1 + c, -((25 >> [ , 0 ][1]) - ("bar" & -4)));
                                        for (var key14 in expr14) {
                                            c = 1 + c, 24..toString() > 22 & this > 38..toString(), c_2 && (c_2.c -= (-3 >= null, 
                                            true, Infinity));
                                        }
                                    }
                                }
                            }
                            {
                                var brake16 = 5;
                                do {
                                    var b_2 = b_2;
                                } while (a++ + (a_2 && a_2[b_2 && b_2[c = 1 + c, b_2 = (c = c + 1, c_2 && (c_2.a += "b" && "function")) << ((b_2 && (b_2[c = 1 + c, 
                                5 >> 1 == ([] ^ true) & (-4 <= -3) / ("a" / false)] += [] !== "undefined")) >= ([ , 0 ].length === 2) - NaN)]]) && --brake16 > 0);
                            }

                          case "foo" / 5 * (5 > this) << (3 >>> null <= (23..toString() < "a")):
                            break;
                        }
                    }
                } while (--b + (a++ + {
                    1.5: (c = c + 1) + ~((c = c + 1, !-1) - NaN % -3 * ("number" / "number")),
                    Infinity: --b + ~a,
                    "-2": --b + (typeof f0 == "function" && --_calls_ >= 0 && f0()),
                    null: (c = c + 1) + (a++ + (b_2 += typeof b_1 === "boolean") || 4).toString()[((0 === 1 ? a : b) || a || 3).toString()]
                }) && --brake3 > 0);
            } else {
                var b = b_2;
            }
        } while ((c = c + 1) + ((b_2 && (b_2[++a] = "foo" >= "b" == "foo" / 23..toString())) < "a" / Infinity << (-0 != false) || a || 3).toString() && --brake1 > 0);
    }
    return {};
}

var c_2 = f0("c", "a");

console.log(null, a, b, c, Infinity, NaN, undefined);
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    var brake1 = 5;
    do {
        if (--b + typeof (b |= a)) {
            var brake3 = 5;
            do {
                for (var brake4 = 5; a++, a /= (c = 1 + c, (a = -2) % (2 === [ , 0 ].length | "a")), 
                c += 1, /[abc4]/g.exec((a || b || 5).toString()), --b, --b, --b, a++, b--, --b, 
                --b, 0 < brake4; --brake4) {
                    switch ((c += 1) + ++a) {
                      case b--:
                        break;

                      case (c += 1) + ("function" == typeof f2 && 0 <= --_calls_ && f2(!1)):
                        if (--b + (c += 1, !0 >> (-2 != (-0 ^ 2 === [ , 0 ].length)))) {
                            var expr7 = --b + [];
                            for (var key7 in expr7) {
                                c = 1 + c;
                                for (var c_2 = expr7[key7], brake8 = 5; c = 1 + c, c_2 && (c_2.foo *= (null == 24..toString()) <= 2 >= ((c_2 += !1) !== void 0 << {})) && 0 < brake8; --brake8) {
                                    c = 1 + c, c_2 = 22, 23..toString();
                                }
                            }
                        } else {
                            var b = a++ + delete b, a_2 = -((24..toString() << 2) / !0 - (0 != (-1 ^ 24..toString())));
                        }
                        var b_2 = (c += 1) + (c = 1 + c, (typeof (-4 >>> (a_2 |= 38..toString() + 0 ^ 1)) || a || 3).toString());
                        break;

                      default:
                        for (var brake12 = 5; a++ + {
                            in: (c = 1 + c, c += 1, (-1 != 23..toString()) % NaN)
                        } && 0 < brake12; --brake12) {
                            var expr13 = a++ + this;
                            for (var key13 in expr13) {
                                b_2 = expr13[key13];
                                var expr14 = (c = 1 + (c = 1 + c), -25);
                                for (var key14 in expr14) {
                                    c = 1 + c, 24..toString(), 38..toString(), c_2 && (c_2.c -= 1 / 0);
                                }
                            }
                        }
                        var brake16 = 5;
                        do {
                            b_2 = b_2;
                        } while (a++ + (a_2 && a_2[b_2 && b_2[c = 1 + c, c += 1, b_2 = (c_2 && (c_2.a += "function")) << ((b_2 && (b_2[c = 1 + c, 
                        2 == (!0 ^ []) & NaN] += "undefined" !== [])) >= (2 === [ , 0 ].length) - NaN)]]) && 0 < --brake16);

                      case NaN * (this < 5) << (3 <= (23..toString() < "a")):
                    }
                }
            } while (--b + (a++ + {
                1.5: (c += 1) + (c += 1, -1),
                Infinity: --b + ~a,
                "-2": --b + ("function" == typeof f0 && 0 <= --_calls_ && f0()),
                null: (c += 1) + (a++ + (b_2 += "boolean" == typeof b_1) || 4).toString()[(b || a || 3).toString()]
            }) && 0 < --brake3);
        } else {
            b = b_2;
        }
    } while ((c += 1) + ((b_2 && (b_2[++a] = 1 == "foo" / 23..toString())) < 0 || a || 3).toString() && 0 < --brake1);
    return {};
}

var c_2 = f0("c", "a");

console.log(null, a, b, c, 1 / 0, NaN, void 0);
original result:
null Infinity 10 347930 Infinity NaN undefined

uglified result:
null Infinity 10 346580 Infinity NaN undefined
// reduced test case (output will differ)

var _calls_ = 10, a = 0, c = 0;

function f0() {
    var brake1 = 5;
    {
        var brake3 = 5;
        do {
            for (var brake4 = 5; [ a++ + (a /= (1 ^ -1) % ([ , 0 ].length === 2)) ] && brake4; --brake4) {
                switch (0) {
                  case 0:
                    var a_2 = 0;

                  default:
                    var expr13 = a + this;
                    for (var key13 in expr13) {
                        (0)[0];
                        var expr14 = (c = 1 + c, 0 - 1);
                        c, (0).toString();
                    }
                    var b_2 = 0;
                }
            }
        } while ({
            "-2": --_calls_ >= 0 && f0()
        } && --brake3);
    }
}

f0();

console.log(c);
// output: 6487
// 
// minify: 6490
// 
// options: {
//   "mangle": false
// }
minify(options):
{
  "mangle": false
}

Suspicious compress options:
  evaluate
@alexlamsl
Copy link
Collaborator Author

@kzc most likely a V8 bug − can't reproduce on Node.js 12, the reduced test case always gives me 6490 on my local copy of Node.js 10.19.0, and locally --reduce-test gave me:

var _calls_ = 10, a = 0, c = 0;

function f0() {
    var brake1 = 5;
    do {
        var brake3 = 5;
        do {
            for (var brake4 = 5; [ a++ + (a /= (1 ^ -1) % ([ , 0 ].length === 2)) ] && brake4; --brake4) {
                var expr13 = a + this;
                for (var key13 in expr13) {
                    c = 1 + c;
                }
            }
        } while (a + (--_calls_ >= 0 && f0()) && --brake3);
    } while (--brake1);
}

f0();

console.log(c);

@kzc
Copy link
Contributor

kzc commented Feb 29, 2020

The original uglified code produces the correct result on all node versions. As you say, has to be a V8 bug.

@kzc
Copy link
Contributor

kzc commented Feb 29, 2020

        for (var brake4 = 5; [ a++ + (a /= (1 ^ -1) % ([ , 0 ].length === 2)) ] && brake4; --brake4) {

Suspicious compress options:
evaluate

Where did this test case come from? Are you sure it's not the fake evaluate xor bug?

@alexlamsl
Copy link
Collaborator Author

It's from GitHub Actions of my fork, which is an identical copy of this repository.

@alexlamsl
Copy link
Collaborator Author

So wait, it doesn't give any reduced test case for you when you do uglifyjs original.js -c --reduce-test on Node.js 10(.19.0)?

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

No test cases produced on latest master with any NodeJS version I tried:

$ node-v10.4.1 bin/uglifyjs original.js -c --reduce-test
// Node.js v10.4.1 on darwin x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }
$ node-v12.13.0 bin/uglifyjs original.js -c --reduce-test
// Node.js v12.13.0 on darwin x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }

Which makes sense because the initial uglified code above also produces the same result as the original.

Do you see a different result?

@alexlamsl
Copy link
Collaborator Author

$ uglifyjs original.js -c --reduce-test
// Node.js v0.10.48 on win32 x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }
$ uglifyjs original.js -c --reduce-test
// Node.js v0.12.18 on win32 x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }
$ uglifyjs original.js -c --reduce-test
// Node.js v4.9.1 on win32 x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }
$ uglifyjs original.js -c --reduce-test
// Node.js v6.17.1 on win32 x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }
$ uglifyjs original.js -c --reduce-test
// Node.js v8.17.0 on win32 x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }
$ uglifyjs original.js -c --reduce-test
// Node.js v10.19.0 on win32 x64
// reduce test pass 1, iteration 0: 4741 bytes
// reduce test pass 1, iteration 25: 2121 bytes
// reduce test pass 1, iteration 50: 2077 bytes
// reduce test pass 1, iteration 75: 2055 bytes
// reduce test pass 1, iteration 100: 2047 bytes
// reduce test pass 1, iteration 125: 2013 bytes
// reduce test pass 1, iteration 150: 1886 bytes
// reduce test pass 1, iteration 175: 1806 bytes
// reduce test pass 1, iteration 200: 1155 bytes
// reduce test pass 1, iteration 225: 1144 bytes
// reduce test pass 1, iteration 250: 1121 bytes
// reduce test pass 1, iteration 275: 1118 bytes
// reduce test pass 1, iteration 300: 1080 bytes
// reduce test pass 1, iteration 325: 1078 bytes
// reduce test pass 1, iteration 350: 725 bytes
// reduce test pass 1, iteration 375: 616 bytes
// reduce test pass 1, iteration 400: 521 bytes
// reduce test pass 1, iteration 425: 392 bytes
// reduce test pass 1: 364 bytes
// reduce test pass 2: 292 bytes
// reduce test pass 3: 277 bytes
var _calls_ = 10, a = 0, c = 0;

function f0() {
    var brake3 = 5;
    do {
        for (var brake4 = 5; [ a++ + (a /= (1 ^ -1) % ([ , 0 ].length === 2)) ] && brake4; --brake4) {
            switch (0) {
              default:
                var expr13 = a + this;
                for (var key13 in expr13) {
                    var expr14 = (c = 1 + c, [ 0 ] - 0);
                }
                0;
            }
        }
    } while ({
        "-2": --_calls_ >= 0 && f0()
    } && --brake3);
}

f0();

console.log(c);
// output: 6472
//
// minify: 6490
//
// options: {
//   "compress": true,
//   "mangle": false
// }
$ uglifyjs original.js -c --reduce-test
// Node.js v12.16.1 on win32 x64
// Can't reproduce test failure with minify options provided:
// {
//   "compress": true,
//   "mangle": false
// }

@alexlamsl
Copy link
Collaborator Author

Wow that's messed up 👻

$ node -v
v10.19.0

$ cat reduced.js | node
6490

$ cat reduced.js | node
6478

$ cat reduced.js | node
6484

$ cat reduced.js | node
6490

$ cat reduced.js | node
6484

$ cat reduced.js | node
6478

$ cat reduced.js | node
6490

$ cat reduced.js | node
6490

$ cat reduced.js | node
6490

$ cat reduced.js | node
6484

$ cat reduced.js | node
6478

$ cat reduced.js | node
6484

$ cat reduced.js | node
6490

$ cat reduced.js | node
6490

$ cat reduced.js | node
6490

$ cat reduced.js | node
6487

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

The globals must have changed between node-v10.4.1 and Node.js v10.19.0 - and later versions.

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

Wow that's messed up

Interesting - same version of Node 10 produces different results. Some random transient global(s) or perhaps a non-deterministic for-in order for globals?

@alexlamsl
Copy link
Collaborator Author

But we aren't probing the content of the global object though - that a + this evaluates to either "Infinity[object global]" or "-Infinity[object global]" throughout all iterations.

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

Side note - if a given version of NodeJS produces a different result each time, then all bets are off for the reduced test case. It breaks a basic assumption of the reduce test algo.

@alexlamsl
Copy link
Collaborator Author

Fully explains why it gave difference reduced test cases between Github Actions and locally.

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

a + this

Good point. Odd.

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

Do you see anything odd on Node 10 if you apply the following patch to the reduced test case?

                var expr13 = a + this;
+                console.error("expr13:", expr13);

@alexlamsl
Copy link
Collaborator Author

That's how I work out what a + this gives − just a big bunch of them, sometimes with the negative sign, sometimes don't.

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

In case of an extraneous space might need JSON.stringify(expr13).

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

So the sign is not deterministic?

@alexlamsl
Copy link
Collaborator Author

That seems to be the issue, yes.

@kzc
Copy link
Contributor

kzc commented Mar 1, 2020

v10.19.0

Just curious why you prefer Node v10.19.0 for ufuzz over v12? Is it faster?

@alexlamsl
Copy link
Collaborator Author

ufuzz keeps tripping over the latest version of Node.js, e.g. nodejs/node#30586, so ended up with v10 for stability.

I assumed letting ufuzz job fetch the latest version of v10 is safe enough, but I guess this is being challenged as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants