Skip to content

Commit 8c8555e

Browse files
committed
fix!: unsound ParamMemref::buffer(), add access api and VolatileBuf
1 parent e651157 commit 8c8555e

6 files changed

Lines changed: 451 additions & 18 deletions

File tree

optee-utee/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ edition = "2018"
2828
optee-utee-sys = { version = "0.8.0", path = "optee-utee-sys" }
2929
optee-utee-macros = { version = "0.8.0", path = "macros" }
3030
bitflags = "1.0.4"
31-
uuid = { version = "0.8", default-features = false }
31+
bytemuck = { version = "1.25.0", default-features = false }
3232
hex = { version = "0.4", default-features = false, features = ["alloc"] }
3333
libc_alloc = "1.0.5"
3434
strum_macros = "0.26"
35+
uuid = { version = "0.8", default-features = false }
3536

3637
[dev-dependencies]
3738
rand = "0.8.5"

optee-utee/src/access.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//! Zero sized types and traits encoding access constraints into the type system.
2+
3+
mod private {
4+
pub trait Sealed {}
5+
}
6+
7+
/// All access related structs implement this trait.
8+
///
9+
/// It is sealed to prevent third parties from implementing it.
10+
pub trait Access: private::Sealed {}
11+
12+
/// A type that is accessible (i.e. *not* [NoAccess])
13+
pub trait Accessible: Access {}
14+
15+
/// A type that is readable
16+
pub trait Readable: Accessible {}
17+
18+
/// A type that is writable
19+
pub trait Writable: Accessible {}
20+
21+
/// Implements [`Access`], [`Accessible`], and [`Readable`]
22+
#[derive(Debug, Default, Copy, Clone)]
23+
pub struct Read;
24+
impl private::Sealed for Read {}
25+
impl Access for Read {}
26+
impl Accessible for Read {}
27+
impl Readable for Read {}
28+
29+
/// Implements [`Access`], [`Accessible`], and [`Writable`]
30+
#[derive(Debug, Default, Copy, Clone)]
31+
pub struct Write;
32+
impl private::Sealed for Write {}
33+
impl Access for Write {}
34+
impl Accessible for Write {}
35+
impl Writable for Write {}
36+
37+
/// Implements [`Access`], [`Accessible`], [`Readable`], [`Writable`]
38+
#[derive(Debug, Default, Copy, Clone)]
39+
pub struct ReadWrite;
40+
impl private::Sealed for ReadWrite {}
41+
impl Access for ReadWrite {}
42+
impl Accessible for ReadWrite {}
43+
impl Readable for ReadWrite {}
44+
impl Writable for ReadWrite {}
45+
46+
/// Implements [`Access`]
47+
#[derive(Debug, Default, Copy, Clone)]
48+
pub struct NoAccess;
49+
impl private::Sealed for NoAccess {}
50+
impl Access for NoAccess {}

optee-utee/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use std::error;
3434
/// Ok(())
3535
/// }
3636
/// ````
37-
pub type Result<T> = result::Result<T, Error>;
37+
pub type Result<T, E = Error> = result::Result<T, E>;
3838

3939
#[derive(Clone)]
4040
pub struct Error {

optee-utee/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,18 @@ pub use optee_utee_macros::{
6464
pub mod trace;
6565
#[macro_use]
6666
mod macros;
67+
pub mod access;
6768
pub mod arithmetical;
6869
pub mod crypto_op;
6970
mod error;
7071
pub mod extension;
7172
pub mod identity;
7273
pub mod net;
7374
pub mod object;
74-
mod parameter;
75+
pub mod parameter;
7576
pub mod property;
7677
mod ta_session;
7778
mod tee_parameter;
7879
pub mod time;
7980
pub mod uuid;
81+
pub mod volatile;

optee-utee/src/parameter.rs

Lines changed: 215 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use crate::{Error, ErrorKind, Result};
19-
use core::{marker, slice};
18+
use crate::{
19+
access::{Accessible, NoAccess, Read, ReadWrite, Writable, Write},
20+
volatile::VolatileBuf,
21+
Error, ErrorKind, Result,
22+
};
23+
use core::{
24+
fmt::{Debug, Display},
25+
marker,
26+
ptr::{addr_of_mut, NonNull},
27+
};
2028
use optee_utee_sys as raw;
2129

2230
pub type RawParamType = u32;
@@ -69,27 +77,137 @@ impl<'parameter> ParamValue<'parameter> {
6977
}
7078
}
7179

72-
pub struct ParamMemref<'parameter> {
73-
raw: *mut raw::Memref,
80+
/// A [`Parameter`] that is a reference to CA-controlled shared memory.
81+
///
82+
/// Note: The memory it points to is volatile and can contain maliciously crafted
83+
/// data, so care should be taken when accessing it. For a safe api, first check
84+
/// the type of the memref via [`Self::in_`], [`Self::out`], or [`Self::inout`].
85+
///
86+
/// These will return type type-safe versions that unsure that read or write access
87+
/// is only allowed based on the underlying [`ParamType`].
88+
///
89+
/// Then, call [`Self::buffer`] which returns a [`VolatileBuf`] that can be accessed
90+
/// safely.
91+
pub struct ParamMemref<'parameter, A = NoAccess> {
92+
raw: NonNull<raw::Memref>,
7493
param_type: ParamType,
75-
_marker: marker::PhantomData<&'parameter mut [u8]>,
94+
capacity: usize,
95+
_phantom_param: marker::PhantomData<&'parameter mut [u8]>,
96+
_phantom_access: marker::PhantomData<A>,
7697
}
7798

78-
impl<'parameter> ParamMemref<'parameter> {
79-
pub fn buffer(&mut self) -> &mut [u8] {
80-
unsafe { slice::from_raw_parts_mut((*self.raw).buffer as *mut u8, (*self.raw).size) }
99+
impl<'parameter, A> ParamMemref<'parameter, A> {
100+
// Helper function to cast access.
101+
fn access<NewAccess>(
102+
self,
103+
expected_param_type: ParamType,
104+
) -> Result<ParamMemref<'parameter, NewAccess>, InvalidAccessErr<Self, NewAccess>> {
105+
if self.param_type != expected_param_type {
106+
return Err(InvalidAccessErr {
107+
param_type: self.param_type,
108+
value: self,
109+
_phantom: marker::PhantomData,
110+
});
111+
}
112+
113+
Ok(ParamMemref::<'parameter, NewAccess> {
114+
raw: self.raw,
115+
param_type: self.param_type,
116+
capacity: self.capacity,
117+
_phantom_param: marker::PhantomData,
118+
_phantom_access: marker::PhantomData,
119+
})
120+
}
121+
122+
/// Checks that this param is a [`ParamType::MemrefInput`] and casts access to [`Read`].
123+
pub fn in_(self) -> Result<ParamMemref<'parameter, Read>, InvalidAccessErr<Self, Read>> {
124+
self.access(ParamType::MemrefInput)
125+
}
126+
127+
/// Checks that this param is a [`ParamType::MemrefOutput`] and casts access to [`Write`].
128+
pub fn out(self) -> Result<ParamMemref<'parameter, Write>, InvalidAccessErr<Self, Write>> {
129+
self.access(ParamType::MemrefOutput)
130+
}
131+
132+
/// Checks that this param is a [`ParamType::MemrefInout`] and casts access to [`ReadWrite`].
133+
pub fn inout(
134+
self,
135+
) -> Result<ParamMemref<'parameter, ReadWrite>, InvalidAccessErr<Self, ReadWrite>> {
136+
self.access(ParamType::MemrefInout)
137+
}
138+
139+
/// Casts access type to [`NoAccess`], preventing anyone from accessing the buffer
140+
/// it points to (except by c-style pointer).
141+
pub fn noaccess(self) -> ParamMemref<'parameter, NoAccess> {
142+
ParamMemref {
143+
raw: self.raw,
144+
param_type: self.param_type,
145+
capacity: self.capacity,
146+
_phantom_param: marker::PhantomData,
147+
_phantom_access: marker::PhantomData,
148+
}
149+
}
150+
151+
pub fn raw(&mut self) -> NonNull<raw::Memref> {
152+
self.raw
153+
}
154+
155+
/// The size of the allocated memory region (i.e. the original value of `self.size`)
156+
pub fn capacity(&self) -> usize {
157+
self.capacity
81158
}
82159

83160
pub fn param_type(&self) -> ParamType {
84161
self.param_type
85162
}
163+
}
86164

