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 #5217

Closed
alexlamsl opened this issue Dec 12, 2021 · 8 comments
Closed

ufuzz failure #5217

alexlamsl opened this issue Dec 12, 2021 · 8 comments

Comments

@alexlamsl
Copy link
Collaborator

alexlamsl commented Dec 12, 2021

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

function f0(foo, b_2) {
    --b + (b_2 += (c = c + 1) + ~((foo && (foo[--b + (foo && typeof foo.set == "function" && --_calls_ >= 0 && foo.set(Number(0xdeadn << 16n | 0xbeefn), 5, 24..toString()))] = ([ 3n ][0] > 2 || 22) ^ Infinity + 0)) / (([] & [ , 0 ][1]) << (38..toString() == -2))));
    {
        var brake2 = 5;
        while (0 in {
            null: (c = c + 1) + [ --b + ([] in {}), ..."" + b_2, foo, a++ + delete (foo = -true !== this > -4 & (b_2 && (b_2[c = 1 + c, 
            false * this >>> (5 > 3) == ([ , 0 ].length === 2 != -0 != + -1)] = 38..toString() | this)) < (-5 ^ -0)) ].Infinity
        } && --brake2 > 0) {
            var brake3 = 5;
            L25048: do {
                {
                    var expr4 = (c = c + 1) + (22 in [ ((c = c + 1) + (typeof await_1 != "function") || a || 3).toString() ]);
                    for (foo in expr4) {
                        L25049: for (var brake5 = 5; {
                            get: 25 in []
                        }.value && brake5 > 0; --brake5) {
                            var brake6 = 5;
                            L25050: do {
                                c = c + 1;
                            } while (a++ + void setImmediate(() => typeof f1 == "function" && --_calls_ >= 0 && f1("bar", typeof f2 == "function" && --_calls_ >= 0 && f2(1, -1, (c = 1 + c, 
                            (foo && (foo[a++ + {
                                1.5: (c = 1 + c, ("object" < -0 != (22 !== 25)) < (/[a2][^e]+$/ !== "function") * ("c" >> 22))
                            }[c = 1 + c, (b_2 && (b_2[--b + (foo += (c = 1 + c, (undefined ^ 24..toString()) - (4 ^ "a") <= ("a" & "a") >>> ("a" >> /[a2][^e]+$/)))] = Infinity / "c" + (-4 || 23..toString()))) < (24..toString() >>> undefined ^ (3 ^ false))]] |= (38..toString() || -0) << (Infinity | []))) * (c = c + 1, 
                            "bar" - false))))) && --brake6 > 0);
                        }
                    }
                }
            } while (3 && --brake3 > 0);
        }
    }
}

var a_2 = f0([ --a, a && typeof a.static == "function" && --_calls_ >= 0 && a.static`${(c = c + 1) + [ (a && typeof a.async == "function" && --_calls_ >= 0 && (a, 
a.async)(-5, (c = 1 + c, (a && ({
    NaN: a[c = 1 + c, ("a" & this || 5 / true) & (a += ([ , 0 ][1] & "bar") > ([ 3n ][0] > 2) - 2)]
} = {
    NaN: 24..toString() * this
})) > (-1 !== Infinity) ^ (this >>> {} | false >>> 4))) || 8).toString()[--b + ++a], a && a[--b ? b = a : --b + {
    get: (c = 1 + c, ((a && (a.b ||= "function" - 25)) >>> (5 ^ NaN)) % ((a && (a.foo = 2 || 38..toString())) ^ (a |= {} >= (-42n).toString()))),
    length: (c = 1 + c, (NaN < -5) + ({} && false) == -/[a2][^e]+$/ - ("" - /[a2][^e]+$/))
}[c = 1 + c, (a = (22, true)) << ("a" >>> 38..toString()) == (4 << this) % ("" === [])]], ..."" + a, {
    undefined: (c = c + 1) + {
        set: (c = 1 + c, (delete 23..toString() > (0 & true)) / (a && (a[--b + a--] += (void {}, 
        null / 1)))),
        static: (c = 1 + c, a && (a[--b + (b ||= a)] = (a && (a.foo = "a" >> 24..toString() ^ (a && (a.then += undefined * "object"))), 
        -1 < {} <= true >>> true)))
    },
    0: {
        in: (c = 1 + c, (2 === -2) ** ("number" || -4) << (5 == -3 & (-2 ^ -3))),
        c: (c = 1 + c, (a += (a && (a[c = 1 + c, ("foo" ^ Infinity) % (24..toString() * -2) > delete (22 <= "function")] = 2 + -2)) !== ("a" && -1)) & ((Infinity || "a") | "undefined" - 24..toString())),
        b: (c = 1 + c, 3 >>> ([ , 0 ].length === 2) < (22 < 5) !== ((-42n).toString() !== 4 & (-2 & -4))),
        a: a
    }[c = 1 + c, ("bar" == "b" != ("a" ?? "object")) > ("object" % "c" < 24..toString() + "number")],
    in: a += (c = 1 + c, (c = c + 1, Infinity ^ -5) % (("function" ^ -1) - (NaN << "function"))),
    set: b = a
} ]}${(c = c + 1) + (a && a[--b + (a && a.length)])}${(c = c + 1) + /[abc4]/g.exec(([ , 0 ].length === 2 || b || 5).toString())}` ].__proto__, --b + ([ , a ] = []), this);

