Skip to content

Commit acb410e

Browse files
authored
Refactor validation of call/tail-call instructions (#1587)
Share a bit more logic between various pairs of instructions and families of "all tail calls" and "all calls" to ensure nothing is accidentally left out. This doesn't functionally change any validation, just reorganizes it.
1 parent 024fb67 commit acb410e

File tree

1 file changed

+69
-64
lines changed

1 file changed

+69
-64
lines changed

crates/wasmparser/src/validator/operators.rs

Lines changed: 69 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,8 @@ where
796796
}
797797
}
798798

799+
/// Returns the corresponding function type for the `func` item located at
800+
/// `function_index`.
799801
fn type_of_function(&self, function_index: u32) -> Result<&'resources FuncType> {
800802
match self.resources.type_of_function(function_index) {
801803
Some(f) => Ok(f),
@@ -808,18 +810,11 @@ where
808810
}
809811
}
810812

811-
/// Validates a `call` instruction, ensuring that the function index is
812-
/// in-bounds and the right types are on the stack to call the function.
813-
fn check_call(&mut self, function_index: u32) -> Result<()> {
814-
let ty = self.type_of_function(function_index)?;
815-
self.check_call_ty(ty)
816-
}
817-
818-
fn check_call_type_index(&mut self, type_index: u32) -> Result<()> {
819-
let ty = self.func_type_at(type_index)?;
820-
self.check_call_ty(ty)
821-
}
822-
813+
/// Checks a call-style instruction which will be invoking the function `ty`
814+
/// specified.
815+
///
816+
/// This will pop parameters from the operand stack for the function's
817+
/// parameters and then push the results of the function on the stack.
823818
fn check_call_ty(&mut self, ty: &FuncType) -> Result<()> {
824819
for &ty in ty.params().iter().rev() {
825820
debug_assert_type_indices_are_ids(ty);
@@ -832,8 +827,53 @@ where
832827
Ok(())
833828
}
834829

835-
/// Validates a call to an indirect function, very similar to `check_call`.
836-
fn check_call_indirect(&mut self, index: u32, table_index: u32) -> Result<()> {
830+
/// Similar to `check_call_ty` except used for tail-call instructions.
831+
fn check_return_call_ty(&mut self, ty: &FuncType) -> Result<()> {
832+
self.check_func_type_same_results(ty)?;
833+
self.check_call_ty(ty)?;
834+
self.check_return()
835+
}
836+
837+
/// Checks the immediate `type_index` of a `call_ref`-style instruction
838+
/// (also `return_call_ref`).
839+
///
840+
/// This will validate that the value on the stack is a `(ref type_index)`
841+
/// or a subtype. This will then return the corresponding function type used
842+
/// for this call (to be used with `check_call_ty` or
843+
/// `check_return_call_ty`).
844+
fn check_call_ref_ty(&mut self, type_index: u32) -> Result<&'resources FuncType> {
845+
let unpacked_index = UnpackedIndex::Module(type_index);
846+
let mut hty = HeapType::Concrete(unpacked_index);
847+
self.resources.check_heap_type(&mut hty, self.offset)?;
848+
// If `None` is popped then that means a "bottom" type was popped which
849+
// is always considered equivalent to the `hty` tag.
850+
if let Some(rt) = self.pop_ref()? {
851+
let expected = RefType::new(true, hty).expect("hty should be previously validated");
852+
let expected = ValType::Ref(expected);
853+
if !self.resources.is_subtype(ValType::Ref(rt), expected) {
854+
bail!(
855+
self.offset,
856+
"type mismatch: funcref on stack does not match specified type",
857+
);
858+
}
859+
}
860+
self.func_type_at(type_index)
861+
}
862+
863+
/// Validates the immediate operands of a `call_indirect` or
864+
/// `return_call_indirect` instruction.
865+
///
866+
/// This will validate that `table_index` is valid and a funcref table. It
867+
/// will additionally pop the index argument which is used to index into the
868+
/// table.
869+
///
870+
/// The return value of this function is the function type behind
871+
/// `type_index` which must then be passedt o `check_{call,return_call}_ty`.
872+
fn check_call_indirect_ty(
873+
&mut self,
874+
type_index: u32,
875+
table_index: u32,
876+
) -> Result<&'resources FuncType> {
837877
let tab = self.check_table_index(table_index)?;
838878
if !self
839879
.resources
@@ -844,15 +884,8 @@ where
844884
"indirect calls must go through a table with type <= funcref",
845885
);
846886
}
847-
let ty = self.func_type_at(index)?;
848887
self.pop_operand(Some(tab.index_type()))?;
849-
for ty in ty.clone().params().iter().rev() {
850-
self.pop_operand(Some(*ty))?;
851-
}
852-
for ty in ty.results() {
853-
self.push_operand(*ty)?;
854-
}
855-
Ok(())
888+
self.func_type_at(type_index)
856889
}
857890

