WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
211205
We can't cast toLength result to unsigned
https://bugs.webkit.org/show_bug.cgi?id=211205
Summary
We can't cast toLength result to unsigned
Saam Barati
Reported
2020-04-29 13:58:02 PDT
This is UB in C++, and does different things depending on what architecture you're on. For example, on x86, this will give us a length of zero, which violates the spec: double x = static_cast<double>(UINT_MAX) + 1; unsigned length = x; On arm64, this will give us UINT_MAX, which is also wrong.
Attachments
WIP
(29.62 KB, patch)
2020-04-29 19:40 PDT
,
Saam Barati
no flags
Details
Formatted Diff
Diff
WIP
(33.19 KB, patch)
2020-04-29 20:05 PDT
,
Saam Barati
no flags
Details
Formatted Diff
Diff
patch
(69.22 KB, patch)
2020-04-30 16:38 PDT
,
Saam Barati
ysuzuki
: review+
Details
Formatted Diff
Diff
patch for landing
(71.74 KB, patch)
2020-04-30 23:30 PDT
,
Saam Barati
no flags
Details
Formatted Diff
Diff
patch for landing
(71.95 KB, patch)
2020-04-30 23:46 PDT
,
Saam Barati
no flags
Details
Formatted Diff
Diff
Show Obsolete
(4)
View All
Add attachment
proposed patch, testcase, etc.
Radar WebKit Bug Importer
Comment 1
2020-04-29 17:20:41 PDT
<
rdar://problem/62625562
>
Saam Barati
Comment 2
2020-04-29 19:40:40 PDT
Created
attachment 398023
[details]
WIP
Saam Barati
Comment 3
2020-04-29 20:05:08 PDT
Created
attachment 398027
[details]
WIP This is pretty annoying, but I think I'm close to done. This is also making us more spec compliant. We'll also need to start throwing errors when producing length > maxSafeInteger.
Alexey Shvayka
Comment 4
2020-04-30 15:23:50 PDT
***
Bug 163417
has been marked as a duplicate of this bug. ***
Saam Barati
Comment 5
2020-04-30 16:38:22 PDT
Created
attachment 398122
[details]
patch
Yusuke Suzuki
Comment 6
2020-04-30 16:47:18 PDT
Comment on
attachment 398122
[details]
patch View in context:
https://bugs.webkit.org/attachment.cgi?id=398122&action=review
r=me. Please ensure that performance is neutral.
> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:930 > + if (LIKELY(length + n <= std::numeric_limits<uint32_t>::max()))
Since maximum-array-index is not UINT32_MAX (MAX_ARRAY_INDEX is UINT32_MAX - 1), should we check `length + n <= MAX_ARRAY_INDEX` instead?
> Source/JavaScriptCore/runtime/JSObject.cpp:1963 > + if (LIKELY(propertyName <= std::numeric_limits<unsigned>::max()))
Ditto.
> Source/JavaScriptCore/runtime/JSObject.h:214 > + if (LIKELY(propertyName <= std::numeric_limits<uint32_t>::max()))
Ditto.
> Source/JavaScriptCore/runtime/JSObject.h:262 > + if (LIKELY(propertyName <= std::numeric_limits<uint32_t>::max()))
Ditto.
> Source/JavaScriptCore/runtime/JSObject.h:312 > + if (LIKELY(i <= std::numeric_limits<uint32_t>::max()))
Ditto.
> Source/JavaScriptCore/runtime/JSObject.h:380 > + if (LIKELY(i <= std::numeric_limits<uint32_t>::max()))
Ditto.
> Source/JavaScriptCore/runtime/JSObjectInlines.h:154 > + if (LIKELY(propertyName <= std::numeric_limits<uint32_t>::max()))
Ditto.
> Source/JavaScriptCore/runtime/JSObjectInlines.h:558 > + if (LIKELY(propertyName <= std::numeric_limits<uint32_t>::max()))
Ditto.
> Source/JavaScriptCore/runtime/JSObjectInlines.h:566 > + if (LIKELY(propertyName <= std::numeric_limits<uint32_t>::max()))
Ditto.
Alexey Shvayka
Comment 7
2020-04-30 16:51:14 PDT
(In reply to Saam Barati from
comment #5
)
> Created
attachment 398122
[details]
> patch
I wonder if `i > MAX_ARRAY_INDEX` check in JSObject::deletePropertyByIndex() is useful , considering `unsigned i`? Would you please unskip test/built-ins/Array/* files in JSTests/test/config.yaml? They should be passing (and fast) with the patch.
Saam Barati
Comment 8
2020-04-30 16:55:42 PDT
Comment on
attachment 398122
[details]
patch View in context:
https://bugs.webkit.org/attachment.cgi?id=398122&action=review
>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:930 >> + if (LIKELY(length + n <= std::numeric_limits<uint32_t>::max())) > > Since maximum-array-index is not UINT32_MAX (MAX_ARRAY_INDEX is UINT32_MAX - 1), should we check `length + n <= MAX_ARRAY_INDEX` instead?
Yeah I think you’re right. Maybe I should add an isIndex() specialized on uint64_t parameter
Mark Lam
Comment 9
2020-04-30 17:09:19 PDT
Comment on
attachment 398122
[details]
patch View in context:
https://bugs.webkit.org/attachment.cgi?id=398122&action=review
> Source/JavaScriptCore/ChangeLog:9 > + toLength, according to the spec, returns an 53-bit integer. In our
/an/a/
> Source/JavaScriptCore/ChangeLog:17 > + - Casting to unsigned from double is undefined behavior when the integer
/integer/integer is/
> Source/JavaScriptCore/ChangeLog:25 > + This patch addresses each bad use the undefined behavior, and by doing so,
/bad use the/bad use of the/
> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:385 > + if (count + length > std::numeric_limits<uint32_t>::max()) {
Do we have a guarantee that count + length can never overflow? I think in practice, this will never be, but can you add a debug ASSERT here just in case? Either that or use CheckedInt32 and be sure.
> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:926 > + if (UNLIKELY(length + argCount > static_cast<uint64_t>(maxSafeInteger())))
Do we need an overflow check for length + argCount here? Or do we have a guarantee that both length and argCount are less than UINT_MAX? If so, can you add debug ASSERT on those?
> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:1257 > + if (UNLIKELY(length + nrArgs > static_cast<uint64_t>(maxSafeInteger())))
Ditto: overflow check?
Alexey Shvayka
Comment 10
2020-04-30 17:19:56 PDT
Comment on
attachment 398122
[details]
patch View in context:
https://bugs.webkit.org/attachment.cgi?id=398122&action=review
If we want to support all edge cases of the spec, we should vet all OOM throws.
> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:1112 > + throwOutOfMemoryError(globalObject, scope);
We should throw a RangeError here.
> Source/JavaScriptCore/runtime/JSONObject.cpp:482 > + throwOutOfMemoryError(globalObject, scope);
If there are indexed getters between 0 and `length`, we should invoke them.
> Source/JavaScriptCore/runtime/JSONObject.cpp:657 > + throwOutOfMemoryError(m_globalObject, scope);
Ditto.
Saam Barati
Comment 11
2020-04-30 19:41:57 PDT
Comment on
attachment 398122
[details]
patch View in context:
https://bugs.webkit.org/attachment.cgi?id=398122&action=review
>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:385 >> + if (count + length > std::numeric_limits<uint32_t>::max()) { > > Do we have a guarantee that count + length can never overflow? I think in practice, this will never be, but can you add a debug ASSERT here just in case? Either that or use CheckedInt32 and be sure.
That's correct. Because these are expected to be 53-bit integers. I can assert in the prologue of this function.
>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:926 >> + if (UNLIKELY(length + argCount > static_cast<uint64_t>(maxSafeInteger()))) > > Do we need an overflow check for length + argCount here? Or do we have a guarantee that both length and argCount are less than UINT_MAX? If so, can you add debug ASSERT on those?
same reason. This is a 53-bit int since it came from toLength.
>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:1112 >> + throwOutOfMemoryError(globalObject, scope); > > We should throw a RangeError here.
Why? The spec essentially allows for engines to throw OOM whenever it feels like it. Why is RangeError better?
>> Source/JavaScriptCore/runtime/JSONObject.cpp:482 >> + throwOutOfMemoryError(globalObject, scope); > > If there are indexed getters between 0 and `length`, we should invoke them.
Same comment. My understand is spec allows us to OOM whenever we feel like it. This feels like a reasonable thing to do here.
>> Source/JavaScriptCore/runtime/JSONObject.cpp:657 >> + throwOutOfMemoryError(m_globalObject, scope); > > Ditto.
ditto
Saam Barati
Comment 12
2020-04-30 19:47:04 PDT
(In reply to Alexey Shvayka from
comment #7
)
> (In reply to Saam Barati from
comment #5
) > > Created
attachment 398122
[details]
> > patch > > I wonder if `i > MAX_ARRAY_INDEX` check in JSObject::deletePropertyByIndex() > is useful , considering `unsigned i`? > > Would you please unskip test/built-ins/Array/* files in > JSTests/test/config.yaml? They should be passing (and fast) with the patch.
What tests do you mean here?
Saam Barati
Comment 13
2020-04-30 23:30:49 PDT
Created
attachment 398164
[details]
patch for landing
Saam Barati
Comment 14
2020-04-30 23:42:39 PDT
A quick smoke test shows this is neutral on JS2 on the cli
Saam Barati
Comment 15
2020-04-30 23:46:47 PDT
Created
attachment 398165
[details]
patch for landing
EWS
Comment 16
2020-05-01 01:45:24 PDT
Committed
r260990
: <
https://trac.webkit.org/changeset/260990
> All reviewed patches have been landed. Closing bug and clearing flags on
attachment 398165
[details]
.
Saam Barati
Comment 17
2020-05-01 11:27:32 PDT
watchOS build fixes in:
https://trac.webkit.org/changeset/261005/webkit
https://trac.webkit.org/changeset/261006/webkit
Alexey Shvayka
Comment 18
2020-08-30 13:29:11 PDT
***
Bug 164456
has been marked as a duplicate of this bug. ***
Alexey Shvayka
Comment 19
2020-08-30 13:35:15 PDT
***
Bug 177326
has been marked as a duplicate of this bug. ***
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug