Bug 210816 - Canonicalize JSBigInt generated by structured-cloning by calling rightTrim
Summary: Canonicalize JSBigInt generated by structured-cloning by calling rightTrim
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Yusuke Suzuki
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2020-04-21 14:24 PDT by Yusuke Suzuki
Modified: 2020-04-21 19:37 PDT (History)
11 users (show)

See Also:


Attachments
Patch (1.88 KB, patch)
2020-04-21 14:26 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (8.42 KB, patch)
2020-04-21 14:42 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (8.42 KB, patch)
2020-04-21 14:59 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (8.95 KB, patch)
2020-04-21 15:56 PDT, Yusuke Suzuki
keith_miller: review+
Details | Formatted Diff | Diff
Patch for landing (11.96 KB, patch)
2020-04-21 17:12 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch for landing (11.93 KB, patch)
2020-04-21 17:18 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Yusuke Suzuki 2020-04-21 14:24:45 PDT
[JSC] Call rightTrim to ensure that corrupted serialized data should not produce wrong JSBigInt
Comment 1 Yusuke Suzuki 2020-04-21 14:26:08 PDT
Created attachment 397119 [details]
Patch
Comment 2 Yusuke Suzuki 2020-04-21 14:42:33 PDT
Created attachment 397123 [details]
Patch
Comment 3 Yusuke Suzuki 2020-04-21 14:59:41 PDT
Created attachment 397128 [details]
Patch
Comment 4 Darin Adler 2020-04-21 15:04:09 PDT
Comment on attachment 397128 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=397128&action=review

> Source/WebCore/bindings/js/SerializedScriptValue.cpp:3038
> +            bigInt = bigInt->rightTrim(m_lexicalGlobalObject->vm());

Wouldn’t it be better for deserialization to *fail* if rightTrim would do anything here?

> Source/WebCore/bindings/js/SerializedScriptValue.cpp:3074
> +        bigInt = bigInt->rightTrim(m_lexicalGlobalObject->vm());

Ditto.
Comment 5 Keith Miller 2020-04-21 15:28:46 PDT
Comment on attachment 397128 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=397128&action=review

> Source/JavaScriptCore/ChangeLog:3
> +        Call rightTrim to ensure that corrupted serialized data should not produce wrong JSBigInt to make structured-cloning for JSBigInt more robust

That's a mouthful, can we just say: "Make structured-cloning for JSBigInt more robust by calling rightTrim"?

We can explain why in the ChangLog.

> Source/WebCore/ChangeLog:8
> +        Let's assume that the serialized data is corrupted. In that case, we should (1) throw an failure if it is detected or (2) produce a right value if it is not detected.

For (2) do you mean a valid value? What would the "right" value be for corrupted input?

> Source/WebCore/ChangeLog:9
> +        We should call `JSBigInt::rightTrim(VM&)` when finishing JSBigInt deserialization to at least ensure that (2).

Nit: ensure that (2) => ensure (2)

>> Source/WebCore/bindings/js/SerializedScriptValue.cpp:3038
>> +            bigInt = bigInt->rightTrim(m_lexicalGlobalObject->vm());
> 
> Wouldn’t it be better for deserialization to *fail* if rightTrim would do anything here?

The only upside I can see of not failing would be that maybe a developer could work around a bug on our end. Overall, I agree with you though.
Comment 6 Yusuke Suzuki 2020-04-21 15:41:38 PDT
Comment on attachment 397128 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=397128&action=review

Thanks!

>> Source/JavaScriptCore/ChangeLog:3
>> +        Call rightTrim to ensure that corrupted serialized data should not produce wrong JSBigInt to make structured-cloning for JSBigInt more robust
> 
> That's a mouthful, can we just say: "Make structured-cloning for JSBigInt more robust by calling rightTrim"?
> 
> We can explain why in the ChangLog.

Will fix.

>> Source/WebCore/ChangeLog:8
>> +        Let's assume that the serialized data is corrupted. In that case, we should (1) throw an failure if it is detected or (2) produce a right value if it is not detected.
> 
> For (2) do you mean a valid value? What would the "right" value be for corrupted input?

Valid value. Corrupt means that it is not ensuring JSBigInt's data layout invariant. For example, zero should be length = 0 and sign = false. But we can represent this value in various form like, length = 1 and value = 0. Or sign = true, length = 0.
This patch is generating correct value while ignoring data layout invariant in JSBigInt.

>> Source/WebCore/ChangeLog:9
>> +        We should call `JSBigInt::rightTrim(VM&)` when finishing JSBigInt deserialization to at least ensure that (2).
> 
> Nit: ensure that (2) => ensure (2)

