forked from mirrors/gecko-dev
		
	Bug 1851568 - Improve validation of tail call result types. r=bvisness
Differential Revision: https://phabricator.services.mozilla.com/D187501
This commit is contained in:
		
							parent
							
								
									e3cea5c34e
								
							
						
					
					
						commit
						fd29b97c09
					
				
					 9 changed files with 99 additions and 57 deletions
				
			
		
							
								
								
									
										20
									
								
								js/src/jit-test/tests/wasm/tail-calls/bug1851568.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								js/src/jit-test/tests/wasm/tail-calls/bug1851568.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| wasmFailValidateText(`(module
 | ||||
|     (func (result i32 f64) | ||||
|       i32.const 1 | ||||
|       f64.const 2.0 | ||||
|     ) | ||||
|     (func (export "f") (result f64) | ||||
|       return_call 0 | ||||
|     ) | ||||
| )`, /type mismatch/);
 | ||||
| 
 | ||||
| wasmFailValidateText(`(module
 | ||||
|     (func (result i32 f64) | ||||
|       i32.const 1 | ||||
|       f64.const 2.0 | ||||
|     ) | ||||
|     (func (export "f") (result f32 i32 f64) | ||||
|       f32.const 3.14 | ||||
|       return_call 0 | ||||
|     ) | ||||
| )`, /type mismatch/);
 | ||||
|  | @ -168,3 +168,53 @@ let fns = i.exports; | |||
| 
 | ||||
| assertEq(fns.churn(800), -575895114); | ||||
| assertEq(fns.churn(1200), -1164697516); | ||||
| 
 | ||||
| wasmValidateText(`(module
 | ||||
|   (rec | ||||
|     (type $s1 (sub (struct i32))) | ||||
|     (type $s2 (sub $s1 (struct i32 f32))) | ||||
|   ) | ||||
|   (func (result (ref $s2)) | ||||
|     struct.new_default $s2 | ||||
|   ) | ||||
|   (func (export "f") (result (ref $s1)) | ||||
|     return_call 0 | ||||
|   ) | ||||
| )`);
 | ||||
| 
 | ||||
| wasmFailValidateText(`(module
 | ||||
|   (rec | ||||
|     (type $s1 (sub (struct i32))) | ||||
|     (type $s2 (sub $s1 (struct i32 f32))) | ||||
|   ) | ||||
|   (func (result (ref $s1)) | ||||
|     struct.new_default $s1 | ||||
|   ) | ||||
|   (func (export "f") (result (ref $s2)) | ||||
|     return_call 0 | ||||
|   ) | ||||
| )`, /type mismatch/);
 | ||||
| 
 | ||||
| wasmValidateText(`(module
 | ||||
|   (rec | ||||
|     (type $s1 (sub (struct i32))) | ||||
|     (type $s2 (sub $s1 (struct i32 f32))) | ||||
|   ) | ||||
|   (type $t (func (result (ref $s2)))) | ||||
|   (func (export "f") (param (ref $t)) (result (ref $s1)) | ||||
|     local.get 0 | ||||
|     return_call_ref $t | ||||
|   ) | ||||
| )`);
 | ||||
| 
 | ||||
| wasmFailValidateText(`(module
 | ||||
|   (rec | ||||
|     (type $s1 (sub (struct i32))) | ||||
|     (type $s2 (sub $s1 (struct i32 f32))) | ||||
|   ) | ||||
|   (type $t (func (result (ref $s1)))) | ||||
|   (func (export "f") (param (ref $t)) (result (ref $s2)) | ||||
|     local.get 0 | ||||
|     return_call_ref $t | ||||
|   ) | ||||
| )`, /type mismatch/);
 | ||||
|  |  | |||
|  | @ -143,7 +143,7 @@ wasmFailValidateText( | |||
|        (table 0 anyfunc) | ||||
|        (func $type-void-vs-num (result i32) | ||||
|          (i32.eqz (return_call_indirect (type 0) (i32.const 0)))))`,
 | ||||
|     /popping value from empty stack/); | ||||
|     /type mismatch: expected 1 values, got 0 values/); | ||||
| 
 | ||||
| wasmFailValidateText( | ||||
|     `(module
 | ||||
|  | @ -151,7 +151,7 @@ wasmFailValidateText( | |||
|        (table 0 anyfunc) | ||||
|        (func $type-num-vs-num | ||||
|          (i32.eqz (return_call_indirect (type 0) (i32.const 0)))))`,
 | ||||
|     /unused values not explicitly dropped/); | ||||
|     /type mismatch: expected 0 values, got 1 values/); | ||||
| 
 | ||||
| wasmFailValidateText( | ||||
|     `(module
 | ||||
|  |  | |||
|  | @ -97,7 +97,7 @@ wasmFailValidateText( | |||
|        (func $type-void-vs-num (result i32) | ||||
|          (return_call 1) (i32.const 0)) | ||||
|        (func))`,
 | ||||
|     /popping value from empty stack/); | ||||
|     /type mismatch: expected 1 values, got 0 values/); | ||||
| 
 | ||||
| wasmFailValidateText( | ||||
|     `(module
 | ||||
|  |  | |||
|  | @ -52,12 +52,13 @@ check_stub1: { | |||
| var ins = wasmEvalText(`(module
 | ||||
|   (import "" "fac-acc" (func $fac-acc (param i64 i64) (result i64))) | ||||
|   (type $ty (func (param i64 i64) (result i64))) | ||||
|   (type $tz (func (param i64) (result i64))) | ||||
|   (table $t 1 1 funcref) | ||||
|   (func $f (export "fac") (param i64) (result i64) | ||||
|     local.get 0 | ||||
|     i64.const 1 | ||||
|     i32.const 0 | ||||
|     return_call_indirect $t | ||||
|     return_call_indirect $t (type $tz) | ||||
|   ) | ||||
|   (elem $t (i32.const 0) $fac-acc) | ||||
| 
 | ||||
|  |  | |||
|  | @ -4817,8 +4817,7 @@ bool BaseCompiler::emitCall() { | |||
| bool BaseCompiler::emitReturnCall() { | ||||
|   uint32_t funcIndex; | ||||
|   BaseNothingVector args_{}; | ||||
|   BaseNothingVector unused_values{}; | ||||
|   if (!iter_.readReturnCall(&funcIndex, &args_, &unused_values)) { | ||||
|   if (!iter_.readReturnCall(&funcIndex, &args_)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | @ -4934,9 +4933,8 @@ bool BaseCompiler::emitReturnCallIndirect() { | |||
|   uint32_t tableIndex; | ||||
|   Nothing callee_; | ||||
|   BaseNothingVector args_{}; | ||||
|   BaseNothingVector unused_values{}; | ||||
|   if (!iter_.readReturnCallIndirect(&funcTypeIndex, &tableIndex, &callee_, | ||||
|                                     &args_, &unused_values)) { | ||||
|                                     &args_)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | @ -5042,9 +5040,7 @@ bool BaseCompiler::emitReturnCallRef() { | |||
|   const FuncType* funcType; | ||||
|   Nothing unused_callee; | ||||
|   BaseNothingVector unused_args{}; | ||||
|   BaseNothingVector unused_values{}; | ||||
|   if (!iter_.readReturnCallRef(&funcType, &unused_callee, &unused_args, | ||||
|                                &unused_values)) { | ||||
|   if (!iter_.readReturnCallRef(&funcType, &unused_callee, &unused_args)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -5101,8 +5101,7 @@ static bool EmitReturnCall(FunctionCompiler& f) { | |||
| 
 | ||||
|   uint32_t funcIndex; | ||||
|   DefVector args; | ||||
|   DefVector unused_values; | ||||
|   if (!f.iter().readReturnCall(&funcIndex, &args, &unused_values)) { | ||||
|   if (!f.iter().readReturnCall(&funcIndex, &args)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | @ -5142,9 +5141,8 @@ static bool EmitReturnCallIndirect(FunctionCompiler& f) { | |||
|   uint32_t tableIndex; | ||||
|   MDefinition* callee; | ||||
|   DefVector args; | ||||
|   DefVector unused_values; | ||||
|   if (!f.iter().readReturnCallIndirect(&funcTypeIndex, &tableIndex, &callee, | ||||
|                                        &args, &unused_values)) { | ||||
|                                        &args)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | @ -5176,9 +5174,8 @@ static bool EmitReturnCallRef(FunctionCompiler& f) { | |||
|   const FuncType* funcType; | ||||
|   MDefinition* callee; | ||||
|   DefVector args; | ||||
|   DefVector unused_values; | ||||
| 
 | ||||
|   if (!f.iter().readReturnCallRef(&funcType, &callee, &args, &unused_values)) { | ||||
|   if (!f.iter().readReturnCallRef(&funcType, &callee, &args)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -690,12 +690,10 @@ class MOZ_STACK_CLASS OpIter : private Policy { | |||
|                                       ValueVector* argValues); | ||||
| #ifdef ENABLE_WASM_TAIL_CALLS | ||||
|   [[nodiscard]] bool readReturnCall(uint32_t* funcTypeIndex, | ||||
|                                     ValueVector* argValues, | ||||
|                                     ValueVector* values); | ||||
|                                     ValueVector* argValues); | ||||
|   [[nodiscard]] bool readReturnCallIndirect(uint32_t* funcTypeIndex, | ||||
|                                             uint32_t* tableIndex, Value* callee, | ||||
|                                             ValueVector* argValues, | ||||
|                                             ValueVector* values); | ||||
|                                             ValueVector* argValues); | ||||
| #endif | ||||
| #ifdef ENABLE_WASM_FUNCTION_REFERENCES | ||||
|   [[nodiscard]] bool readCallRef(const FuncType** funcType, Value* callee, | ||||
|  | @ -703,8 +701,7 @@ class MOZ_STACK_CLASS OpIter : private Policy { | |||
| 
 | ||||
| #  ifdef ENABLE_WASM_TAIL_CALLS | ||||
|   [[nodiscard]] bool readReturnCallRef(const FuncType** funcType, Value* callee, | ||||
|                                        ValueVector* argValues, | ||||
|                                        ValueVector* values); | ||||
|                                        ValueVector* argValues); | ||||
| #  endif | ||||
| #endif | ||||
|   [[nodiscard]] bool readOldCallDirect(uint32_t numFuncImports, | ||||
|  | @ -2462,8 +2459,7 @@ inline bool OpIter<Policy>::readCall(uint32_t* funcTypeIndex, | |||
| #ifdef ENABLE_WASM_TAIL_CALLS | ||||
| template <typename Policy> | ||||
| inline bool OpIter<Policy>::readReturnCall(uint32_t* funcTypeIndex, | ||||
|                                            ValueVector* argValues, | ||||
|                                            ValueVector* values) { | ||||
|                                            ValueVector* argValues) { | ||||
|   MOZ_ASSERT(Classify(op_) == OpKind::ReturnCall); | ||||
| 
 | ||||
|   if (!readVarU32(funcTypeIndex)) { | ||||
|  | @ -2480,15 +2476,11 @@ inline bool OpIter<Policy>::readReturnCall(uint32_t* funcTypeIndex, | |||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (!push(ResultType::Vector(funcType.results()))) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // Check if callee results are subtypes of caller's.
 | ||||
|   Control& body = controlStack_[0]; | ||||
|   MOZ_ASSERT(body.kind() == LabelKind::Body); | ||||
| 
 | ||||
|   // Pop function results as the instruction will cause a return.
 | ||||
|   if (!popWithType(body.resultType(), values)) { | ||||
|   if (!checkIsSubtypeOf(ResultType::Vector(funcType.results()), | ||||
|                         body.resultType())) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | @ -2549,8 +2541,7 @@ template <typename Policy> | |||
| inline bool OpIter<Policy>::readReturnCallIndirect(uint32_t* funcTypeIndex, | ||||
|                                                    uint32_t* tableIndex, | ||||
|                                                    Value* callee, | ||||
|                                                    ValueVector* argValues, | ||||
|                                                    ValueVector* values) { | ||||
|                                                    ValueVector* argValues) { | ||||
|   MOZ_ASSERT(Classify(op_) == OpKind::ReturnCallIndirect); | ||||
|   MOZ_ASSERT(funcTypeIndex != tableIndex); | ||||
| 
 | ||||
|  | @ -2589,15 +2580,11 @@ inline bool OpIter<Policy>::readReturnCallIndirect(uint32_t* funcTypeIndex, | |||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (!push(ResultType::Vector(funcType.results()))) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // Check if callee results are subtypes of caller's.
 | ||||
|   Control& body = controlStack_[0]; | ||||
|   MOZ_ASSERT(body.kind() == LabelKind::Body); | ||||
| 
 | ||||
|   // Pop function results as the instruction will cause a return.
 | ||||
|   if (!popWithType(body.resultType(), values)) { | ||||
|   if (!checkIsSubtypeOf(ResultType::Vector(funcType.results()), | ||||
|                         body.resultType())) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  | @ -2636,8 +2623,7 @@ inline bool OpIter<Policy>::readCallRef(const FuncType** funcType, | |||
| template <typename Policy> | ||||
| inline bool OpIter<Policy>::readReturnCallRef(const FuncType** funcType, | ||||
|                                               Value* callee, | ||||
|                                               ValueVector* argValues, | ||||
|                                               ValueVector* values) { | ||||
|                                               ValueVector* argValues) { | ||||
|   MOZ_ASSERT(Classify(op_) == OpKind::ReturnCallRef); | ||||
| 
 | ||||
|   uint32_t funcTypeIndex; | ||||
|  | @ -2656,15 +2642,11 @@ inline bool OpIter<Policy>::readReturnCallRef(const FuncType** funcType, | |||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (!push(ResultType::Vector((*funcType)->results()))) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // Check if callee results are subtypes of caller's.
 | ||||
|   Control& body = controlStack_[0]; | ||||
|   MOZ_ASSERT(body.kind() == LabelKind::Body); | ||||
| 
 | ||||
|   // Pop function results as the instruction will cause a return.
 | ||||
|   if (!popWithType(body.resultType(), values)) { | ||||
|   if (!checkIsSubtypeOf(ResultType::Vector((*funcType)->results()), | ||||
|                         body.resultType())) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -223,8 +223,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, | |||
|         } | ||||
|         uint32_t unusedIndex; | ||||
|         NothingVector unusedArgs{}; | ||||
|         NothingVector unusedValues{}; | ||||
|         CHECK(iter.readReturnCall(&unusedIndex, &unusedArgs, &unusedValues)); | ||||
|         CHECK(iter.readReturnCall(&unusedIndex, &unusedArgs)); | ||||
|       } | ||||
|       case uint16_t(Op::ReturnCallIndirect): { | ||||
|         if (!env.tailCallsEnabled()) { | ||||
|  | @ -232,9 +231,8 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, | |||
|         } | ||||
|         uint32_t unusedIndex, unusedIndex2; | ||||
|         NothingVector unusedArgs{}; | ||||
|         NothingVector unusedValues{}; | ||||
|         CHECK(iter.readReturnCallIndirect(&unusedIndex, &unusedIndex2, ¬hing, | ||||
|                                           &unusedArgs, &unusedValues)); | ||||
|                                           &unusedArgs)); | ||||
|       } | ||||
| #endif | ||||
| #ifdef ENABLE_WASM_FUNCTION_REFERENCES | ||||
|  | @ -253,9 +251,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env, | |||
|         } | ||||
|         const FuncType* unusedType; | ||||
|         NothingVector unusedArgs{}; | ||||
|         NothingVector unusedValues{}; | ||||
|         CHECK(iter.readReturnCallRef(&unusedType, ¬hing, &unusedArgs, | ||||
|                                      &unusedValues)); | ||||
|         CHECK(iter.readReturnCallRef(&unusedType, ¬hing, &unusedArgs)); | ||||
|       } | ||||
| #  endif | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Yury Delendik
						Yury Delendik