Closed Bug 1687661 Opened 4 years ago Closed 4 years ago

Issue involving IonMonkey and Math.trunc

Categories

(Core :: JavaScript Engine: JIT, defect, P1)

defect

Tracking

()

RESOLVED FIXED
86 Branch
Tracking Status
firefox-esr78 --- unaffected
firefox84 --- unaffected
firefox85 --- unaffected
firefox86 --- fixed

People

(Reporter: gkw, Assigned: iain)

References

(Blocks 1 open bug, Regression)

Details

(Keywords: regression, reporter-external, testcase)

Attachments

(2 files)

let x = [0, 0 / 0, 0.1, 1]
function f(x, y) {
    return Math.trunc(+(y ? x : y) || ~y);
}
for (let i = 0; i < 3; i++) {
    f(x[i], 0);
    f(x[i], 0);
    f(x[i], 1);
    if (i !== 2) {
        f(x[i], 1);
    } else {
        assertEq(f(x[i], 1), 0);
    }
}

AR=ar sh ./configure --enable-debug --with-ccache --enable-gczeal --enable-debug-symbols --disable-tests

Assertion fails on latest m-c rev a3cd8f83fefa with -fuzzing-safe --differential-testing --no-threads --ion-eager, using GCC 10.2.0, passes when run with --fuzzing-safe --differential-testing --no-threads --baseline-eager --no-ion. --disable-bailout-loop-check does not seem to make the issue go away. Also passes when run on rev 680f46ca1194, the parent of the potential regressor on both sets of flags.

$ ./js-dbg-64-linux-x86_64-a3cd8f83fefa --fuzzing-safe --differential-testing --no-threads --ion-eager failed-assert.js 
failed-assert.js:12:17 Error: Assertion failed: got 0.1, expected 0
Stack:
  @failed-assert.js:12:0

May be related to bug 1686702.

Unsure how bad this is, so marking s-s and setting needinfo? from :iain. Please open up if this is not the case.

Flags: needinfo?
The first bad revision is:
changeset:   https://hg.mozilla.org/mozilla-central/rev/256684d262e2
user:        Iain Ireland
date:        Fri Jan 15 17:59:56 2021 +0000
summary:     Bug 1686702: Fold to MToNumberInt32 r=jandem
Flags: needinfo? → needinfo?(iireland)

Set release status flags based on info from the regressing bug 1686702

My patch in bug 1686702 is missing a setGuard.

We have nodes like this:

26 phi bitnot23:Int32 unbox20:Double
...
31 unbox phi26 to Int32 (fallible)
32 return unbox32:Int32

In TypeAnalyzer, we see that phi26 has inputs of different types and insert a ToDouble in the previous block. As a result, unbox31 is trying to unbox a Double, so we insert a Box. But then return32, which uses BoxInputsPolicy, calls BoxAt on its operand. Because it is an Unbox, this code kicks in and looks past the Unbox, giving us the result:

26 phi toDouble33:Double unbox20:Double 
...
34 box phi26:Double 
31 unbox box34 to Int32 (fallible)
32 return box34:Value       

The return no longer directly depends on the unbox. That's not an immediate problem, because the unbox is fallible and therefore a guard. However, in GVN the code that I added kicks in and converts box34 and unbox31 into tonumberint32 phi27:Double, which is not a guard. It has no uses, so DCE eliminates it.

I don't think this can lead to type confusion, because it only occurs when the conversion has no uses. I think that this is just a correctness issue, but I'm not 100% certain.

Patch coming shortly.

Flags: needinfo?(iireland)
Assignee: nobody → iireland
Status: NEW → ASSIGNED

Depends on D102468

(In reply to Iain Ireland [:iain] from comment #3)

I don't think this can lead to type confusion, because it only occurs when the conversion has no uses. I think that this is just a correctness issue, but I'm not 100% certain.

Should we unhide this?

Group: core-security → javascript-core-security
Flags: needinfo?(iireland)

I don't think it would hurt to keep this hidden until I land the fix.

Flags: needinfo?(iireland)

Thanks. I'll just mark this as sec-audit then.

Keywords: sec-audit

In practice, off-by-1 issues caused by truncation could lead to bigger problem which allow to read beyond boundaries in some cases, such as when the error flow in Range Analysis and causes bad hypothesis.

We should indeed be careful with such errors.

I don't think this can lead to type confusion, because it only occurs when the conversion has no uses. I think that this is just a correctness issue, but I'm not 100% certain.

How something which has no uses can flow in the returned value of a function? Or this is a different issue than the bad assertion?

Severity: -- → S2
Priority: -- → P1

nbp: See my description above. The function returns Math.trunc(X), but so far we've only seen Int32 values for X, so Math.trunc is transpiled to a fallible Unbox<int32>.

31 unbox phi26 to Int32 (fallible)
32 return unbox32:Int32

TypeAnalysis makes a series of individually reasonable changes that end up with us boxing the return value earlier, and not depending on the unbox except as a guard.

34 box phi26:Double 
31 unbox box34 to Int32 (fallible)
32 return box34:Value  

This will, unfortunately, always bail, even though (based on baseline IC information) we know that the double value is extremely likely to be an integer. This can cause bailout loops. In bug 1686702 I added some code in MUnbox::foldsTo that converts Unbox<int32>(Box<double>(X)) to ToNumberInt32(X), which will check whether the double can be represented as an Int32 before bailing.

However, I was not marking the ToNumberInt32 as a guard. In this case, it had no explicit uses, so DCE removed it, which meant that we didn't bail out when we should have.

There shouldn't be any concerns about bad values flowing into RangeAnalysis, because if it had any uses then DCE would not remove it.

Flags: sec-bounty?

(In reply to Iain Ireland [:iain] from comment #10)

There shouldn't be any concerns about bad values flowing into RangeAnalysis, because if it had any uses then DCE would not remove it.

Sounds like this is not s-s, can we unhide this bug :iain?

Flags: needinfo?(iireland)

Similarly, if so, we should remove sec-audit

Group: core-security-release
Flags: needinfo?(iireland)
Keywords: sec-audit
Flags: sec-bounty? → sec-bounty-
Has Regression Range: --- → yes
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: