summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Sheludko <ishell@chromium.org>2019-11-13 20:37:22 +0100
committerMichael Brüning <michael.bruning@qt.io>2020-03-10 15:48:04 +0000
commit7622e2b8071fdf5eb01f9494690e860e3f87bce2 (patch)
tree8d86a882ee2e7416e1189c2e1d287b9e6687b8e8
parent6b0d12aa31ae3553db04277d46ce14f57a6e20b3 (diff)
downloadqtwebengine-chromium-7622e2b8071fdf5eb01f9494690e860e3f87bce2.tar.gz
[Backport] CVE-2020-6395 - Out of bounds read in JavaScript
Manual backport of patch originally reviewed on https://chromium-review.googlesource.com/c/v8/v8/+/1910939: [builtins] Ensure constructor has a prototype slot Drive-by-cleanup: simplify related helper functions in CSA. Bug: chromium:1022855 Change-Id: Iea0e090e319365d11cdd16603d67d402968b851a Reviewed-by: Jüri Valdmann <juri.valdmann@qt.io>
-rw-r--r--chromium/v8/src/builtins/base.tq16
-rw-r--r--chromium/v8/src/builtins/builtins-constructor-gen.cc11
-rw-r--r--chromium/v8/src/codegen/code-stub-assembler.cc58
-rw-r--r--chromium/v8/src/codegen/code-stub-assembler.h28
-rw-r--r--chromium/v8/src/diagnostics/objects-debug.cc3
5 files changed, 65 insertions, 51 deletions
diff --git a/chromium/v8/src/builtins/base.tq b/chromium/v8/src/builtins/base.tq
index 07af1f441f8..24c355d6b3e 100644
--- a/chromium/v8/src/builtins/base.tq
+++ b/chromium/v8/src/builtins/base.tq
@@ -336,15 +336,13 @@ macro NewJSObject(implicit context: Context)(): JSObject {
};
}
-extern macro HasPrototypeSlot(JSFunction): bool;
+type JSFunctionWithPrototypeSlot extends JSFunction;
macro GetDerivedMap(implicit context: Context)(
target: JSFunction, newTarget: JSReceiver): Map {
try {
- const constructor = Cast<JSFunction>(newTarget) otherwise SlowPath;
- if (!HasPrototypeSlot(constructor)) {
- goto SlowPath;
- }
+ const constructor =
+ Cast<JSFunctionWithPrototypeSlot>(newTarget) otherwise SlowPath;
assert(IsConstructor(constructor));
const map =
Cast<Map>(constructor.prototype_or_initial_map) otherwise SlowPath;
@@ -1814,6 +1812,9 @@ extern macro HeapObjectToString(HeapObject): String
labels CastError;
extern macro HeapObjectToConstructor(HeapObject): Constructor
labels CastError;
+extern macro HeapObjectToJSFunctionWithPrototypeSlot(HeapObject):
+ JSFunctionWithPrototypeSlot
+ labels CastError;
extern macro HeapObjectToHeapNumber(HeapObject): HeapNumber
labels CastError;
extern macro HeapObjectToSloppyArgumentsElements(HeapObject):
@@ -1967,6 +1968,11 @@ Cast<Constructor>(o: HeapObject): Constructor
return HeapObjectToConstructor(o) otherwise CastError;
}
+Cast<JSFunctionWithPrototypeSlot>(o: HeapObject): JSFunctionWithPrototypeSlot
+ labels CastError {
+ return HeapObjectToJSFunctionWithPrototypeSlot(o) otherwise CastError;
+}
+
Cast<HeapNumber>(o: HeapObject): HeapNumber
labels CastError {
if (IsHeapNumber(o)) return %RawDownCast<HeapNumber>(o);
diff --git a/chromium/v8/src/builtins/builtins-constructor-gen.cc b/chromium/v8/src/builtins/builtins-constructor-gen.cc
index 767e626432e..56dc23e233e 100644
--- a/chromium/v8/src/builtins/builtins-constructor-gen.cc
+++ b/chromium/v8/src/builtins/builtins-constructor-gen.cc
@@ -182,15 +182,14 @@ compiler::TNode<JSObject> ConstructorBuiltinsAssembler::EmitFastNewObject(
SloppyTNode<Context> context, SloppyTNode<JSFunction> target,
SloppyTNode<JSReceiver> new_target, Label* call_runtime) {
// Verify that the new target is a JSFunction.
- Label fast(this), end(this);
- GotoIf(HasInstanceType(new_target, JS_FUNCTION_TYPE), &fast);
- Goto(call_runtime);
-
- BIND(&fast);
+ Label end(this);
+ TNode<JSFunction> new_target_func =
+ HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
+ // Fast path.
// Load the initial map and verify that it's in fact a map.
Node* initial_map =
- LoadObjectField(new_target, JSFunction::kPrototypeOrInitialMapOffset);
+ LoadJSFunctionPrototypeOrInitialMap(new_target_func);
GotoIf(TaggedIsSmi(initial_map), call_runtime);
GotoIf(DoesntHaveInstanceType(initial_map, MAP_TYPE), call_runtime);
diff --git a/chromium/v8/src/codegen/code-stub-assembler.cc b/chromium/v8/src/codegen/code-stub-assembler.cc
index e4f35ddcc88..392221e8725 100644
--- a/chromium/v8/src/codegen/code-stub-assembler.cc
+++ b/chromium/v8/src/codegen/code-stub-assembler.cc
@@ -2608,42 +2608,38 @@ TNode<BoolT> CodeStubAssembler::IsGeneratorFunction(
shared_function_info, SharedFunctionInfo::kFlagsOffset,
MachineType::Uint32()));
- return TNode<BoolT>::UncheckedCast(Word32Or(
- Word32Or(
- Word32Or(
- Word32Equal(function_kind,
- Int32Constant(FunctionKind::kAsyncGeneratorFunction)),
- Word32Equal(
- function_kind,
- Int32Constant(FunctionKind::kAsyncConciseGeneratorMethod))),
- Word32Equal(function_kind,
- Int32Constant(FunctionKind::kGeneratorFunction))),
- Word32Equal(function_kind,
- Int32Constant(FunctionKind::kConciseGeneratorMethod))));
-}
-
-TNode<BoolT> CodeStubAssembler::HasPrototypeSlot(TNode<JSFunction> function) {
- return TNode<BoolT>::UncheckedCast(IsSetWord32<Map::HasPrototypeSlotBit>(
- LoadMapBitField(LoadMap(function))));
-}
-
-TNode<BoolT> CodeStubAssembler::HasPrototypeProperty(TNode<JSFunction> function,
- TNode<Map> map) {
+ // See IsGeneratorFunction(FunctionKind kind).
+ return IsInRange(function_kind, FunctionKind::kAsyncConciseGeneratorMethod,
+ FunctionKind::kConciseGeneratorMethod);
+}
+
+TNode<BoolT> CodeStubAssembler::IsJSFunctionWithPrototypeSlot(
+ TNode<HeapObject> object) {
+ // Only JSFunction maps may have HasPrototypeSlotBit set.
+ return TNode<BoolT>::UncheckedCast(
+ IsSetWord32<Map::HasPrototypeSlotBit>(LoadMapBitField(LoadMap(object))));
+}
+
+void CodeStubAssembler::BranchIfHasPrototypeProperty(
+ TNode<JSFunction> function, TNode<Int32T> function_map_bit_field,
+ Label* if_true, Label* if_false) {
// (has_prototype_slot() && IsConstructor()) ||
// IsGeneratorFunction(shared()->kind())
uint32_t mask =
Map::HasPrototypeSlotBit::kMask | Map::IsConstructorBit::kMask;
- return TNode<BoolT>::UncheckedCast(
- Word32Or(IsAllSetWord32(LoadMapBitField(map), mask),
- IsGeneratorFunction(function)));
+
+ GotoIf(IsAllSetWord32(function_map_bit_field, mask), if_true);
+ Branch(IsGeneratorFunction(function), if_true, if_false);
}
void CodeStubAssembler::GotoIfPrototypeRequiresRuntimeLookup(
TNode<JSFunction> function, TNode<Map> map, Label* runtime) {
// !has_prototype_property() || has_non_instance_prototype()
- GotoIfNot(HasPrototypeProperty(function, map), runtime);
- GotoIf(IsSetWord32<Map::HasNonInstancePrototypeBit>(LoadMapBitField(map)),
- runtime);
+ TNode<Int32T> map_bit_field = LoadMapBitField(map);
+ Label next_check(this);
+ BranchIfHasPrototypeProperty(function, map_bit_field, &next_check, runtime);
+ BIND(&next_check);
+ GotoIf(IsSetWord32<Map::HasNonInstancePrototypeBit>(map_bit_field), runtime);
}
Node* CodeStubAssembler::LoadJSFunctionPrototype(Node* function,
@@ -13532,14 +13528,6 @@ TNode<BoolT> CodeStubAssembler::IsElementsKindLessThanOrEqual(
return Int32LessThanOrEqual(target_kind, Int32Constant(reference_kind));
}
-TNode<BoolT> CodeStubAssembler::IsElementsKindInRange(
- TNode<Int32T> target_kind, ElementsKind lower_reference_kind,
- ElementsKind higher_reference_kind) {
- return Uint32LessThanOrEqual(
- Int32Sub(target_kind, Int32Constant(lower_reference_kind)),
- Int32Constant(higher_reference_kind - lower_reference_kind));
-}
-
Node* CodeStubAssembler::IsDebugActive() {
Node* is_debug_active = Load(
MachineType::Uint8(),
diff --git a/chromium/v8/src/codegen/code-stub-assembler.h b/chromium/v8/src/codegen/code-stub-assembler.h
index 3a5a6233889..0db31fdfb83 100644
--- a/chromium/v8/src/codegen/code-stub-assembler.h
+++ b/chromium/v8/src/codegen/code-stub-assembler.h
@@ -374,6 +374,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
return CAST(heap_object);
}
+ TNode<JSFunction> HeapObjectToJSFunctionWithPrototypeSlot(
+ TNode<HeapObject> heap_object, Label* fail) {
+ GotoIfNot(IsJSFunctionWithPrototypeSlot(heap_object), fail);
+ return CAST(heap_object);
+ }
+
Node* MatchesParameterMode(Node* value, ParameterMode mode);
#define PARAMETER_BINOP(OpName, IntPtrOpName, SmiOpName) \
@@ -727,6 +733,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> WordIsAligned(SloppyTNode<WordT> word, size_t alignment);
TNode<BoolT> WordIsPowerOfTwo(SloppyTNode<IntPtrT> value);
+ // Check if lower_limit <= value <= higher_limit.
+ template <typename U>
+ TNode<BoolT> IsInRange(TNode<Word32T> value, U lower_limit, U higher_limit) {
+ DCHECK_LE(lower_limit, higher_limit);
+ STATIC_ASSERT(sizeof(U) <= kInt32Size);
+ return Uint32LessThanOrEqual(Int32Sub(value, Int32Constant(lower_limit)),
+ Int32Constant(higher_limit - lower_limit));
+ }
+
#if DEBUG
void Bind(Label* label, AssemblerDebugInfo debug_info);
#endif // DEBUG
@@ -1274,9 +1289,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Map> LoadJSArrayElementsMap(SloppyTNode<Int32T> kind,
SloppyTNode<Context> native_context);
- TNode<BoolT> HasPrototypeSlot(TNode<JSFunction> function);
+ TNode<BoolT> IsJSFunctionWithPrototypeSlot(TNode<HeapObject> object);
TNode<BoolT> IsGeneratorFunction(TNode<JSFunction> function);
- TNode<BoolT> HasPrototypeProperty(TNode<JSFunction> function, TNode<Map> map);
+ void BranchIfHasPrototypeProperty(TNode<JSFunction> function,
+ TNode<Int32T> function_map_bit_field,
+ Label* if_true, Label* if_false);
void GotoIfPrototypeRequiresRuntimeLookup(TNode<JSFunction> function,
TNode<Map> map, Label* runtime);
// Load the "prototype" property of a JSFunction.
@@ -2393,11 +2410,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
ElementsKind reference_kind);
TNode<BoolT> IsElementsKindLessThanOrEqual(TNode<Int32T> target_kind,
ElementsKind reference_kind);
- // Check if reference_kind_a <= target_kind <= reference_kind_b
+ // Check if lower_reference_kind <= target_kind <= higher_reference_kind.
TNode<BoolT> IsElementsKindInRange(TNode<Int32T> target_kind,
ElementsKind lower_reference_kind,
- ElementsKind higher_reference_kind);
-
+ ElementsKind higher_reference_kind) {
+ return IsInRange(target_kind, lower_reference_kind, higher_reference_kind);
+ }
// String helpers.
// Load a character from a String (might flatten a ConsString).
TNode<Int32T> StringCharCodeAt(SloppyTNode<String> string,
diff --git a/chromium/v8/src/diagnostics/objects-debug.cc b/chromium/v8/src/diagnostics/objects-debug.cc
index 07634f3ca79..c5d1b25737f 100644
--- a/chromium/v8/src/diagnostics/objects-debug.cc
+++ b/chromium/v8/src/diagnostics/objects-debug.cc
@@ -691,6 +691,9 @@ void Map::MapVerify(Isolate* isolate) {
.IsConsistentWithBackPointers());
SLOW_DCHECK(!FLAG_unbox_double_fields ||
layout_descriptor().IsConsistentWithMap(*this));
+ // Only JSFunction maps have has_prototype_slot() bit set and constructible
+ // JSFunction objects must have prototype slot.
+ CHECK_IMPLIES(has_prototype_slot(), instance_type() == JS_FUNCTION_TYPE);
if (!may_have_interesting_symbols()) {
CHECK(!has_named_interceptor());
CHECK(!is_dictionary_map());