console.log(null, a, b, c, Infinity, NaN, undefined);
// uglified code
//
var _calls_=10,a=100,b=10,c=0;function f0(foo,b_2){--b,b_2+=(c+=1)+~((foo&&(foo[--b+(foo&&"function"==typeof foo.set&&0<=--_calls_&&foo.set(Number(0xdeadn<<16n|0xbeefn),5,24..toString()))]=(2<3n||22)^1/0))/((0&[])<<(-2==38..toString())));for(var brake2=5;0 in{null:(c+=1)+[--b+([]in{}),...""+b_2,foo,a+++(foo=-1!==-4<this&(b_2&&(b_2[c=1+c,!1*this>>>!0==(2===[,0].length!=-0!=-1)]=38..toString()|this))<-5,!0)].Infinity}&&0<--brake2;){var brake3=5;do{for(foo in(c+=1)+(22 in[((c+=1)+("function"!=typeof await_1)||a||3).toString()]))for(var brake5=5;{get:25 in[]}.value&&0<brake5;--brake5)for(var brake6=5;c+=1,a+++void setImmediate(()=>"function"==typeof f1&&0<=--_calls_&&f1("bar","function"==typeof f2&&0<=--_calls_&&f2(1,-1,(c=1+c,(foo&&(foo[a+++{1.5:!1}[c=1+(c=1+c),(b_2&&(b_2[--b+(foo+=(c=1+c,(void 0^24..toString())-4<=0))]=NaN))<(24..toString()>>>void 0^3)]]|=(38..toString()||-0)<<(1/0|[])))*(c+=1,NaN)))))&&0<--brake6;);}while(0<--brake3)}}var a_2=f0([--a,a&&"function"==typeof a.static&&0<=--_calls_&&a.static`${(c+=1)+[(a&&"function"==typeof a.async&&0<=--_calls_&&(0,a.async)(-5,(c=1+c,!0<(a&&({NaN:a[c=1+c,("a"&this||5)&(a+=(2<3n)-2<0)]}={NaN:24..toString()*this}))^(this>>>{}|0)))||8).toString()[--b+ ++a],a&&a[--b?b=a:--b+{get:(c=1+c,((a&&(a.b||=NaN))>>>5)%((a&&(a.foo=2))^(a|={}>=(-42n).toString()))),length:(c=1+c,!1)}[c=1+c,(a=!0)<<("a">>>38..toString())==(4<<this)%(""===[])]],...""+a,{undefined:(c+=1)+{set:(c=1+c,23..toString(),!0/(a&&(a[--b+a--]+=0))),static:(c=1+c,a&&(a[--b+(b||=a)]=(a&&(a.foo="a">>24..toString()^(a&&(a.then+=NaN))),-1<{}<=0)))},0:{in:(c=1+c,0),c:(c=1+c,(a+=-1!==(a&&(a[c=1+c,!0<0%(-2*24..toString())]=0)))&(1/0|"undefined"-24..toString())),b:(c=1+c,3>>>(2===[,0].length)<!1!==(4!==(-42n).toString()&-4)),a:a}[c=1+c,NaN<24..toString()+"number"<!0],in:a+=(c=1+c,c+=1,-0),set:b=a}]}${(c+=1)+(a&&a[--b+(a&&a.length)])}${(c+=1)+/[abc4]/g.exec((2===[,0].length||b||5).toString())}`].__proto__,--b+([,a]=[]),this);console.log(null,a,b,c,1/0,NaN,void 0);
original result:
TypeError: callback is not a function
    at processTicksAndRejections (node:internal/process/task_queues:78:11)

Node.js v17.2.0
uglified result:
null NaN 6 3 Infinity NaN undefined
@alexlamsl
Copy link
Collaborator Author

@kzc this one's perplexing. For starters I cannot reproduce any failures locally using the exact same version of Node.js (albeit on Windows − job ran on macOS).

@kzc
Copy link
Contributor

kzc commented Dec 12, 2021

It's 100% reproducible and 100% strange.

$ node-v16.1.0 bin/uglifyjs 5217.js --reduce-test
// Node.js v16.1.0 on darwin x64
// Can't reproduce test failure
// minify options: {
//   "compress": false,
//   "mangle": false
// }
$ node-v16.1.0 bin/uglifyjs 5217.js -c reduce_vars=0,merge_vars=0,collapse_vars=0,unused=0,booleans=0,conditionals=0,join_vars=0 --reduce-test
// Node.js v16.1.0 on darwin x64
// reduce test pass 1, iteration 0: 4090 bytes
// reduce test pass 1, iteration 25: 2620 bytes
// reduce test pass 1, iteration 50: 2558 bytes
// reduce test pass 1, iteration 75: 2558 bytes
// reduce test pass 1, iteration 100: 2511 bytes
// reduce test pass 1, iteration 125: 2505 bytes
// reduce test pass 1, iteration 150: 2511 bytes
// reduce test pass 1, iteration 175: 2505 bytes
// reduce test pass 1, iteration 200: 2515 bytes
...bounces around above 2500 bytes...

After 30 minutes --reduce-test hasn't emitted a result after 650 iterations.

$ node-v16.1.0 5217.js 
null NaN 6 3 Infinity NaN undefined

$ cat 5217.js | node-v16.1.0 
null NaN 6 3 Infinity NaN undefined

$ cat 5217.js | cat | node-v16.1.0 
null NaN 6 3 Infinity NaN undefined

$ cat 5217.js | uglify-js | node-v16.1.0 
null NaN 6 3 Infinity NaN undefined
node:internal/process/task_queues:78
          callback();
          ^
TypeError: callback is not a function
    at processTicksAndRejections (node:internal/process/task_queues:78:11)

$ cat 5217.js | uglify-js > 0.js

$ cat 0.js | node-v16.1.0 
null NaN 6 3 Infinity NaN undefined

$ node-v16.1.0 0.js
null NaN 6 3 Infinity NaN undefined
$ cat 5217.js | uglify-js -b | node-v16.1.0 
null NaN 6 3 Infinity NaN undefined
node:internal/process/task_queues:78
          callback();
          ^
TypeError: callback is not a function
    at processTicksAndRejections (node:internal/process/task_queues:78:11)

$ cat 5217.js | uglify-js -b > 1.js

$ node-v16.1.0 1.js 
null NaN 6 3 Infinity NaN undefined

$ cat 1.js | node-v16.1.0 
null NaN 6 3 Infinity NaN undefined
$ diff -U0 5217.js 1.js
--- 5217.js	2021-12-12 10:10:35.000000000 -0500
+++ 1.js	2021-12-12 11:01:18.000000000 -0500
@@ -1,2 +0,0 @@
-// original code
-// (beautified)
@@ -63 +60,0 @@
-

I think it's a NodeJS bug that has something to do with the rate input is sent to its parser.

@alexlamsl
Copy link
Collaborator Author

Interesting − so looks like it's harder to hit this bug on Windows (or my specific set of hardware), but not entirely impossible:

> ver
Microsoft Windows [Version 10.0.14393]
> node -v
v17.2.0
> node bin\uglifyjs 5217.js --reduce-test
// Node.js v17.2.0 on win32 x64
// Can't reproduce test failure
// minify options: {
//   "compress": false,
//   "mangle": false
// }
> node bin\uglifyjs 5217.js -c reduce_vars=0,merge_vars=0,collapse_vars=0,unused=0,booleans=0,conditionals=0,join_vars=0 --reduce-test
// Node.js v17.2.0 on win32 x64
// Can't reproduce test failure
// minify options: {
//   "compress": {
//     "reduce_vars": 0,
//     "merge_vars": 0,
//     "collapse_vars": 0,
//     "unused": 0,
//     "booleans": 0,
//     "conditionals": 0,
//     "join_vars": 0
//   },
//   "mangle": false
// }

So far so "good" − however:

> type 5217.js | node
null NaN 6 3 Infinity NaN undefined
> type 5217.js | node bin\uglifyjs | node
null NaN 6 3 Infinity NaN undefined
node:internal/process/task_queues:78
          callback();
          ^

TypeError: callback is not a function
    at processTicksAndRejections (node:internal/process/task_queues:78:11)

Node.js v17.2.0

@kzc
Copy link
Contributor

kzc commented Dec 12, 2021

I tried command line utilities to slow down catting to NodeJS but the code ran correctly. But those utilities were line-based rather than character based. I think the NodeJS and/or V8 parser has a bug near or slightly after the 2048 byte mark.

@alexlamsl
Copy link
Collaborator Author

Ripping out the new language features, this bug seems to go all the way back to Node.js v10:

> node -v
v8.17.0

> node bin\uglifyjs test.js | node
null NaN 6 3 Infinity NaN undefined
> node -v
v10.24.1

> node bin\uglifyjs test.js | node
null NaN 6 3 Infinity NaN undefined
RangeError [ERR_INVALID_ASYNC_ID]: Invalid asyncId value: undefined
    at validateAsyncId (internal/async_hooks.js:122:16)
    at emitBeforeScript (internal/async_hooks.js:344:3)
    at process._tickCallback (internal/process/next_tick.js:46:9)
    at evalScript (internal/bootstrap/node.js:593:13)
    at Socket.<anonymous> (internal/bootstrap/node.js:323:15)
    at Socket.emit (events.js:203:15)
    at endReadableNT (_stream_readable.js:1145:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)
> node -v
v12.22.7

> node bin\uglifyjs test.js | node
null NaN 6 3 Infinity NaN undefined
internal/process/task_queues.js:79
          callback();
          ^

TypeError: callback is not a function
    at processTicksAndRejections (internal/process/task_queues.js:79:11)
> node -v
v14.18.2

> node bin\uglifyjs test.js | node
null NaN 6 3 Infinity NaN undefined
internal/process/task_queues.js:77
          callback();
          ^

TypeError: callback is not a function
    at processTicksAndRejections (internal/process/task_queues.js:77:11)
> node -v
v16.13.1

> node bin\uglifyjs test.js | node
null NaN 6 3 Infinity NaN undefined
node:internal/process/task_queues:78
          callback();
          ^

TypeError: callback is not a function
    at processTicksAndRejections (node:internal/process/task_queues:78:11)
> node -v
v17.2.0

> node bin\uglifyjs test.js | node
null NaN 6 3 Infinity NaN undefined
node:internal/process/task_queues:78
          callback();
          ^

TypeError: callback is not a function
    at processTicksAndRejections (node:internal/process/task_queues:78:11)

Node.js v17.2.0

test.js

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

