Skip to main content

optee_utee/parameter/
memref.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Type-safe wrappers for TEE memory-reference parameters.
19//!
20//! A *memref* (memory reference) parameter maps a shared-memory buffer
21//! between the host and the TA. Unlike value parameters which carry two
22//! `u32` values, memrefs can transport arbitrary byte sequences.
23//!
24//! This module provides:
25//!
26//! * [`ParameterMemref`] trait for the shared buffer length.
27//! * [`ParameterMemrefRead`] trait for reading the buffer contents.
28//! * [`ParameterMemrefWrite`] trait for writing into buffers and
29//!   reporting updated sizes.
30//! * Three concrete wrappers encoding the data direction:
31//!   [`ParameterMemrefInput`], [`ParameterMemrefOutput`],
32//!   [`ParameterMemrefInout`].
33//!
34//! # Direction guarantees
35//!
36//! | Type | Host → TA | TA → Host |
37//! |---|---|---|
38//! | `ParameterMemrefInput` | ✓ | ✗ |
39//! | `ParameterMemrefOutput` | ✗ | ✓ |
40//! | `ParameterMemrefInout` | ✓ | ✓ |
41//!
42//! # Shared-memory safety
43//!
44//! The buffers are mapped from Normal World, which may access them concurrently.
45//! Consequently, this module only provides copy-based accessors (`read_to_vec`,
46//! `read_at` for reading; `write_at`, `set_output` for writing), mitigating
47//! REE-controlled time-of-check-to-time-of-use (TOCTOU) risks. Prefer
48//! validating and using the same copy rather than validating one fetch and
49//! using another.
50
51use super::{FromRawParameter, ParamType, RawParamType, check_type_is};
52use crate::{ErrorKind, Result, raw::TEE_Param};
53
54/// Shared buffer length for a memory-reference parameter.
55///
56/// Implemented by [`ParameterMemrefInput`], [`ParameterMemrefOutput`],
57/// and [`ParameterMemrefInout`]. This is the supertrait of
58/// [`ParameterMemrefRead`] and [`ParameterMemrefWrite`].
59///
60/// The value is the size the host supplied for the shared buffer. OP-TEE
61/// validates this range against the caller's shared memory when the TA is
62/// invoked, and the accessors in this module bound every read and write by
63/// this size, so they cannot access past the host-supplied buffer. For output
64/// and in/out parameters the size reported back to the host is updated
65/// separately through [`ParameterMemrefWrite::set_updated_size`]; `buffer_len`
66/// keeps returning the original host-supplied size.
67pub trait ParameterMemref {
68    /// Returns the size of the shared buffer as supplied by the host, in bytes.
69    fn buffer_len(&self) -> usize;
70}
71
72/// Read-only access to a memory-reference parameter's buffer.
73///
74/// Implemented by [`ParameterMemrefInput`] and [`ParameterMemrefInout`].
75///
76/// `read_to_vec` returns a TA-owned copy that is unaffected by later REE
77/// modification; prefer validating and using the same copy.
78pub trait ParameterMemrefRead: ParameterMemref {
79    /// Copies the shared buffer into TA-owned memory.
80    ///
81    /// The allocation is sized from the host-supplied `buffer_len`. Callers
82    /// that cannot accept an allocation of that size should bound
83    /// `buffer_len` first, or read fixed-size fields with [`Self::read_at`].
84    fn read_to_vec(&self) -> alloc::vec::Vec<u8> {
85        let len = self.buffer_len();
86        let mut copy = alloc::vec![0; len];
87        if len != 0 {
88            unsafe {
89                crate::raw::TEE_MemMove(copy.as_mut_ptr().cast(), self.buffer_ptr().cast(), len);
90            }
91        }
92        copy
93    }
94
95    /// Copies bytes starting at `offset` into `dest`.
96    ///
97    /// Returns the number of bytes actually read. The copy is truncated
98    /// to the available bytes when `offset + dest.len()` exceeds the
99    /// buffer length; reading exactly at the end returns `Ok(0)`.
100    ///
101    /// Returns `ErrorKind::BadParameters` if `offset` is beyond the end
102    /// of the buffer.
103    fn read_at(&self, offset: usize, dest: &mut [u8]) -> Result<usize> {
104        if offset > self.buffer_len() {
105            return Err(ErrorKind::BadParameters.into());
106        }
107        let available = self.buffer_len() - offset;
108        let to_read = core::cmp::min(dest.len(), available);
109        if to_read != 0 {
110            unsafe {
111                crate::raw::TEE_MemMove(
112                    dest.as_mut_ptr().cast(),
113                    self.buffer_ptr().add(offset).cast(),
114                    to_read,
115                );
116            }
117        }
118        Ok(to_read)
119    }
120
121    /// Returns the start of the shared input buffer.
122    #[doc(hidden)]
123    fn buffer_ptr(&self) -> *const u8;
124}
125
126/// Write access to a memory-reference parameter's buffer.
127///
128/// Implemented by [`ParameterMemrefOutput`] and [`ParameterMemrefInout`].
129///
130/// All accessors copy caller-supplied data into shared memory, so callers never
131/// hold a direct reference into Normal-World memory.
132pub trait ParameterMemrefWrite: ParameterMemref {
133    /// Sets the updated size after bounds checking.
134    ///
135    /// Returns `ErrorKind::ShortBuffer` if `size > buffer_len()`.
136    fn set_updated_size(&mut self, size: usize) -> Result<()> {
137        if size > self.buffer_len() {
138            return Err(ErrorKind::ShortBuffer.into());
139        }
140        unsafe { self.set_updated_size_unchecked(size) };
141        Ok(())
142    }
143
144    /// Copies `data` into the buffer, then updates the reported size.
145    fn set_output<T: AsRef<[u8]>>(&mut self, data: T) -> Result<()> {
146        self.write_at(0, data)
147    }
148
149    /// Copies `data` into the buffer at the given `offset`, then updates the
150    /// reported size to `offset + data.len()`.
151    ///
152    /// Returns `ErrorKind::ShortBuffer` if the new size would exceed
153    /// the buffer length.
154    fn write_at<T: AsRef<[u8]>>(&mut self, offset: usize, data: T) -> Result<()> {
155        let input = data.as_ref();
156        let new_size = offset
157            .checked_add(input.len())
158            .ok_or(ErrorKind::ShortBuffer)?;
159        if new_size > self.buffer_len() {
160            return Err(ErrorKind::ShortBuffer.into());
161        }
162        if !input.is_empty() {
163            unsafe {
164                crate::raw::TEE_MemMove(
165                    self.buffer_ptr().add(offset).cast(),
166                    input.as_ptr().cast(),
167                    input.len(),
168                );
169            }
170        }
171        unsafe { self.set_updated_size_unchecked(new_size) };
172        Ok(())
173    }
174
175    /// Returns the start of the shared output buffer.
176    #[doc(hidden)]
177    fn buffer_ptr(&mut self) -> *mut u8;
178
179    /// Directly sets the updated size without bounds checking.
180    ///
181    /// # Safety
182    ///
183    /// The `size` must not exceed `buffer_len()`. Prefer
184    /// [`ParameterMemrefWrite::set_updated_size`] unless the caller has already
185    /// checked the bounds.
186    unsafe fn set_updated_size_unchecked(&mut self, size: usize);
187}
188
189/// A memory-reference input parameter.
190///
191/// The host passes a read-only buffer to the TA. The length is the
192/// original buffer size as specified by the host.
193pub struct ParameterMemrefInput<'a>(&'a TEE_Param);
194
195/// A memory-reference in/out parameter.
196///
197/// The host passes a read-write buffer. The TA may read the initial contents,
198/// overwrite them, and report the final number of valid bytes.
199pub struct ParameterMemrefInout<'a> {
200    capacity: usize,
201    raw_param: &'a mut TEE_Param,
202}
203
204/// A memory-reference output parameter.
205///
206/// The host provides a write-only buffer. The TA fills the buffer and report
207/// the final number of valid bytes via
208/// [`ParameterMemrefWrite::set_updated_size`].
209pub struct ParameterMemrefOutput<'a> {
210    capacity: usize,
211    raw_param: &'a mut TEE_Param,
212}
213
214impl<'a> FromRawParameter<'a> for ParameterMemrefInput<'a> {
215    unsafe fn from_raw(raw_type: RawParamType, raw_param: &'a mut TEE_Param) -> Result<Self> {
216        check_type_is(raw_type, ParamType::MemrefInput)?;
217        if unsafe { raw_param.memref.buffer }.is_null() {
218            return Err(ErrorKind::BadParameters.into());
219        }
220        Ok(Self(raw_param))
221    }
222}
223impl<'a> FromRawParameter<'a> for ParameterMemrefInout<'a> {
224    unsafe fn from_raw(raw_type: RawParamType, raw_param: &'a mut TEE_Param) -> Result<Self> {
225        check_type_is(raw_type, ParamType::MemrefInout)?;
226        if unsafe { raw_param.memref.buffer }.is_null() {
227            return Err(ErrorKind::BadParameters.into());
228        }
229        Ok(Self {
230            capacity: unsafe { raw_param.memref.size },
231            raw_param,
232        })
233    }
234}
235impl<'a> FromRawParameter<'a> for ParameterMemrefOutput<'a> {
236    unsafe fn from_raw(raw_type: RawParamType, raw_param: &'a mut TEE_Param) -> Result<Self> {
237        check_type_is(raw_type, ParamType::MemrefOutput)?;
238        if unsafe { raw_param.memref.buffer }.is_null() {
239            return Err(ErrorKind::BadParameters.into());
240        }
241        Ok(Self {
242            capacity: unsafe { raw_param.memref.size },
243            raw_param,
244        })
245    }
246}
247
248impl<'a> ParameterMemref for ParameterMemrefInout<'a> {
249    fn buffer_len(&self) -> usize {
250        self.capacity
251    }
252}
253
254impl<'a> ParameterMemref for ParameterMemrefOutput<'a> {
255    fn buffer_len(&self) -> usize {
256        self.capacity
257    }
258}
259
260impl<'a> ParameterMemref for ParameterMemrefInput<'a> {
261    fn buffer_len(&self) -> usize {
262        unsafe { self.0.memref.size }
263    }
264}
265
266impl<'a> ParameterMemrefWrite for ParameterMemrefInout<'a> {
267    fn buffer_ptr(&mut self) -> *mut u8 {
268        unsafe { self.raw_param.memref.buffer as *mut u8 }
269    }
270    unsafe fn set_updated_size_unchecked(&mut self, size: usize) {
271        self.raw_param.memref.size = size;
272    }
273}
274
275impl<'a> ParameterMemrefWrite for ParameterMemrefOutput<'a> {
276    fn buffer_ptr(&mut self) -> *mut u8 {
277        unsafe { self.raw_param.memref.buffer as *mut u8 }
278    }
279    unsafe fn set_updated_size_unchecked(&mut self, size: usize) {
280        self.raw_param.memref.size = size;
281    }
282}
283
284impl<'a> ParameterMemrefRead for ParameterMemrefInout<'a> {
285    fn buffer_ptr(&self) -> *const u8 {
286        unsafe { self.raw_param.memref.buffer as *const u8 }
287    }
288}
289
290impl<'a> ParameterMemrefRead for ParameterMemrefInput<'a> {
291    fn buffer_ptr(&self) -> *const u8 {
292        unsafe { self.0.memref.buffer as *const u8 }
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299    use crate::raw;
300
301    fn memref(buffer: *mut u8, size: usize) -> TEE_Param {
302        TEE_Param {
303            memref: raw::Memref {
304                buffer: buffer.cast(),
305                size,
306            },
307        }
308    }
309
310    #[test]
311    fn typed_wrappers_reject_null_buffers() {
312        let cases = [
313            (raw::TEE_PARAM_TYPE_MEMREF_INPUT, ParamType::MemrefInput),
314            (raw::TEE_PARAM_TYPE_MEMREF_INOUT, ParamType::MemrefInout),
315            (raw::TEE_PARAM_TYPE_MEMREF_OUTPUT, ParamType::MemrefOutput),
316        ];
317
318        for (raw_type, param_type) in cases {
319            let mut raw_param = memref(core::ptr::null_mut(), 0);
320            let error = match param_type {
321                ParamType::MemrefInput => unsafe {
322                    ParameterMemrefInput::from_raw(raw_type, &mut raw_param).map(|_| ())
323                },
324                ParamType::MemrefInout => unsafe {
325                    ParameterMemrefInout::from_raw(raw_type, &mut raw_param).map(|_| ())
326                },
327                ParamType::MemrefOutput => unsafe {
328                    ParameterMemrefOutput::from_raw(raw_type, &mut raw_param).map(|_| ())
329                },
330                _ => unreachable!(),
331            }
332            .unwrap_err();
333            assert_eq!(error.kind(), ErrorKind::BadParameters);
334        }
335    }
336
337    #[test]
338    fn zero_length_non_null_buffer_is_valid() {
339        let mut raw_param = memref(core::ptr::NonNull::<u8>::dangling().as_ptr(), 0);
340        let input = unsafe {
341            ParameterMemrefInput::from_raw(raw::TEE_PARAM_TYPE_MEMREF_INPUT, &mut raw_param)
342        }
343        .unwrap();
344        assert!(input.read_to_vec().is_empty());
345    }
346
347    /// Guarantees the `read_at` boundary contract: reading exactly at the end
348    /// returns `Ok(0)` with no shared-memory copy; reading beyond the end
349    /// returns `BadParameters` instead of touching out-of-bounds memory; and
350    /// offset arithmetic never overflows even with `usize::MAX`.
351    #[test]
352    fn read_at_boundary_contract() {
353        let mut backing = [1u8, 2, 3, 4, 5];
354        let mut raw_param = memref(backing.as_mut_ptr(), backing.len());
355        let input = unsafe {
356            ParameterMemrefInput::from_raw(raw::TEE_PARAM_TYPE_MEMREF_INPUT, &mut raw_param)
357        }
358        .unwrap();
359        // exactly at the end: zero bytes, no copy
360        let mut empty: [u8; 0] = [];
361        assert_eq!(input.read_at(5, &mut empty).unwrap(), 0);
362        let mut too_big = [0u8; 10];
363        assert_eq!(input.read_at(5, &mut too_big).unwrap(), 0);
364        // beyond the end: caller bug, rejected without copying
365        assert_eq!(
366            input.read_at(6, &mut too_big).unwrap_err().kind(),
367            ErrorKind::BadParameters
368        );
369        assert_eq!(
370            input.read_at(usize::MAX, &mut too_big).unwrap_err().kind(),
371            ErrorKind::BadParameters
372        );
373    }
374}