858891
/// Validates a `return` instruction, popping types from the operand
@@ -868,20 +901,6 @@ where
868901
Ok(())
869902
}
870903

871-
/// Check that the function at the given index has the same result types as
872-
/// the current function's results.
873-
fn check_func_same_results(&self, function_index: u32) -> Result<()> {
874-
let ty = self.type_of_function(function_index)?;
875-
self.check_func_type_same_results(ty)
876-
}
877-
878-
/// Check that the type at the given index has the same result types as the
879-
/// current function's results.
880-
fn check_func_type_index_same_results(&self, type_index: u32) -> Result<()> {
881-
let ty = self.func_type_at(type_index)?;
882-
self.check_func_type_same_results(ty)
883-
}
884-
885904
/// Check that the given type has the same result types as the current
886905
/// function's results.
887906
fn check_func_type_same_results(&self, callee_ty: &FuncType) -> Result<()> {
@@ -1551,47 +1570,33 @@ where
15511570
Ok(())
15521571
}
15531572
fn visit_call(&mut self, function_index: u32) -> Self::Output {
1554-
self.check_call(function_index)?;
1573+
let ty = self.type_of_function(function_index)?;
1574+
self.check_call_ty(ty)?;
15551575
Ok(())
15561576
}
15571577
fn visit_return_call(&mut self, function_index: u32) -> Self::Output {
1558-
self.check_call(function_index)?;
1559-
self.check_return()?;
1560-
self.check_func_same_results(function_index)?;
1578+
let ty = self.type_of_function(function_index)?;
1579+
self.check_return_call_ty(ty)?;
15611580
Ok(())
15621581
}
15631582
fn visit_call_ref(&mut self, type_index: u32) -> Self::Output {
1564-
let unpacked_index = UnpackedIndex::Module(type_index);
1565-
let mut hty = HeapType::Concrete(unpacked_index);
1566-
self.resources.check_heap_type(&mut hty, self.offset)?;
1567-
// If `None` is popped then that means a "bottom" type was popped which
1568-
// is always considered equivalent to the `hty` tag.
1569-
if let Some(rt) = self.pop_ref()? {
1570-
let expected = RefType::new(true, hty).expect("hty should be previously validated");
1571-
let expected = ValType::Ref(expected);
1572-
if !self.resources.is_subtype(ValType::Ref(rt), expected) {
1573-
bail!(
1574-
self.offset,
1575-
"type mismatch: funcref on stack does not match specified type",
1576-
);
1577-
}
1578-
}
1579-
self.check_call_type_index(type_index)
1583+
let ty = self.check_call_ref_ty(type_index)?;
1584+
self.check_call_ty(ty)?;
1585+
Ok(())
15801586
}
15811587
fn visit_return_call_ref(&mut self, type_index: u32) -> Self::Output {
1582-
self.visit_call_ref(type_index)?;
1583-
self.check_return()?;
1584-
self.check_func_type_index_same_results(type_index)?;
1588+
let ty = self.check_call_ref_ty(type_index)?;
1589+
self.check_return_call_ty(ty)?;
15851590
Ok(())
15861591
}
1587-
fn visit_call_indirect(&mut self, index: u32, table_index: u32) -> Self::Output {
1588-
self.check_call_indirect(index, table_index)?;
1592+
fn visit_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output {
1593+
let ty = self.check_call_indirect_ty(type_index, table_index)?;
1594+
self.check_call_ty(ty)?;
15891595
Ok(())
15901596
}
15911597
fn visit_return_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output {
1592-
self.check_call_indirect(type_index, table_index)?;
1593-
self.check_return()?;
1594-
self.check_func_type_index_same_results(type_index)?;
1598+
let ty = self.check_call_indirect_ty(type_index, table_index)?;
1599+
self.check_return_call_ty(ty)?;
15951600
Ok(())
15961601
}
15971602
fn visit_drop(&mut self) -> Self::Output {

0 commit comments

Comments
 (0)