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

Closed
alexlamsl opened this issue Nov 8, 2020 · 8 comments · Fixed by #4270
Closed

ufuzz failure #4269

alexlamsl opened this issue Nov 8, 2020 · 8 comments · Fixed by #4270
Labels

Comments

@alexlamsl
Copy link
Collaborator

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

function f0(parseInt) {
    c = c + 1;
    a = {
        3: a && a.foo,
        set 1.5(a_2) {
            {
                var expr3 = a_2 && typeof a_2.c == "function" && --_calls_ >= 0 && a_2.c();
                for (a_2 in expr3) {
                    c = 1 + c;
                    var c_2 = expr3[a_2];
                    c = c + 1;
                }
            }
            this.foo /= (((-2 ^ -2 ^ 4 << "a") !== ([ , 0 ][1] < 24..toString() && "a" < "foo")) >= (2 - "function") % ("a" << -5) >>> ("a" == this) / +0) % ((c = c + 1, 
            (null && 2) === (25 == "foo")) > (("" | -5 || 24..toString() != "a") == ((c_2 && (c_2[c = 1 + c, 
            (22, false, 4 >>> "undefined") > (-3 <= "bar" > ("object" != this))] *= "b" << ([ , 0 ].length === 2))) == ("", 
            -0)))) === (("" >> [ , 0 ][1] === 1 % {}) - (-4 >>> 38..toString()) % ("" && "number") !== ("undefined" >= undefined >= "foo" % "undefined" == ("object" << Infinity || Infinity !== 5)) != (([ , 0 ].length === 2) * {} & 22 < null | this >>> false === ([] && "c")) - ((a_2 = "c" || 3) && /[a2][^e]+$/ + undefined) * (c_2 && (c_2[(c = c + 1) + (c_2 && typeof c_2.in == "function" && --_calls_ >= 0 && c_2.in())] = (a_2 && (a_2[c = 1 + c, 
            ("c" <= null & (3 ^ NaN)) + ((c_2 -= "number" > 4) !== false < 22)] -= "foo" === undefined)) ^ (null | 22))));
        },
        set 3(Infinity_1) {
            try {
                {
                    return a++ + (typeof f1 == "function" && --_calls_ >= 0 && f1("undefined", (c = 1 + c, 
                    ((c = c + 1, "") ^ "b" != "") << ("number" >= "c") - (22 !== "bar"))));
                }
            } catch (b_1) {
                var b_1_2;
                try {
                    try {
                        c = 1 + c, ("a" & "foo") / (([ , 0 ].length === 2) >= "a") >= (([] & 0) >= "b" / null);
                    } catch (a) {
                    } finally {
                    }
                } catch (b_1) {
                    c = 1 + c, null - ([ , 0 ].length === 2) && NaN * 3 && 23..toString() != -1 ^ {} === this;
                    c = 1 + c, (undefined & -4) + (-3 && [ , 0 ][1]) + ((Infinity_1 && (Infinity_1.foo %= "bar" || null)) >>> ("object" > 5));
                }
            }
            this.in = (("number" % 5 | 0 * -3) <= (undefined & "number") >>> this - Infinity ^ (b_1_2 += [] >= [] & -1 >>> 1 & (5 < undefined) + (25 === -2))) >> ((-5 >= -1) >>> ({} ^ "c") === 5 / -3 % (0 >> 22) & (NaN >>> 25 < 24..toString() * "a") * (([ , 0 ][1] || -0) && 1 | -0)) || (((3, 
            3) ^ (38..toString() ^ [ , 0 ][1])) === (false === 24..toString() ^ (-2 ^ "c"))) >>> ((1 > -4 != (Infinity_1 && (Infinity_1.null %= 2 / "undefined"))) <= ((0 == "c") >= (b_1_2 += "foo" * -0))) >= (("undefined" == /[a2][^e]+$/ | ![]) ^ (Infinity_1 && (Infinity_1.b += [] > this && 22 == "foo"))) >>> ((4 ^ 25) === ("foo" & "b") || b_1_2 && (b_1_2[void b] &= {} % undefined & (Infinity_1 && (Infinity_1[c = 1 + c, 
            ((22 && -2) ^ undefined < []) <= (b_1_2 && (b_1_2[--b + (b_1_2 && typeof b_1_2.var == "function" && --_calls_ >= 0 && b_1_2.var(38..toString(), false))] = (this ^ Infinity) * (-5 % "c")))] += 1 < 38..toString()))));
        },
        [undefined / 3 - (25 && [ , 0 ][1]) > ("function" <= [ , 0 ][1] <= (NaN ^ false))]: 0 === 1 ? a : b,
        3: typeof a == "function" && --_calls_ >= 0 && a(4)
    };
}

