[vm, corelib] Fix bounds checks when accessing short ByteData buffers

TEST=tests/corelib/regress_57091_test.dart
Fixes https://github.com/dart-lang/sdk/issues/57091

Change-Id: Iac828f49d5f4756f1aa228992ce054380bbd8422
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395421
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Alexander Markov
2024-11-15 16:49:56 +00:00
committed by Commit Queue
parent eca32d78e4
commit e673478afd
2 changed files with 105 additions and 20 deletions
+62 -20
View File
@@ -5157,7 +5157,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
int getInt16(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 1);
// Check bounds for range [byteOffset, byteOffset + 1].
_byteDataByteOffsetCheck(this, byteOffset + 1, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
var result = _typedData._getInt16(offsetInBytes + byteOffset);
if (identical(endian, Endian.host)) {
return result;
@@ -5167,7 +5169,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setInt16(int byteOffset, int value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 1);
// Check bounds for range [byteOffset, byteOffset + 1].
_byteDataByteOffsetCheck(this, byteOffset + 1, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
_typedData._setInt16(
offsetInBytes + byteOffset,
identical(endian, Endian.host) ? value : _byteSwap16(value),
@@ -5176,7 +5180,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
int getUint16(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 1);
// Check bounds for range [byteOffset, byteOffset + 1].
_byteDataByteOffsetCheck(this, byteOffset + 1, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
var result = _typedData._getUint16(offsetInBytes + byteOffset);
if (identical(endian, Endian.host)) {
return result;
@@ -5186,7 +5192,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setUint16(int byteOffset, int value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 1);
// Check bounds for range [byteOffset, byteOffset + 1].
_byteDataByteOffsetCheck(this, byteOffset + 1, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
_typedData._setUint16(
offsetInBytes + byteOffset,
identical(endian, Endian.host) ? value : _byteSwap16(value),
@@ -5195,7 +5203,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
int getInt32(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 3);
// Check bounds for range [byteOffset, byteOffset + 3].
_byteDataByteOffsetCheck(this, byteOffset + 3, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
var result = _typedData._getInt32(offsetInBytes + byteOffset);
if (identical(endian, Endian.host)) {
return result;
@@ -5205,7 +5215,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setInt32(int byteOffset, int value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 3);
// Check bounds for range [byteOffset, byteOffset + 3].
_byteDataByteOffsetCheck(this, byteOffset + 3, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
_typedData._setInt32(
offsetInBytes + byteOffset,
identical(endian, Endian.host) ? value : _byteSwap32(value),
@@ -5214,7 +5226,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
int getUint32(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 3);
// Check bounds for range [byteOffset, byteOffset + 3].
_byteDataByteOffsetCheck(this, byteOffset + 3, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
var result = _typedData._getUint32(offsetInBytes + byteOffset);
if (identical(endian, Endian.host)) {
return result;
@@ -5224,7 +5238,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setUint32(int byteOffset, int value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 3);
// Check bounds for range [byteOffset, byteOffset + 3].
_byteDataByteOffsetCheck(this, byteOffset + 3, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
_typedData._setUint32(
offsetInBytes + byteOffset,
identical(endian, Endian.host) ? value : _byteSwap32(value),
@@ -5233,7 +5249,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
int getInt64(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 7);
// Check bounds for range [byteOffset, byteOffset + 7].
_byteDataByteOffsetCheck(this, byteOffset + 7, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
var result = _typedData._getInt64(offsetInBytes + byteOffset);
if (identical(endian, Endian.host)) {
return result;
@@ -5243,7 +5261,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setInt64(int byteOffset, int value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 7);
// Check bounds for range [byteOffset, byteOffset + 7].
_byteDataByteOffsetCheck(this, byteOffset + 7, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
_typedData._setInt64(
offsetInBytes + byteOffset,
identical(endian, Endian.host) ? value : _byteSwap64(value),
@@ -5252,7 +5272,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
int getUint64(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 7);
// Check bounds for range [byteOffset, byteOffset + 7].
_byteDataByteOffsetCheck(this, byteOffset + 7, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
var result = _typedData._getUint64(offsetInBytes + byteOffset);
if (identical(endian, Endian.host)) {
return result;
@@ -5262,7 +5284,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setUint64(int byteOffset, int value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 7);
// Check bounds for range [byteOffset, byteOffset + 7].
_byteDataByteOffsetCheck(this, byteOffset + 7, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
_typedData._setUint64(
offsetInBytes + byteOffset,
identical(endian, Endian.host) ? value : _byteSwap64(value),
@@ -5271,7 +5295,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
double getFloat32(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 3);
// Check bounds for range [byteOffset, byteOffset + 3].
_byteDataByteOffsetCheck(this, byteOffset + 3, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
if (identical(endian, Endian.host)) {
return _typedData._getFloat32(offsetInBytes + byteOffset);
}
@@ -5283,7 +5309,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setFloat32(int byteOffset, double value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 3);
// Check bounds for range [byteOffset, byteOffset + 3].
_byteDataByteOffsetCheck(this, byteOffset + 3, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
if (identical(endian, Endian.host)) {
_typedData._setFloat32(offsetInBytes + byteOffset, value);
return;
@@ -5294,7 +5322,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
double getFloat64(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 7);
// Check bounds for range [byteOffset, byteOffset + 7].
_byteDataByteOffsetCheck(this, byteOffset + 7, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
if (identical(endian, Endian.host)) {
return _typedData._getFloat64(offsetInBytes + byteOffset);
}
@@ -5306,7 +5336,9 @@ final class _ByteDataView implements ByteData {
@pragma("vm:prefer-inline")
void setFloat64(int byteOffset, double value, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 7);
// Check bounds for range [byteOffset, byteOffset + 7].
_byteDataByteOffsetCheck(this, byteOffset + 7, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
if (identical(endian, Endian.host)) {
_typedData._setFloat64(offsetInBytes + byteOffset, value);
return;
@@ -5316,7 +5348,9 @@ final class _ByteDataView implements ByteData {
}
Float32x4 getFloat32x4(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 15);
// Check bounds for range [byteOffset, byteOffset + 15].
_byteDataByteOffsetCheck(this, byteOffset + 15, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
// TODO(johnmccutchan) : Need to resolve this for endianity.
return _typedData._getFloat32x4(offsetInBytes + byteOffset);
}
@@ -5326,13 +5360,17 @@ final class _ByteDataView implements ByteData {
Float32x4 value, [
Endian endian = Endian.big,
]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 15);
// Check bounds for range [byteOffset, byteOffset + 15].
_byteDataByteOffsetCheck(this, byteOffset + 15, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
// TODO(johnmccutchan) : Need to resolve this for endianity.
_typedData._setFloat32x4(offsetInBytes + byteOffset, value);
}
Float64x2 getFloat64x2(int byteOffset, [Endian endian = Endian.big]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 15);
// Check bounds for range [byteOffset, byteOffset + 15].
_byteDataByteOffsetCheck(this, byteOffset + 15, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
// TODO(johnmccutchan) : Need to resolve this for endianity.
return _typedData._getFloat64x2(offsetInBytes + byteOffset);
}
@@ -5342,7 +5380,9 @@ final class _ByteDataView implements ByteData {
Float64x2 value, [
Endian endian = Endian.big,
]) {
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length - 15);
// Check bounds for range [byteOffset, byteOffset + 15].
_byteDataByteOffsetCheck(this, byteOffset + 15, length);
byteOffset = _byteDataByteOffsetCheck(this, byteOffset, length);
// TODO(johnmccutchan) : Need to resolve this for endianity.
_typedData._setFloat64x2(offsetInBytes + byteOffset, value);
}
@@ -5402,6 +5442,7 @@ void _throwIfNull(val, String name) {
}
}
// Length should be non-negative.
@pragma("vm:recognized", "other")
@pragma("vm:prefer-inline")
int _typedDataIndexCheck(Object indexable, int index, int length) {
@@ -5416,6 +5457,7 @@ int _typedDataIndexCheck(Object indexable, int index, int length) {
return index;
}
// Length should be non-negative.
@pragma("vm:recognized", "other")
@pragma("vm:prefer-inline")
int _byteDataByteOffsetCheck(ByteData indexable, int byteOffset, int length) {
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Regression test for https://github.com/dart-lang/sdk/issues/57091.
// Verify that bounds checks correctly happen when accessing short ByteData.
import 'dart:typed_data';
import "package:expect/expect.dart";
void testGet(ByteData data) {
Expect.throws(() => data.getInt8(0));
Expect.throws(() => data.getUint8(0));
Expect.throws(() => data.getInt16(0));
Expect.throws(() => data.getUint16(0));
Expect.throws(() => data.getInt32(0));
Expect.throws(() => data.getUint32(0));
Expect.throws(() => data.getInt64(0));
Expect.throws(() => data.getUint64(0));
Expect.throws(() => data.getFloat32(0));
Expect.throws(() => data.getFloat64(0));
}
void testSet(ByteData data) {
Expect.throws(() => data.setInt8(0, 0));
Expect.throws(() => data.setUint8(0, 0));
Expect.throws(() => data.setInt16(0, 0));
Expect.throws(() => data.setUint16(0, 0));
Expect.throws(() => data.setInt32(0, 0));
Expect.throws(() => data.setUint32(0, 0));
Expect.throws(() => data.setInt64(0, 0));
Expect.throws(() => data.setUint64(0, 0));
Expect.throws(() => data.setFloat32(0, 0));
Expect.throws(() => data.setFloat64(0, 0));
}
void main() {
List<int> output = [];
ByteData bytes = Uint8List.fromList(output).buffer.asByteData();
testGet(bytes);
testSet(bytes);
testGet(bytes.asUnmodifiableView());
}