wasmer_wasix/syscalls/wasix/
reflect_signature.rs

1use crate::{WasiEnv, WasiError, WasiModuleTreeHandles, state::FunctionLookupError};
2use tracing::{instrument, trace};
3use wasmer::{FunctionEnvMut, MemorySize, Type, WasmPtr, WasmSlice};
4use wasmer_wasix_types::wasi::{Bool, Errno, ReflectionResult, WasmValueType};
5
6fn serialize_types(types: &[Type]) -> Result<Vec<WasmValueType>, Errno> {
7    types
8        .iter()
9        .map(|t| {
10            WasmValueType::try_from(*t).map_err(|_| {
11                trace!("Cannot convert type {} to WasmValueType", t);
12                Errno::Inval
13            })
14        })
15        .collect::<Result<Vec<_>, _>>()
16}
17
18/// Provides information about a function's signature.
19///
20/// ### Errors
21///
22/// Besides the standard error codes, `reflect_signature` may set `errno` to the
23/// following values:
24///
25/// - `Errno::Inval`: The function pointer is not valid, i.e. it does not point to a
26/// function in the indirect function table or the function has a unsupported
27/// signature. The sizes in the result are undefined in this case.
28/// - `Errno::Overflow`: The argument_types and result_types buffers were not big enough
29/// to hold the signature. They will be left unchanged. The reflection result
30/// will be valid.
31#[instrument(
32    level = "trace",
33    skip_all,
34    fields(
35        %function_id,
36        argument_types_ptr = argument_types.offset().into(),
37        argument_types_len,
38        result_types_ptr = result_types.offset().into(),
39        result_types_len,
40        result = result.offset().into()),
41    ret
42)]
43pub fn reflect_signature<M: MemorySize>(
44    mut ctx: FunctionEnvMut<'_, WasiEnv>,
45    function_id: u32,
46    argument_types: WasmPtr<WasmValueType, M>,
47    argument_types_len: u16,
48    result_types: WasmPtr<WasmValueType, M>,
49    result_types_len: u16,
50    result: WasmPtr<ReflectionResult, M>,
51) -> Result<Errno, WasiError> {
52    let is_closure = WasiModuleTreeHandles::is_closure(&mut ctx, function_id).map_err(|e| {
53        trace!("Failed to check if function is a closure: {}", e);
54        WasiError::Exit(Errno::Noexec.into())
55    })?;
56    let cacheable = if is_closure { Bool::False } else { Bool::True };
57
58    let (env, mut store) = ctx.data_and_store_mut();
59
60    let function_lookup_result = env
61        .inner()
62        .indirect_function_table_lookup(&mut store, function_id);
63
64    let memory = unsafe { env.memory_view(&store) };
65    let signature_info = result.deref(&memory);
66
67    // Look up the function in the indirect function table
68    let function = match function_lookup_result {
69        Ok(f) => f,
70        Err(e) => {
71            let cacheable = match e {
72                FunctionLookupError::Empty(_) => cacheable,
73                FunctionLookupError::OutOfBounds(_) => Bool::False,
74                _ => Bool::True,
75            };
76            trace!(
77                "Failed to look up function in indirect function table: {}",
78                e
79            );
80            wasi_try_mem_ok!(signature_info.write(ReflectionResult {
81                cacheable,
82                arguments: 0,
83                results: 0,
84            }));
85            return Ok(e.into());
86        }
87    };
88
89    let function_type = function.ty(&store);
90    let arguments = function_type.params();
91    let results = function_type.results();
92
93    wasi_try_mem_ok!(signature_info.write(ReflectionResult {
94        cacheable,
95        arguments: arguments.len() as u16,
96        results: results.len() as u16,
97    }));
98
99    if (arguments.len() as u16 > argument_types_len) {
100        trace!(
101            "Provided arguments buffer is too small {}/{}",
102            argument_types_len,
103            arguments.len()
104        );
105        return Ok(Errno::Overflow);
106    }
107    if (results.len() as u16 > result_types_len) {
108        trace!(
109            "Provided results buffer is too small {}/{}",
110            result_types_len,
111            results.len()
112        );
113        return Ok(Errno::Overflow);
114    }
115
116    let serialized_argument_types = wasi_try_ok!(serialize_types(function_type.params()));
117    let serialized_result_types = wasi_try_ok!(serialize_types(function_type.results()));
118
119    let mut argument_types_slice = wasi_try_mem_ok!(WasmSlice::<WasmValueType>::new(
120        &memory,
121        argument_types.offset().into(),
122        arguments.len() as u64
123    ));
124    let mut result_types_slice = wasi_try_mem_ok!(WasmSlice::<WasmValueType>::new(
125        &memory,
126        result_types.offset().into(),
127        results.len() as u64
128    ));
129
130    wasi_try_mem_ok!(argument_types_slice.write_slice(&serialized_argument_types));
131    wasi_try_mem_ok!(result_types_slice.write_slice(&serialized_result_types));
132
133    Ok(Errno::Success)
134}