var a_1 = f0(/[a2][^e]+$/);

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

function f0(parseInt) {
    c += 1, a = {
        3: a && a.foo,
        set 1.5(a_2) {
            var expr3 = a_2 && "function" == typeof a_2.c && 0 <= --_calls_ && a_2.c();
            for (a_2 in expr3) {
                c = 1 + c;
                var c_2 = expr3[a_2];
                c += 1;
            }
            this.foo /= ((4 !== (0 < 24..toString() && !0)) >= NaN >>> ("a" == this) / 0) % (c += 1, 
            (-5 == (-0 == (c_2 && (c_2[c = 1 + c, ("object" != this) < !1 < 4] *= "b" << (2 === [ , 0 ].length))))) < !1) === ((0 == 1 % {}) - (-4 >>> 38..toString()) % "" !== !1 != ((2 === [ , 0 ].length) * {} & !1 | this >>> !1 === "c") - (a_2 = "c", 
            (/[a2][^e]+$/ + void 0) * (c_2 && (c_2[(c += 1) + (c_2 && "function" == typeof c_2.in && 0 <= --_calls_ && c_2.in())] = 22 ^ (a_2 && (a_2[c = 1 + c, 
            +(!0 !== (c_2 -= !1))] -= !1))))));
        },
        set 3(Infinity_1) {
            try {
                return a++ + ("function" == typeof f1 && 0 <= --_calls_ && f1("undefined", (c = 1 + c, 
                (!0 ^ (c += 1, "")) << 0)));
            } catch (b_1) {
                var b_1_2;
                try {
                    try {
                        c = 1 + c;
                    } catch (a) {}
                } catch (b_1) {
                    c = 1 + (c = 1 + c), Infinity_1 && (Infinity_1.foo %= "bar");
                }
            }
            this.in = (0 <= 0 >>> this - 1 / 0 ^ (b_1_2 += [] <= [] & -1 >>> 1 & 0)) >> (!1 >>> ("c" ^ {}) === NaN & -0 * (0 < "a" * 24..toString())) || ((3 ^ 38..toString()) == (!1 === 24..toString() ^ -2)) >>> ((1 != (Infinity_1 && (Infinity_1.null %= NaN))) <= ((b_1_2 += NaN) <= !1)) >= (0 ^ (Infinity_1 && (Infinity_1.b += this < [] && !1))) >>> (b_1_2 && (b_1_2[void 0] &= {} % void 0 & (Infinity_1 && (Infinity_1[c = 1 + c, 
            (-2 ^ void 0 < []) <= (b_1_2 && (b_1_2[--b + (b_1_2 && "function" == typeof b_1_2.var && 0 <= --_calls_ && b_1_2.var(38..toString(), !1))] = NaN * (this ^ 1 / 0)))] += 1 < 38..toString()))));
        },
        false: b,
        3: "function" == typeof a && 0 <= --_calls_ && a(4)
    };
}

var a_1 = f0(/[a2][^e]+$/);

console.log(null, a, b, c, 1 / 0, NaN, void 0);
original result:
null { '3': false, '1.5': [Setter], false: 10 } 10 1 Infinity NaN undefined

uglified result:
null { '3': [Setter], '1.5': [Setter], false: 10 } 10 1 Infinity NaN undefined
// reduced test case (output will differ)

// (beautified)
console.log({
    set 3(Infinity_1) {},
    [0]: 0,
    3: 0
});
// output: { '0': 0, '3': 0 }
// 
// minify: { '0': 0, '3': [Setter] }
// 
// options: {
//   "mangle": false,
//   "validate": true
// }
minify(options):
{
  "mangle": false
}

Suspicious compress options:
  evaluate
  objects
  properties
  side_effects
@alexlamsl alexlamsl added the bug label Nov 8, 2020
@alexlamsl
Copy link
Collaborator Author

@kzc behold the beauty of ECMAScript! 🤣

