Commit 91f0cd00 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[ubsan] Fix various ClusterFuzz-found issues

Fixing a few float and int overflows.
Drive-by fix: with --experimental-wasm-bigint, Number values
may not be used to initialize i64-typed globals. The existing
code for doing that relied on UB; since it's a spec violation
the fix is to throw instead.

No regression test for 933103 because it will OOM anyway.
No regression test for 932896 because it would be extremely slow.

Bug: chromium:927894, chromium:927996, chromium:930086, chromium:932679, chromium:932896, chromium:933103, chromium:933134
Change-Id: Iae1c1ff1038af4512a52d3e56b8c4b75f2233314
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1495911
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60075}
parent ad49f129
......@@ -10939,7 +10939,11 @@ int64_t Isolate::AdjustAmountOfExternalAllocatedMemory(
reinterpret_cast<int64_t*>(reinterpret_cast<uint8_t*>(this) +
I::kExternalMemoryAtLastMarkCompactOffset);
const int64_t amount = *external_memory + change_in_bytes;
// Embedders are weird: we see both over- and underflows here. Perform the
// addition with unsigned types to avoid undefined behavior.
const int64_t amount =
static_cast<int64_t>(static_cast<uint64_t>(change_in_bytes) +
static_cast<uint64_t>(*external_memory));
*external_memory = amount;
int64_t allocation_diff_since_last_mc =
......
......@@ -448,7 +448,12 @@ BUILTIN(StringRaw) {
Object::ToLength(isolate, raw_len));
IncrementalStringBuilder result_builder(isolate);
const uint32_t length = static_cast<uint32_t>(raw_len->Number());
// Intentional spec violation: we ignore {length} values >= 2^32, because
// assuming non-empty chunks they would generate too-long strings anyway.
const double raw_len_number = raw_len->Number();
const uint32_t length = raw_len_number > std::numeric_limits<uint32_t>::max()
? std::numeric_limits<uint32_t>::max()
: static_cast<uint32_t>(raw_len_number);
if (length > 0) {
Handle<Object> first_element;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, first_element,
......
......@@ -27,21 +27,18 @@ BUILTIN(TypedArrayPrototypeBuffer) {
namespace {
int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
int64_t relative;
if (V8_LIKELY(num->IsSmi())) {
relative = Smi::ToInt(*num);
int64_t relative = Smi::ToInt(*num);
return relative < 0 ? std::max<int64_t>(relative + maximum, minimum)
: std::min<int64_t>(relative, maximum);
} else {
DCHECK(num->IsHeapNumber());
double fp = HeapNumber::cast(*num)->value();
if (V8_UNLIKELY(!std::isfinite(fp))) {
// +Infinity / -Infinity
DCHECK(!std::isnan(fp));
return fp < 0 ? minimum : maximum;
}
relative = static_cast<int64_t>(fp);
double relative = HeapNumber::cast(*num)->value();
DCHECK(!std::isnan(relative));
return static_cast<int64_t>(
relative < 0 ? std::max<double>(relative + maximum, minimum)
: std::min<double>(relative, maximum));
}
return relative < 0 ? std::max<int64_t>(relative + maximum, minimum)
: std::min<int64_t>(relative, maximum);
}
} // namespace
......
......@@ -977,9 +977,12 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
break;
case IrOpcode::kNumberConstant: {
double const fv = OpParameter<double>(node->op());
int64_t const iv = static_cast<int64_t>(fv);
if (static_cast<double>(iv) == fv) {
return jsgraph()->Int64Constant(iv);
using limits = std::numeric_limits<int64_t>;
if (fv <= limits::max() && fv >= limits::min()) {
int64_t const iv = static_cast<int64_t>(fv);
if (static_cast<double>(iv) == fv) {
return jsgraph()->Int64Constant(iv);
}
}
break;
}
......
......@@ -1875,7 +1875,7 @@ MaybeHandle<FreshlyAllocatedBigInt> BigInt::AllocateFor(
bits_min = (bits_min + roundup) >> kBitsPerCharTableShift;
if (bits_min <= static_cast<size_t>(kMaxInt)) {
// Divide by kDigitsBits, rounding up.
int length = (static_cast<int>(bits_min) + kDigitBits - 1) / kDigitBits;
int length = static_cast<int>((bits_min + kDigitBits - 1) / kDigitBits);
if (length <= kMaxLength) {
Handle<MutableBigInt> result =
MutableBigInt::New(isolate, length, pretenure).ToHandleChecked();
......
......@@ -738,6 +738,9 @@ inline uint64_t FixedTypedArray<BigUint64ArrayTraits>::from(double value) {
template <>
inline float FixedTypedArray<Float32ArrayTraits>::from(double value) {
using limits = std::numeric_limits<float>;
if (value > limits::max()) return limits::infinity();
if (value < limits::lowest()) return -limits::infinity();
return static_cast<float>(value);
}
......
......@@ -3,7 +3,9 @@
// found in the LICENSE file.
#include "src/wasm/module-instantiate.h"
#include "src/asmjs/asm-js.h"
#include "src/conversions-inl.h"
#include "src/heap/heap-inl.h" // For CodeSpaceMemoryModificationScope.
#include "src/property-descriptor.h"
#include "src/utils.h"
......@@ -132,6 +134,7 @@ class InstanceBuilder {
void LoadDataSegments(Handle<WasmInstanceObject> instance);
void WriteGlobalValue(const WasmGlobal& global, double value);
void WriteGlobalValue(const WasmGlobal& global, int64_t num);
void WriteGlobalValue(const WasmGlobal& global,
Handle<WasmGlobalObject> value);
......@@ -647,25 +650,34 @@ void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
switch (global.type) {
case kWasmI32:
WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
static_cast<int32_t>(num));
DoubleToInt32(num));
break;
case kWasmI64:
WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
static_cast<int64_t>(num));
// The Wasm-BigInt proposal currently says that i64 globals may
// only be initialized with BigInts. See:
// https://github.com/WebAssembly/JS-BigInt-integration/issues/12
UNREACHABLE();
break;
case kWasmF32:
WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
static_cast<float>(num));
DoubleToFloat32(num));
break;
case kWasmF64:
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
static_cast<double>(num));
WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
break;
default:
UNREACHABLE();
}
}
void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, int64_t num) {
TRACE("init [globals_start=%p + %u] = %" PRId64 ", type = %s\n",
reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
global.offset, num, ValueTypes::TypeName(global.type));
DCHECK_EQ(kWasmI64, global.type);
WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
}
void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
Handle<WasmGlobalObject> value) {
TRACE("init [globals_start=%p + %u] = ",
......@@ -1045,7 +1057,7 @@ bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
return true;
}
if (value->IsNumber()) {
if (value->IsNumber() && global.type != kWasmI64) {
WriteGlobalValue(global, value->Number());
return true;
}
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
load('test/mjsunit/wasm/wasm-module-builder.js');
// crbug.com/933134
(function() {
var builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", "i32", kWasmI32);
builder.addImportedGlobal("mod", "f32", kWasmF32);
var module = new WebAssembly.Module(builder.toBuffer());
return new WebAssembly.Instance(module, {
mod: {
i32: 1e12,
f32: 1e300,
}
});
})();
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// crbug.com/923466
__v_5 = [ -1073741825, -2147483648];
__v_5.sort();
......@@ -17,3 +19,38 @@ new Date(2148022800000).toString();
// crbug.com/927212
assertThrows(() => (2n).toString(-2147483657), RangeError);
// crbug.com/927894
var typed_array = new Uint8Array(16);
typed_array.fill(0, -1.7976931348623157e+308);
// crbug.com/927996
var float_array = new Float32Array(1);
float_array[0] = 1e51;
// crbug.com/930086
(function() {
try {
// Build up a 536870910-character string (just under 2**30).
var string = "ff";
var long_string = "0x";
for (var i = 2; i < 29; i++) {
string = string + string;
long_string += string;
}
assertThrows(() => BigInt(long_string), SyntaxError);
} catch (e) {
/* 32-bit architectures have a lower string length limit. */
}
})();
// crbug.com/932679
(function() {
const buffer = new DataView(new ArrayBuffer(2));
function __f_14159(buffer) {
try { return buffer.getUint16(Infinity, true); } catch(e) { return 0; }
}
__f_14159(buffer);
%OptimizeFunctionOnNextCall(__f_14159);
__f_14159(buffer);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment