wasmer_wasix/syscalls/wasix/
reflect_signature.rs

1use crate::{WasiEnv, WasiError, 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 (env, mut store) = ctx.data_and_store_mut();
53
54    let function_lookup_result = env
55        .inner()
56        .indirect_function_table_lookup(&mut store, function_id);
57
58    let memory = unsafe { env.memory_view(&store) };
59    let signature_info = result.deref(&memory);
60
61    // Look up the function in the indirect function table
62    let function = match function_lookup_result {
63        Ok(f) => f,
64        Err(e) => {
65            let cacheable = match e {
66                FunctionLookupError::Empty(_) => {
67                    if env.inner().is_closure(function_id) {
68                        Bool::False
69                    } else {
70                        Bool::True
71                    }
72                }
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 is_closure = env.inner().is_closure(function_id);
90    let cacheable = if is_closure { Bool::False } else { Bool::True };
91
92    let function_type = function.ty(&store);
93    let arguments = function_type.params();
94    let results = function_type.results();
95
96    wasi_try_mem_ok!(signature_info.write(ReflectionResult {
97        cacheable,
98        arguments: arguments.len() as u16,
99        results: results.len() as u16,
100    }));
101
102    if (arguments.len() as u16 > argument_types_len) {
103        trace!(
104            "Provided arguments buffer is too small {}/{}",
105            argument_types_len,
106            arguments.len()
107        );
108        return Ok(Errno::Overflow);
109    }
110    if (results.len() as u16 > result_types_len) {
111        trace!(
112            "Provided results buffer is too small {}/{}",
113            result_types_len,
114            results.len()
115        );
116        return Ok(Errno::Overflow);
117    }
118
119    let serialized_argument_types = wasi_try_ok!(serialize_types(function_type.params()));
120    let serialized_result_types = wasi_try_ok!(serialize_types(function_type.results()));
121
122    let mut argument_types_slice = wasi_try_mem_ok!(WasmSlice::<WasmValueType>::new(
123        &memory,
124        argument_types.offset().into(),
125        arguments.len() as u64
126    ));
127    let mut result_types_slice = wasi_try_mem_ok!(WasmSlice::<WasmValueType>::new(
128        &memory,
129        result_types.offset().into(),
130        results.len() as u64
131    ));
132
133    wasi_try_mem_ok!(argument_types_slice.write_slice(&serialized_argument_types));
134    wasi_try_mem_ok!(result_types_slice.write_slice(&serialized_result_types));
135
136    Ok(Errno::Success)
137}