87-
pub fn raw(&mut self) -> *mut raw::Memref {
88-
self.raw
165+
impl<'parameter, A: Accessible> ParamMemref<'parameter, A> {
166+
/// Can return `None` if the buffer is null, which is a valid state that can happen
167+
/// in OP-TEE.
168+
pub fn buffer(&mut self) -> Option<VolatileBuf<'parameter, A>> {
169+
let memref = unsafe { self.raw.read() };
170+
let buf_ptr = memref.buffer.cast::<u8>();
171+
let buf_ptr = NonNull::new(buf_ptr)?;
172+
173+
Some(unsafe { VolatileBuf::new(buf_ptr, self.capacity) })
174+
}
175+
}
176+
177+
impl<'parameter, A: Writable> ParamMemref<'parameter, A> {
178+
/// Errors if the new size would be bigger than `self.capacity()`
179+
pub fn set_updated_size(&mut self, size: usize) -> Result<(), BiggerThanCapacityErr> {
180+
if size > self.capacity {
181+
return Err(BiggerThanCapacityErr {
182+
requested_size: size,
183+
capacity: self.capacity,
184+
});
185+
}
186+
let memref = unsafe { self.raw.as_mut() };
187+
memref.size = size;
188+
189+
Ok(())
89190
}
90191

91-
pub fn set_updated_size(&mut self, size: usize) {
92-
unsafe { (*self.raw).size = size };
192+
/// By convention, setting the raw size to something larger than what was initially passed by OP-TEE, is a signal to
193+
/// the CA that there is insufficient memory allocated to output a result, and that the CA should invoke the function again
194+
/// with a larger buffer. See section `4.3.6.4` of the Global Platform TEE Internal Core API.
195+
/// Note that this does *not* update `self.capacity()`.
196+
/// Returns an error if the requested amount is not any bigger than the existing capacity.
197+
pub fn request_more_capacity(
198+
&mut self,
199+
new_capacity: usize,
200+
) -> Result<(), NotBiggerThanCapacityErr> {
201+
if new_capacity <= self.capacity {
202+
return Err(NotBiggerThanCapacityErr {
203+
requested_capacity: new_capacity,
204+
capacity: self.capacity,
205+
});
206+
}
207+
let memref = unsafe { self.raw.as_mut() };
208+
memref.size = new_capacity;
209+
210+
Ok(())
93211
}
94212
}
95213

@@ -127,9 +245,11 @@ impl Parameter {
127245
match self.param_type {
128246
ParamType::MemrefInout | ParamType::MemrefInput | ParamType::MemrefOutput => {
129247
Ok(ParamMemref {
130-
raw: &mut (*self.raw).memref,
248+
raw: NonNull::new_unchecked(addr_of_mut!((*self.raw).memref)),
131249
param_type: self.param_type,
132-
_marker: marker::PhantomData,
250+
capacity: unsafe { *self.raw }.memref.size,
251+
_phantom_access: marker::PhantomData,
252+
_phantom_param: marker::PhantomData,
133253
})
134254
}
135255
_ => Err(Error::new(ErrorKind::BadParameters)),
@@ -160,7 +280,7 @@ impl From<u32> for ParamTypes {
160280
}
161281
}
162282

163-
#[derive(Copy, Clone)]
283+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
164284
pub enum ParamType {
165285
None = 0,
166286
ValueInput = 1,
@@ -171,6 +291,20 @@ pub enum ParamType {
171291
MemrefInout = 7,
172292
}
173293

294+
impl Display for ParamType {
295+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
296+
match self {
297+
ParamType::None => write!(f, "None"),
298+
ParamType::ValueInput => write!(f, "ValueInput"),
299+
ParamType::ValueOutput => write!(f, "ValueOutput"),
300+
ParamType::ValueInout => write!(f, "ValueInout"),
301+
ParamType::MemrefInput => write!(f, "MemrefInput"),
302+
ParamType::MemrefOutput => write!(f, "MemrefOutput"),
303+
ParamType::MemrefInout => write!(f, "MemrefInout"),
304+
}
305+
}
306+
}
307+
174308
impl From<u32> for ParamType {
175309
fn from(value: u32) -> Self {
176310
match value {
@@ -185,3 +319,69 @@ impl From<u32> for ParamType {
185319
}
186320
}
187321
}
322+
323+
#[derive(Debug)]
324+
pub struct BiggerThanCapacityErr {
325+
requested_size: usize,
326+
capacity: usize,
327+
}
328+
329+
impl Display for BiggerThanCapacityErr {
330+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
331+
write!(
332+
f,
333+
"requested size {} but this is bigger than the capacity {}",
334+
self.requested_size, self.capacity,
335+
)
336+
}
337+
}
338+
339+
impl core::error::Error for BiggerThanCapacityErr {}
340+
341+
#[derive(Debug)]
342+
pub struct NotBiggerThanCapacityErr {
343+
requested_capacity: usize,
344+
capacity: usize,
345+
}
346+
347+
impl Display for NotBiggerThanCapacityErr {
348+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
349+
write!(
350+
f,
351+
"requested capacity {} but should have been bigger the original capacity {}",
352+
self.requested_capacity, self.capacity,
353+
)
354+
}
355+
}
356+
357+
impl core::error::Error for NotBiggerThanCapacityErr {}
358+
359+
/// Error while attempting to cast access
360+
#[derive(Debug, Clone)]
361+
pub struct InvalidAccessErr<T, NewAccess> {
362+
param_type: ParamType,
363+
value: T,
364+
_phantom: marker::PhantomData<NewAccess>,
365+
}
366+
367+
impl<T, NewAccess> Display for InvalidAccessErr<T, NewAccess> {
368+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
369+
write!(
370+
f,
371+
"unable to cast access to {}, param_type was {}",
372+
core::any::type_name::<NewAccess>(),
373+
self.param_type
374+
)
375+
}
376+
}
377+
378+
impl<T: Debug + Display, NewAccess: Debug + Display> core::error::Error
379+
for InvalidAccessErr<T, NewAccess>
380+
{
381+
}
382+
383+
impl<T, NewAccess> InvalidAccessErr<T, NewAccess> {
384+
pub fn into_inner(self) -> T {
385+
self.value
386+
}
387+
}

0 commit comments

Comments
 (0)