function f0(foo, b_2) {
    --b + (b_2 += (c = c + 1) + ~((foo && (foo[--b + (foo && typeof foo.set == "function" && --_calls_ >= 0 && foo.set())] = (3 > 2 || 22) ^ Infinity + 0)) / (([] & [ , 0 ][1]) << (38..toString() == -2))));
    {
        var brake2 = 5;
        while (0 in {
            null: (c = c + 1) + [ --b + ([] in {}), ..."" + b_2, foo, a++ + delete (foo = -true !== this > -4 & (b_2 && (b_2[c = 1 + c,
            false * this >>> (5 > 3) == ([ , 0 ].length === 2 != -0 != + -1)] = 38..toString() | this)) < (-5 ^ -0)) ].Infinity
        } && --brake2 > 0) {
            var brake3 = 5;
            L25048: do {
                {
                    var expr4 = (c = c + 1) + (22 in [ ((c = c + 1) + (typeof await_1 != "function") || a || 3).toString() ]);
                    for (foo in expr4) {
                        L25049: for (var brake5 = 5; {
                            get: 25 in []
                        }.value && brake5 > 0; --brake5) {
                            var brake6 = 5;
                            L25050: do {
                                c = c + 1;
                            } while (a++ + void setImmediate(() => typeof f1 == "function" && --_calls_ >= 0 && f1("bar", typeof f2 == "function" && --_calls_ >= 0 && f2(1, -1, (c = 1 + c,
                            (foo && (foo[a++ + {
                                1.5: (c = 1 + c, ("object" < -0 != (22 !== 25)) < (/[a2][^e]+$/ !== "function") * ("c" >> 22))
                            }[c = 1 + c, (b_2 && (b_2[--b + (foo += (c = 1 + c, (undefined ^ 24..toString()) - (4 ^ "a") <= ("a" & "a") >>> ("a" >> /[a2][^e]+$/)))] = Infinity / "c" + (-4 || 23..toString()))) < (24..toString() >>> undefined ^ (3 ^ false))]] |= (38..toString() || -0) << (Infinity | []))) * (c = c + 1,
                            "bar" - false))))) && --brake6 > 0);
                        }
                    }
                }
            } while (3 && --brake3 > 0);
        }
    }
}

var a_2 = f0([ --a, a && typeof a.static == "function" && --_calls_ >= 0 && a.static`${(c = c + 1) + [ (a && typeof a.async == "function" && --_calls_ >= 0 && (a,
a.async)(-5, (c = 1 + c, (a && ({
    NaN: a[c = 1 + c, ("a" & this || 5 / true) & (a += ([ , 0 ][1] & "bar") > (3 > 2) - 2)]
} = {
    NaN: 24..toString() * this
})) > (-1 !== Infinity) ^ (this >>> {} | false >>> 4))) || 8).toString()[--b + ++a], a && a[--b ? b = a : --b + {
    length: (c = 1 + c, (NaN < -5) + ({} && false) == -/[a2][^e]+$/ - ("" - /[a2][^e]+$/))
}[c = 1 + c, (a = (22, true)) << ("a" >>> 38..toString()) == (4 << this) % ("" === [])]], ..."" + a, {
    undefined: (c = c + 1) + {
        set: (c = 1 + c, (delete 23..toString() > (0 & true)) / (a && (a[--b + a--] += (void {},
        null / 1)))),
    },
    in: a += (c = 1 + c, (c = c + 1, Infinity ^ -5) % (("function" ^ -1) - (NaN << "function"))),
    set: b = a
} ]}${(c = c + 1) + (a && a[--b + (a && a.length)])}${(c = c + 1) + /[abc4]/g.exec(([ , 0 ].length === 2 || b || 5).toString())}` ].__proto__, --b + ([ , a ] = []), this);

console.log(null, a, b, c, Infinity, NaN, undefined);

@alexlamsl
Copy link
Collaborator Author

I tried command line utilities to slow down catting to NodeJS but the code ran correctly. But those utilities were line-based rather than character based.

Another possibility might be the TTY modes when piping one node process to another vs. cat to node.

@kzc
Copy link
Contributor

kzc commented Dec 12, 2021

This bug is only peripherally related to uglify. Uglify happens to emit output at the exact rate needed to trigger the bug in NodeJS.

If tests run on my Mac are any indication, I'm sure if you do the following on Windows, the NodeJS bug will not be triggered:

> type 5217.js | node

> type 5217.js | node bin\uglifyjs > 0.js

> type 0.js | node

> node 0.js

@alexlamsl
Copy link
Collaborator Author

Indeed they seem to work on both Node.js v16 & v17:

> node -v
v16.13.1

> type 5217.js | node
null NaN 6 3 Infinity NaN undefined

> type 5217.js | node bin\uglifyjs > 0.js

> type 0.js | node
null NaN 6 3 Infinity NaN undefined

> node 0.js
null NaN 6 3 Infinity NaN undefined
> node -v
v17.2.0

> type 5217.js | node
null NaN 6 3 Infinity NaN undefined

> type 5217.js | node bin\uglifyjs > 0.js

> type 0.js | node
null NaN 6 3 Infinity NaN undefined

> node 0.js
null NaN 6 3 Infinity NaN undefined

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