Fixed.

>>> Source/WebCore/bindings/js/SerializedScriptValue.cpp:3038
>>> +            bigInt = bigInt->rightTrim(m_lexicalGlobalObject->vm());
>> 
>> Wouldn’t it be better for deserialization to *fail* if rightTrim would do anything here?
> 
> The only upside I can see of not failing would be that maybe a developer could work around a bug on our end. Overall, I agree with you though.

My wording would be misleading. Here, what I said wrong is "wrong in terms of internal invariant of JSBigInt". For example, in this format, we have several ways to represent zero JSBigInt. One is 0 length. One is 1 length, and value is zero. One is 0-length, but sign is true.
But in JSBigInt layout, we are assuming some invariant in JSBigInt. For example, if JSBigInt is zero, length should be zero, and sign flag should be false.
This `rightTrim(VM&)` offers the way to canonicalize JSBigInt format even if the input is not holding JSBigInt's internal invariant.
I think this direction is better. We should not expose such an internal invariant of JSBigInt in serialization code. This makes JSBigInt implementation free from the format of serialization code.
Just calling rightTrim correctly canonicalize JSBigInt's internal representation regardless of how it is constructed.
Comment 7 Yusuke Suzuki 2020-04-21 15:51:59 PDT
Comment on attachment 397128 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=397128&action=review

>>>> Source/WebCore/bindings/js/SerializedScriptValue.cpp:3038
>>>> +            bigInt = bigInt->rightTrim(m_lexicalGlobalObject->vm());
>>> 
>>> Wouldn’t it be better for deserialization to *fail* if rightTrim would do anything here?
>> 
>> The only upside I can see of not failing would be that maybe a developer could work around a bug on our end. Overall, I agree with you though.
> 
> My wording would be misleading. Here, what I said wrong is "wrong in terms of internal invariant of JSBigInt". For example, in this format, we have several ways to represent zero JSBigInt. One is 0 length. One is 1 length, and value is zero. One is 0-length, but sign is true.
> But in JSBigInt layout, we are assuming some invariant in JSBigInt. For example, if JSBigInt is zero, length should be zero, and sign flag should be false.
> This `rightTrim(VM&)` offers the way to canonicalize JSBigInt format even if the input is not holding JSBigInt's internal invariant.
> I think this direction is better. We should not expose such an internal invariant of JSBigInt in serialization code. This makes JSBigInt implementation free from the format of serialization code.
> Just calling rightTrim correctly canonicalize JSBigInt's internal representation regardless of how it is constructed.

Let's consider about adding a new invariant into JSBigInt's internal format. If we throw an error if serialized format does not meet JSBigInt's internal invariant, then, we cannot easily modify JSBigInt's invariant because this serialization format requires backward-compatibility.
On the other hand, if this canonicalizes the generated JSBigInt, then we can freely add new invariants into JSBigInt implementation without considering the serialization format.
Comment 8 Darin Adler 2020-04-21 15:55:08 PDT
(In reply to Yusuke Suzuki from comment #6)
> I think this direction is better. We should not expose such an internal
> invariant of JSBigInt in serialization code.

OK.
Comment 9 Yusuke Suzuki 2020-04-21 15:56:59 PDT
Created attachment 397134 [details]
Patch
Comment 10 Yusuke Suzuki 2020-04-21 15:57:14 PDT
(In reply to Darin Adler from comment #8)
> (In reply to Yusuke Suzuki from comment #6)
> > I think this direction is better. We should not expose such an internal
> > invariant of JSBigInt in serialization code.
> 
> OK.

Thanks!! I've updated.
Comment 11 Keith Miller 2020-04-21 16:13:29 PDT
Comment on attachment 397134 [details]
Patch

r=me. I was forgetting that the point of this is to be written to disk. This patch makes sense now.
Comment 12 Yusuke Suzuki 2020-04-21 16:15:22 PDT
I'll further add a test using `internals.deserializeBuffer` to test that this canonicalization happens correctly!
Comment 13 Yusuke Suzuki 2020-04-21 17:12:14 PDT
Created attachment 397143 [details]
Patch for landing
Comment 14 Yusuke Suzuki 2020-04-21 17:18:38 PDT
Created attachment 397145 [details]
Patch for landing
Comment 15 EWS 2020-04-21 19:36:02 PDT
Committed r260489: <https://trac.webkit.org/changeset/260489>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 397145 [details].
Comment 16 Radar WebKit Bug Importer 2020-04-21 19:37:15 PDT
<rdar://problem/62148234>