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}