$ node -v
v14.13.1
$ echo console.log({ set 1(v){}, [2]:0, 1:3 }) | node
{ '1': 3, '2': 0 }

$ echo console.log({ set 1(v){}, 2:0, 1:3 }) | node
{ '1': [Setter], '2': 0 }

A computed property will reorder your other properties even though the computed value is not colliding with any other names. But only if those other names are numbers...

$ echo console.log({ set p(v){}, [2]:0, p:3 }) | node
{ '2': 0, p: 3 }

$ echo console.log({ set p(v){}, 2:0, p:3 }) | node
{ '2': 0, p: 3 }

... so how about if I quote those "numbers" so they are definitely strings?

$ echo console.log({ set "1"(v){}, [2]:0, "1":3 }) | node
{ '1': 3, '2': 0 }

$ echo console.log({ set "1"(v){}, 2:0, "1":3 }) | node
{ '1': [Setter], '2': 0 }
$ echo console.log({ set "1"(v){}, ["2"]:0, "1":3 }) | node
{ '1': 3, '2': 0 }

$ echo console.log({ set "1"(v){}, "2":0, "1":3 }) | node
{ '1': [Setter], '2': 0 }

... nup, coz reasons.

@kzc
Copy link
Contributor

kzc commented Nov 8, 2020

That's pretty obscure. Only a fuzzer would find such a thing.

If it makes you feel any better, both babel and buble get it wrong too:

$ echo 'console.log({ set 1(v){}, [2]:0, 1:3 })' | node
{ '1': 3, '2': 0 }

$ echo 'console.log({ set 1(v){}, [2]:0, 1:3 })' | babel | node
{ '1': [Setter], '2': 0 }

$ echo 'console.log({ set 1(v){}, [2]:0, 1:3 })' | buble | node
{ '1': [Setter], '2': 0 }

Sometimes I find if you read the ES spec there is some rhyme or reason for the ordering. Then other times it's just an arbitrary decision based on the initial implementation of the feature.

@alexlamsl
Copy link
Collaborator Author

You'd hope there'll at least be consistency...

$ node -v
v0.10.48

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': 2, '07': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': 2, '08': [Setter] }
$ node -v
v0.12.18

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
[stdin]:1
console.log({ set 07(v) {}, 07: 2, });
                            ^^
SyntaxError: Object literal may not have data and accessor property with the same name
    at Object.exports.runInThisContext (vm.js:73:16)

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
[stdin]:1
console.log({ set 08(v) {}, 08: 2, });
                            ^^
SyntaxError: Object literal may not have data and accessor property with the same name
    at Object.exports.runInThisContext (vm.js:73:16)
$ node -v
v4.9.1

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': [Setter] }
$ node -v
v6.17.1

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': [Setter] }
$ node -v
v8.17.0

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': [Setter] }
$ node -v
v10.22.1

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': [Setter] }
$ node -v
v12.19.0

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': 2, '08': [Setter] }
$ node -v
v14.13.1

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': 2, '08': [Setter] }

alexlamsl added a commit to alexlamsl/UglifyJS that referenced this issue Nov 9, 2020
@kzc
Copy link
Contributor

kzc commented Nov 9, 2020

You'd hope there'll at least be consistency

Interesting. Either this is behavior is not defined by the spec, or it's an engine issue. What do other browser engines produce?

alexlamsl added a commit that referenced this issue Nov 9, 2020
@alexlamsl
Copy link
Collaborator Author

Syntax Error (duplicated setter/property name) on Internet Explorer 11

{ 7: 2 } & { 8: 2 } on Firefox 82

Error: Unexpected number '07' & '08' on Safari 5

Syntax Error (duplicated setter/property name) on Opera 12

$ nvs use chakracore
v10.13.0

$ echo console.log({ set 07(v) {}, 07: 2, }); | node
{ '7': [Setter] }

$ echo console.log({ set 08(v) {}, 08: 2, }); | node
{ '8': [Setter] }

@kzc
Copy link
Contributor

kzc commented Nov 9, 2020

So it appears to be undefined behavior in the spec. Not helpful for fuzzing.

@kzc
Copy link
Contributor

kzc commented May 25, 2021

@alexlamsl
Copy link
Collaborator Author

Let's see if they would take care of #4941 as well.

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

Successfully merging a pull request may close this issue.

2 participants