diff options
author | Igor Sheludko <ishell@chromium.org> | 2019-11-13 20:37:22 +0100 |
---|---|---|
committer | Michael Brüning <michael.bruning@qt.io> | 2020-03-10 15:48:04 +0000 |
commit | 7622e2b8071fdf5eb01f9494690e860e3f87bce2 (patch) | |
tree | 8d86a882ee2e7416e1189c2e1d287b9e6687b8e8 /chromium | |
parent | 6b0d12aa31ae3553db04277d46ce14f57a6e20b3 (diff) | |
download | qtwebengine-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>
Diffstat (limited to 'chromium')
-rw-r--r-- | chromium/v8/src/builtins/base.tq | 16 | ||||
-rw-r--r-- | chromium/v8/src/builtins/builtins-constructor-gen.cc | 11 | ||||
-rw-r--r-- | chromium/v8/src/codegen/code-stub-assembler.cc | 58 | ||||
-rw-r--r-- | chromium/v8/src/codegen/code-stub-assembler.h | 28 | ||||
-rw-r--r-- | chromium/v8/src/diagnostics/objects-debug.cc | 3 |
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()); |