From 45babf96daae7bdd6b3b426b70669242cdd94ada Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 29 May 2026 23:10:15 +0000 Subject: [PATCH 1/9] fix OP-TEE client identity handling --- litebox_common_optee/src/lib.rs | 52 ++++++++++++++++++++++++++- litebox_shim_optee/src/lib.rs | 10 +++--- litebox_shim_optee/src/msg_handler.rs | 34 +++++++++++++----- 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index b2cf533cae..41bc745818 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -624,6 +624,17 @@ pub struct TeeUuid { } impl TeeUuid { + /// The nil UUID (all zeros, RFC 4122 S4.1.7). + /// + /// Used for anonymous clients (e.g. ,`TeeLogin::Public`) that carry no + /// REE-derived identity. + pub const NIL: Self = Self { + time_low: 0, + time_mid: 0, + time_hi_and_version: 0, + clock_seq_and_node: [0; 8], + }; + /// Converts a UUID from a 16-byte array in RFC 4122 format (big-endian for numeric fields). /// /// The byte layout is: @@ -832,8 +843,13 @@ const TEE_LOGIN_APPLICATION: u32 = 0x4; const TEE_LOGIN_APPLICATION_USER: u32 = 0x5; const TEE_LOGIN_APPLICATION_GROUP: u32 = 0x6; const TEE_LOGIN_TRUSTED_APP: u32 = 0xf000_0000; +// Private OP-TEE login for in-kernel REE clients (from +// `tee_api_defines_extensions.h`). Userspace cannot send it. It uses +// the nil client UUID. +const TEE_LOGIN_REE_KERNEL: u32 = 0x8000_0000; /// `TEE Login type` from `optee_os/lib/libutee/include/tee_api_defines.h` +/// (plus the `TEE_LOGIN_REE_KERNEL` extension). #[derive(Clone, Copy, PartialEq, TryFromPrimitive, Immutable, IntoBytes)] #[repr(u32)] pub enum TeeLogin { @@ -843,6 +859,7 @@ pub enum TeeLogin { Application = TEE_LOGIN_APPLICATION, ApplicationUser = TEE_LOGIN_APPLICATION_USER, ApplicationGroup = TEE_LOGIN_APPLICATION_GROUP, + ReeKernel = TEE_LOGIN_REE_KERNEL, TrustedApp = TEE_LOGIN_TRUSTED_APP, } @@ -1469,6 +1486,11 @@ const OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: u8 = 0xb; // Note: `OPTEE_MSG_ATTR_TYPE_FMEM_*` are aliases of `OPTEE_MSG_ATTR_TYPE_RMEM_*`. // Whether it is RMEM of FMEM depends on the conduit. +/// Meta-parameter marker (bit 8) of the attribute word. Set on the `OpenSession` +/// TA-UUID and client-identity params, which are consumed by the secure world +/// and not delivered to the TA. +const OPTEE_MSG_ATTR_META: u64 = 1 << 8; + #[non_exhaustive] #[derive(Debug, PartialEq, TryFromPrimitive)] #[repr(u8)] @@ -1492,11 +1514,17 @@ pub enum OpteeMsgAttrType { /// - bit 8 – meta /// - bit 9 – noncontig /// - bits \[63:10\] – reserved (zero) -#[derive(Clone, Copy, Default, FromBytes, IntoBytes, Immutable, KnownLayout)] +#[derive(Clone, Copy, Default, PartialEq, Eq, FromBytes, IntoBytes, Immutable, KnownLayout)] #[repr(transparent)] pub struct OpteeMsgAttr(u64); impl OpteeMsgAttr { + /// The exact attribute word an `OpenSession` meta value parameter must carry + /// (`OPTEE_MSG_ATTR_META | OPTEE_MSG_ATTR_TYPE_VALUE_INPUT`, all other bits + /// zero). See [`OpteeMsgArgs::get_meta_param_value`]. + pub const META_VALUE_INPUT: Self = + Self(OPTEE_MSG_ATTR_META | OPTEE_MSG_ATTR_TYPE_VALUE_INPUT as u64); + /// Returns the attribute type (bits 0–7). #[allow(clippy::cast_possible_truncation)] pub fn attr_type(&self) -> u8 { @@ -1757,6 +1785,28 @@ impl OpteeMsgArgs { .ok_or(OpteeSmcReturnCode::EBadCmd)?) } } + + /// Read a value parameter that must be tagged as an `OpenSession` meta parameter. + /// + /// `OpenSession` conveys the TA UUID and client identity in the first two + /// params, each marked exactly [`OpteeMsgAttr::META_VALUE_INPUT`]. This + /// mirrors OP-TEE OS `get_open_session_meta()`, which rejects the request + /// unless the full attr word of both meta params equals that value. Plain + /// `get_param_value` ignores these bits, so it must not be used for this. + pub fn get_meta_param_value( + &self, + index: usize, + ) -> Result { + if index >= self.num_params as usize { + return Err(OpteeSmcReturnCode::ENotAvail); + } + let param = &self.params[index]; + if param.attr != OpteeMsgAttr::META_VALUE_INPUT { + return Err(OpteeSmcReturnCode::EBadCmd); + } + param.get_param_value().ok_or(OpteeSmcReturnCode::EBadCmd) + } + pub fn set_param_value( &mut self, index: usize, diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index 2afba0c79b..d9f218f0cc 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -244,9 +244,11 @@ impl OpteeShim { thread: ThreadState::new(), session_id, ta_app_id: ta_uuid, + // Fall back to the anonymous public identity when no client is + // supplied (matches OP-TEE OS / the Linux driver). client_identity: client.unwrap_or(TeeIdentity { - login: TeeLogin::User, - uuid: TeeUuid::default(), + login: TeeLogin::Public, + uuid: TeeUuid::NIL, }), tee_cryp_state_map: TeeCrypStateMap::new(), tee_obj_map: TeeObjMap::new(), @@ -1475,8 +1477,8 @@ mod test_utils { session_id: SessionIdPool::allocate().unwrap(), ta_app_id: TeeUuid::default(), client_identity: TeeIdentity { - login: TeeLogin::User, - uuid: TeeUuid::default(), + login: TeeLogin::Public, + uuid: TeeUuid::NIL, }, tee_cryp_state_map: TeeCrypStateMap::new(), tee_obj_map: TeeObjMap::new(), diff --git a/litebox_shim_optee/src/msg_handler.rs b/litebox_shim_optee/src/msg_handler.rs index 024df141a9..0a800d911b 100644 --- a/litebox_shim_optee/src/msg_handler.rs +++ b/litebox_shim_optee/src/msg_handler.rs @@ -436,25 +436,43 @@ pub fn decode_ta_request( let (ta_uuid, client_identity, skip): (Option, Option, usize) = if ta_entry_func == UteeEntryFunc::OpenSession { - // If it is an OpenSession request, extract UUIDs and login from params[0] and params[1] - // Based on observed Linux kernel behavior: + // If it is an OpenSession request, extract the TA UUID, client UUID, + // and login from the two meta params. Wire layout (per the Linux + // OP-TEE driver): // - params[0].a/b = TA UUID (two little-endian u64 values) - // - params[1].a/b = client UUID (two little-endian u64 values) + // - params[1].a/b = client UUID, but only meaningful for the + // user/group/application logins; PUBLIC/REE_KERNEL ignore it and + // report the nil UUID (see the match below). // - params[1].c = client login type (TEE_LOGIN_*) - let param0 = msg_args.get_param_value(0)?; + let param0 = msg_args.get_meta_param_value(0)?; let ta_data = [param0.a, param0.b]; - let param1 = msg_args.get_param_value(1)?; - let client_data = [param1.a, param1.b]; + let param1 = msg_args.get_meta_param_value(1)?; let login: u32 = param1.c.trunc(); - let login = TeeLogin::try_from(login).unwrap_or(TeeLogin::Public); + // Reject unknown login methods + let login = TeeLogin::try_from(login).map_err(|_| OpteeSmcReturnCode::EBadCmd)?; + + // Only the REE-derived user/group/application logins carry a + // meaningful client UUID. PUBLIC and REE_KERNEL clients are anonymous + // and must report the nil UUID (OP-TEE OS memsets it to zero). + // TRUSTED_APP identifies a TA-to-TA caller and is established + // internally, never from a normal-world message. + let client_uuid = match login { + TeeLogin::Public | TeeLogin::ReeKernel => TeeUuid::NIL, + TeeLogin::User + | TeeLogin::Group + | TeeLogin::Application + | TeeLogin::ApplicationUser + | TeeLogin::ApplicationGroup => TeeUuid::from_u64_array([param1.a, param1.b]), + TeeLogin::TrustedApp => return Err(OpteeSmcReturnCode::EBadCmd), + }; // Skip the first two parameters as they convey TA and client UUIDs ( Some(TeeUuid::from_u64_array(ta_data)), Some(TeeIdentity { login, - uuid: TeeUuid::from_u64_array(client_data), + uuid: client_uuid, }), 2, ) From e2e0a36a643bd9a05cea88e170115e99e6f0a0c3 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 29 May 2026 23:40:59 +0000 Subject: [PATCH 2/9] fix harness --- .../src/tests.rs | 90 ++++++++++++++++++- .../tests/aes-ta-cmds.json | 5 +- .../tests/hello-ta-cmds.json | 5 +- .../tests/kmpp-ta-cmds.json | 6 ++ .../tests/random-ta-cmds.json | 5 +- 5 files changed, 106 insertions(+), 5 deletions(-) diff --git a/litebox_runner_optee_on_linux_userland/src/tests.rs b/litebox_runner_optee_on_linux_userland/src/tests.rs index 9bfe30f8ec..4b6ce03467 100644 --- a/litebox_runner_optee_on_linux_userland/src/tests.rs +++ b/litebox_runner_optee_on_linux_userland/src/tests.rs @@ -7,7 +7,9 @@ use litebox::platform::RawConstPointer; use litebox::utils::TruncateExt; -use litebox_common_optee::{TeeParamType, UteeEntryFunc, UteeParamOwned, UteeParams}; +use litebox_common_optee::{ + TeeIdentity, TeeLogin, TeeParamType, TeeUuid, UteeEntryFunc, UteeParamOwned, UteeParams, +}; use litebox_shim_optee::session::SessionManager; use litebox_shim_optee::{LoadedProgram, UserConstPtr}; use serde::Deserialize; @@ -51,12 +53,22 @@ pub fn run_ta_with_test_commands( let ta_head = litebox_common_optee::parse_ta_head(ta_bin) .expect("Failed to parse TA header from ta_bin"); let session_token = session_manager.try_acquire_open_session_token().unwrap(); + // Emulate the client identity a real REE client would present. The + // secure world normally derives this from the OpenSession meta + // params; here the test file states it, defaulting to a `user` login. + let client_identity = cmd.client_identity.as_ref().map_or( + TeeIdentity { + login: TeeLogin::User, + uuid: TeeUuid::NIL, + }, + ClientIdentityJson::to_tee_identity, + ); let loaded = shim .load_ldelf( ldelf_bin, ta_head.uuid, Some(ta_bin), - None, + Some(client_identity), session_token.session_id().unwrap(), ) .map_err(|_| { @@ -175,6 +187,80 @@ pub struct TaCommandBase64 { cmd_id: u32, #[serde(default)] args: Vec, + /// Client identity presented for an `OpenSession`, mirroring the + /// `TEE_Identity` a real REE client would supply (the secure world derives + /// this from the message meta params; this userland harness has no such + /// client, so the test states it here). Ignored for other commands. When + /// absent it defaults to a `user` login with the nil UUID, which is what a + /// typical REE user client presents. + #[serde(default)] + client_identity: Option, +} + +/// Client identity for an `OpenSession`, parsed from the test JSON. +#[derive(Debug, Deserialize)] +struct ClientIdentityJson { + #[serde(default)] + login: ClientLoginJson, + /// RFC 4122 UUID string (hyphens optional). Defaults to the nil UUID. + #[serde(default)] + uuid: Option, +} + +/// JSON mirror of [`TeeLogin`], so test files can name logins (e.g. `"user"`) +/// instead of raw `TEE_LOGIN_*` constants. +#[derive(Debug, Default, Clone, Copy, Deserialize)] +#[serde(rename_all = "snake_case")] +enum ClientLoginJson { + Public, + #[default] + User, + Group, + Application, + ApplicationUser, + ApplicationGroup, + ReeKernel, + TrustedApp, +} + +impl From for TeeLogin { + fn from(login: ClientLoginJson) -> Self { + match login { + ClientLoginJson::Public => TeeLogin::Public, + ClientLoginJson::User => TeeLogin::User, + ClientLoginJson::Group => TeeLogin::Group, + ClientLoginJson::Application => TeeLogin::Application, + ClientLoginJson::ApplicationUser => TeeLogin::ApplicationUser, + ClientLoginJson::ApplicationGroup => TeeLogin::ApplicationGroup, + ClientLoginJson::ReeKernel => TeeLogin::ReeKernel, + ClientLoginJson::TrustedApp => TeeLogin::TrustedApp, + } + } +} + +impl ClientIdentityJson { + fn to_tee_identity(&self) -> TeeIdentity { + let uuid = self + .uuid + .as_deref() + .map_or(TeeUuid::NIL, parse_uuid_or_panic); + TeeIdentity { + login: self.login.into(), + uuid, + } + } +} + +/// Parse an RFC 4122 UUID string (hyphens optional) into a [`TeeUuid`]. +fn parse_uuid_or_panic(s: &str) -> TeeUuid { + let hex: String = s.chars().filter(|&c| c != '-').collect(); + assert_eq!(hex.len(), 32, "client uuid must be 32 hex digits: {s:?}"); + let mut bytes = [0u8; 16]; + for (i, byte) in bytes.iter_mut().enumerate() { + *byte = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16) + .unwrap_or_else(|_| panic!("invalid hex in client uuid: {s:?}")); + } + TeeUuid::from_bytes(bytes) } #[derive(Debug, Deserialize)] diff --git a/litebox_runner_optee_on_linux_userland/tests/aes-ta-cmds.json b/litebox_runner_optee_on_linux_userland/tests/aes-ta-cmds.json index 8bc266ac0b..a80a3bcd09 100644 --- a/litebox_runner_optee_on_linux_userland/tests/aes-ta-cmds.json +++ b/litebox_runner_optee_on_linux_userland/tests/aes-ta-cmds.json @@ -1,6 +1,9 @@ [ { - "func_id": "open_session" + "func_id": "open_session", + "client_identity": { + "login": "user" + } }, { "func_id": "invoke_command", diff --git a/litebox_runner_optee_on_linux_userland/tests/hello-ta-cmds.json b/litebox_runner_optee_on_linux_userland/tests/hello-ta-cmds.json index a55e89770a..9d87bf1087 100644 --- a/litebox_runner_optee_on_linux_userland/tests/hello-ta-cmds.json +++ b/litebox_runner_optee_on_linux_userland/tests/hello-ta-cmds.json @@ -1,6 +1,9 @@ [ { - "func_id": "open_session" + "func_id": "open_session", + "client_identity": { + "login": "user" + } }, { "func_id": "invoke_command", diff --git a/litebox_runner_optee_on_linux_userland/tests/kmpp-ta-cmds.json b/litebox_runner_optee_on_linux_userland/tests/kmpp-ta-cmds.json index 9eb2e0cee8..88d5e41870 100644 --- a/litebox_runner_optee_on_linux_userland/tests/kmpp-ta-cmds.json +++ b/litebox_runner_optee_on_linux_userland/tests/kmpp-ta-cmds.json @@ -1,6 +1,9 @@ [ { "func_id": "open_session", + "client_identity": { + "login": "user" + }, "args": [ { "param_type": "value_input", @@ -31,6 +34,9 @@ }, { "func_id": "open_session", + "client_identity": { + "login": "user" + }, "args": [ { "param_type": "value_input", diff --git a/litebox_runner_optee_on_linux_userland/tests/random-ta-cmds.json b/litebox_runner_optee_on_linux_userland/tests/random-ta-cmds.json index 166b1d7180..8df2c67dc4 100644 --- a/litebox_runner_optee_on_linux_userland/tests/random-ta-cmds.json +++ b/litebox_runner_optee_on_linux_userland/tests/random-ta-cmds.json @@ -1,6 +1,9 @@ [ { - "func_id": "open_session" + "func_id": "open_session", + "client_identity": { + "login": "user" + } }, { "func_id": "invoke_command", From 0de72be14972b6e1294ccfe6287b0bb0f2a189a9 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 26 Jun 2026 04:57:27 +0000 Subject: [PATCH 3/9] per-session client_identity --- Cargo.lock | 1 - dev_tests/src/ratchet.rs | 2 +- litebox_common_optee/src/lib.rs | 4 +- litebox_runner_lvbs/Cargo.toml | 1 - litebox_runner_lvbs/src/lib.rs | 47 +++++++------- .../src/lib.rs | 15 ++--- .../src/tests.rs | 32 ++++++---- litebox_shim_optee/src/lib.rs | 62 +++++++++++-------- litebox_shim_optee/src/session.rs | 51 ++++++++++++++- litebox_shim_optee/src/syscalls/tee.rs | 2 +- 10 files changed, 135 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ad506f6b7..d32914691f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1642,7 +1642,6 @@ dependencies = [ "litebox_shim_optee", "litebox_util_log", "log", - "once_cell", "spin 0.10.0", "x86_64", ] diff --git a/dev_tests/src/ratchet.rs b/dev_tests/src/ratchet.rs index fc38bc866d..c94a856d64 100644 --- a/dev_tests/src/ratchet.rs +++ b/dev_tests/src/ratchet.rs @@ -40,7 +40,7 @@ fn ratchet_globals() -> Result<()> { ("litebox_platform_lvbs/", 24), ("litebox_platform_multiplex/", 1), ("litebox_platform_windows_userland/", 8), - ("litebox_runner_lvbs/", 6), + ("litebox_runner_lvbs/", 5), ("litebox_runner_snp/", 2), ("litebox_shim_linux/", 1), ("litebox_shim_optee/", 4), diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index 41bc745818..3214f1fd2f 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -761,7 +761,7 @@ pub struct TaHead { pub const TA_HEAD_SECTION_NAME: &str = ".ta_head"; /// `TEE_Identity` from `optee_os/lib/libutee/include/tee_api_types.h`. -#[derive(Clone, Copy, PartialEq, Immutable, IntoBytes)] +#[derive(Clone, Copy, PartialEq, Debug, Immutable, IntoBytes)] #[repr(C)] pub struct TeeIdentity { pub login: TeeLogin, @@ -850,7 +850,7 @@ const TEE_LOGIN_REE_KERNEL: u32 = 0x8000_0000; /// `TEE Login type` from `optee_os/lib/libutee/include/tee_api_defines.h` /// (plus the `TEE_LOGIN_REE_KERNEL` extension). -#[derive(Clone, Copy, PartialEq, TryFromPrimitive, Immutable, IntoBytes)] +#[derive(Clone, Copy, PartialEq, Debug, TryFromPrimitive, Immutable, IntoBytes)] #[repr(u32)] pub enum TeeLogin { Public = TEE_LOGIN_PUBLIC, diff --git a/litebox_runner_lvbs/Cargo.toml b/litebox_runner_lvbs/Cargo.toml index a1d7894292..3c21d1f140 100644 --- a/litebox_runner_lvbs/Cargo.toml +++ b/litebox_runner_lvbs/Cargo.toml @@ -14,7 +14,6 @@ litebox_shim_optee = { path = "../litebox_shim_optee/", version = "0.1.0" } litebox_util_log = { version = "0.1.0", path = "../litebox_util_log" } log = { version = "0.4", default-features = false } spin = { version = "0.10.0", default-features = false, features = ["spin_mutex"] } -once_cell = { version = "1.21.3", default-features = false, features = ["race", "alloc"] } [target.'cfg(target_arch = "x86_64")'.dependencies] x86_64 = { version = "0.15.2", default-features = false, features = ["instructions"] } diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index bbfb64b64a..13f0e71453 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -42,9 +42,8 @@ use litebox_shim_optee::msg_handler::{ decode_ta_request, handle_optee_msg_args, handle_optee_smc_args, packed_msg_args_lock, update_optee_msg_args, }; -use litebox_shim_optee::session::{OpenSessionTarget, SessionManager, TaInstance}; +use litebox_shim_optee::session::{OpenSessionTarget, TaInstance, session_manager}; use litebox_shim_optee::{NormalWorldConstPtr, NormalWorldMutPtr, UserConstPtr}; -use once_cell::race::OnceBox; /// Seed the initial heap regions so the global allocator has enough memory /// for slab-backed allocations (the slab needs >= 2 MB backing pages). @@ -288,12 +287,6 @@ fn optee_smc_handler_entry_inner( Ok(0) } -/// Get the global session manager. -fn session_manager() -> &'static SessionManager { - static SESSION_MANAGER: OnceBox = OnceBox::new(); - SESSION_MANAGER.get_or_init(|| Box::new(SessionManager::new())) -} - /// Switch to the base page table. /// /// This must be called before returning to VTL0 to ensure VTL1 reentry is @@ -527,6 +520,7 @@ fn handle_open_session( msg_args_phys_addr, instance, params, + client_identity, &ta_req_info, ), OpenSessionTarget::NewInstance => open_session_new_instance( @@ -560,6 +554,7 @@ fn open_session_single_instance( msg_args_phys_addr: u64, instance: &TaInstance, params: &[litebox_common_optee::UteeParamOwned], + client_identity: Option, ta_req_info: &litebox_shim_optee::msg_handler::TaRequestInfo, ) -> Result<(), OpteeSmcReturnCode> { let task_pt_id = instance.task_page_table_id(); @@ -570,6 +565,9 @@ fn open_session_single_instance( // Safe to unwrap: session ID has been just created. let runner_session_id = session_token.session_id().unwrap(); + // Record the client identity before running OpenSession + session_manager().set_session_client_identity(runner_session_id, client_identity); + debug_serial_println!( "Reusing single-instance TA: uuid={:?}, task_pt_id={}, session_id={}", ta_uuid, @@ -588,7 +586,7 @@ fn open_session_single_instance( .ok_or(OpteeSmcReturnCode::EBadCmd)? .load_ta_context( params, - Some(runner_session_id), + runner_session_id, UteeEntryFunc::OpenSession as u32, None, ) @@ -704,7 +702,7 @@ fn open_session_single_instance( } /// Create a new TA instance for a session. Must be called from within a -/// [`SessionManager::with_ta`] closure. +/// [`litebox_shim_optee::session::SessionManager::with_ta`] closure. /// /// If ldelf loading or OpenSession entry point fails, the page table is torn down. /// Per OP-TEE OS semantics: if OpenSession returns non-success, cleanup happens. @@ -741,19 +739,13 @@ fn open_session_new_instance( // Load ldelf and TA - Box immediately to keep at fixed heap address let shim = litebox_shim_optee::OpteeShimBuilder::new().build(); let loaded_program = Box::new( - shim.load_ldelf( - LDELF_BINARY, - ta_uuid, - Some(ta_bin), - client_identity, - runner_session_id, - ) - .map_err(|_| { - // Safety: We are about to tear down this TA instance; - // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; - OpteeSmcReturnCode::ENomem - })?, + shim.load_ldelf(LDELF_BINARY, ta_uuid, Some(ta_bin)) + .map_err(|_| { + // Safety: We are about to tear down this TA instance; + // no references to user-space memory will be held afterwards. + unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + OpteeSmcReturnCode::ENomem + })?, ); let ta_flags = loaded_program.ta_flags; @@ -801,6 +793,9 @@ fn open_session_new_instance( return Ok(()); } + // Record the client identity before running OpenSession + session_manager().set_session_client_identity(runner_session_id, client_identity); + // Load TA context with parameters for OpenSession - pass actual session_id loaded_program.entrypoints.as_ref().ok_or_else(|| { // Safety: We are about to tear down this TA instance; @@ -814,7 +809,7 @@ fn open_session_new_instance( .unwrap() .load_ta_context( params, - Some(runner_session_id), + runner_session_id, UteeEntryFunc::OpenSession as u32, None, ) @@ -983,7 +978,7 @@ fn handle_invoke_command( entrypoints_ref .load_ta_context( params.as_slice(), - Some(session_id), + session_id, UteeEntryFunc::InvokeCommand as u32, Some(cmd_id), ) @@ -1095,7 +1090,7 @@ fn handle_close_session( .unwrap() .load_ta_context( &[], - Some(session_id), + session_id, UteeEntryFunc::CloseSession as u32, None, ) diff --git a/litebox_runner_optee_on_linux_userland/src/lib.rs b/litebox_runner_optee_on_linux_userland/src/lib.rs index ae37334cd4..e2f0ea7d0b 100644 --- a/litebox_runner_optee_on_linux_userland/src/lib.rs +++ b/litebox_runner_optee_on_linux_userland/src/lib.rs @@ -5,7 +5,7 @@ use anyhow::{Context as _, Result}; use clap::Parser; use litebox_common_optee::{TeeUuid, UteeEntryFunc, UteeParamOwned}; use litebox_platform_multiplex::Platform; -use litebox_shim_optee::session::SessionManager; +use litebox_shim_optee::session::session_manager; use std::path::PathBuf; mod tests; @@ -109,21 +109,14 @@ fn run_ta_with_default_commands( ldelf_bin: &[u8], ta_bin: &[u8], ) { - let session_manager = SessionManager::new(); for func_id in [UteeEntryFunc::OpenSession, UteeEntryFunc::CloseSession] { let params = [const { UteeParamOwned::None }; UteeParamOwned::TEE_NUM_PARAMS]; if func_id == UteeEntryFunc::OpenSession { - let session_token = session_manager.try_acquire_open_session_token().unwrap(); + let session_token = session_manager().try_acquire_open_session_token().unwrap(); let session_id = session_token.session_id().unwrap(); let loaded_program = shim - .load_ldelf( - ldelf_bin, - TeeUuid::default(), - Some(ta_bin), - None, - session_id, - ) + .load_ldelf(ldelf_bin, TeeUuid::default(), Some(ta_bin)) .map_err(|_| { panic!("Failed to load ldelf"); }) @@ -140,7 +133,7 @@ fn run_ta_with_default_commands( // loaded binary and heap. In that sense, we can create (and destroy) a stack // for each command freely. let _ = entrypoints - .load_ta_context(params.as_slice(), None, func_id as u32, None) + .load_ta_context(params.as_slice(), session_id, func_id as u32, None) .map_err(|_| { panic!("Failed to load TA context"); }); diff --git a/litebox_runner_optee_on_linux_userland/src/tests.rs b/litebox_runner_optee_on_linux_userland/src/tests.rs index 4b6ce03467..193bfc939b 100644 --- a/litebox_runner_optee_on_linux_userland/src/tests.rs +++ b/litebox_runner_optee_on_linux_userland/src/tests.rs @@ -10,7 +10,7 @@ use litebox::utils::TruncateExt; use litebox_common_optee::{ TeeIdentity, TeeLogin, TeeParamType, TeeUuid, UteeEntryFunc, UteeParamOwned, UteeParams, }; -use litebox_shim_optee::session::SessionManager; +use litebox_shim_optee::session::session_manager; use litebox_shim_optee::{LoadedProgram, UserConstPtr}; use serde::Deserialize; use std::path::PathBuf; @@ -28,7 +28,9 @@ pub fn run_ta_with_test_commands( serde_json::from_str(&json_str).unwrap() }; let mut ta_info: Option = None; - let session_manager = SessionManager::new(); + // The active session id for the TA. Set at OpenSession and reused for the + // subsequent InvokeCommand entries on the same persistent session. + let mut session_id: Option = None; for cmd in ta_commands { assert!( @@ -52,7 +54,9 @@ pub fn run_ta_with_test_commands( if func_id == UteeEntryFunc::OpenSession { let ta_head = litebox_common_optee::parse_ta_head(ta_bin) .expect("Failed to parse TA header from ta_bin"); - let session_token = session_manager.try_acquire_open_session_token().unwrap(); + let mut session_token = session_manager().try_acquire_open_session_token().unwrap(); + let open_session_id = session_token.session_id().unwrap(); + session_id = Some(open_session_id); // Emulate the client identity a real REE client would present. The // secure world normally derives this from the OpenSession meta // params; here the test file states it, defaulting to a `user` login. @@ -63,14 +67,9 @@ pub fn run_ta_with_test_commands( }, ClientIdentityJson::to_tee_identity, ); + session_manager().set_session_client_identity(open_session_id, Some(client_identity)); let loaded = shim - .load_ldelf( - ldelf_bin, - ta_head.uuid, - Some(ta_bin), - Some(client_identity), - session_token.session_id().unwrap(), - ) + .load_ldelf(ldelf_bin, ta_head.uuid, Some(ta_bin)) .map_err(|_| { panic!("Failed to load TA"); }) @@ -89,17 +88,28 @@ pub fn run_ta_with_test_commands( "ldelf exits with error: return_code={:#x}", ctx.rax ); + // The session persists across all commands, so disarm the token: + // its drop must not recycle the id or clear the client identity. + session_token.disarm(); } if let Some(info) = ta_info.as_mut() { // In OP-TEE TA, each command invocation is like (re)starting the TA with a new stack with // loaded binary and heap. In that sense, we can create (and destroy) a stack // for each command freely. + // `ta_info` is only `Some` after an OpenSession, which also sets + // `session_id`, so this command runs on that established session. + let session_id = session_id.expect("session id set by OpenSession"); let _ = info .entrypoints .as_ref() .unwrap() - .load_ta_context(params.as_slice(), None, func_id as u32, Some(cmd.cmd_id)) + .load_ta_context( + params.as_slice(), + session_id, + func_id as u32, + Some(cmd.cmd_id), + ) .map_err(|_| { panic!("Failed to load TA context"); }); diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index d9f218f0cc..5f64397228 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -228,28 +228,25 @@ pub struct OpteeShim(Arc); impl OpteeShim { /// Load the given `ldelf` binary into memory while making it ready to load the TA binary specified - /// by `ta_uuid` (and optionally `ta_bin`). `client` specifies the one requesting the TA load. + /// by `ta_uuid` (and optionally `ta_bin`). + /// + /// The loaded program is an *instance*: a single instance can serve many + /// sessions. The active session id is supplied per entry via + /// [`OpteeShimEntrypoints::load_ta_context`], and the caller's identity is + /// recorded per session in the session registry via + /// [`session::SessionManager::set_session_client_identity`]. pub fn load_ldelf( &self, ldelf_bin: &[u8], ta_uuid: TeeUuid, ta_bin: Option<&[u8]>, - client: Option, - session_id: u32, ) -> Result { let entrypoints = crate::OpteeShimEntrypoints { _not_send: core::marker::PhantomData, task: Task { global: self.0.clone(), thread: ThreadState::new(), - session_id, ta_app_id: ta_uuid, - // Fall back to the anonymous public identity when no client is - // supplied (matches OP-TEE OS / the Linux driver). - client_identity: client.unwrap_or(TeeIdentity { - login: TeeLogin::Public, - uuid: TeeUuid::NIL, - }), tee_cryp_state_map: TeeCrypStateMap::new(), tee_obj_map: TeeObjMap::new(), ta_handle_map: TaHandleMap::new(), @@ -316,7 +313,7 @@ impl OpteeShimEntrypoints { pub fn load_ta_context( &self, params: &[litebox_common_optee::UteeParamOwned], - session_id: Option, + session_id: u32, func_id: u32, cmd_id: Option, ) -> Result<(), loader::elf::ElfLoaderError> { @@ -326,10 +323,6 @@ impl OpteeShimEntrypoints { self.task.thread.init_state.set(init_state); Ok(()) } - - pub fn get_session_id(&self) -> u32 { - self.task.session_id - } } /// Information about a loaded TA program. @@ -762,7 +755,7 @@ impl Task { fn load_ta_context( &self, params: &[litebox_common_optee::UteeParamOwned], - session_id: Option, + session_id: u32, func_id: u32, cmd_id: Option, ) -> Result { @@ -794,13 +787,35 @@ impl Task { Ok(ThreadInitState::Ta { cmd_id: cmd_id.unwrap_or(0) as usize, params_address: ta_stack.get_params_address(), - session_id: session_id.unwrap_or(self.session_id) as usize, + session_id: session_id as usize, func_id: func_id as usize, entry_point: self.get_ta_entry_point(), stack_top: ta_stack.get_cur_stack_top(), }) } + /// The session id currently executing in this task, taken from the thread's + /// init state (set per entry by [`Self::load_ta_context`]). Returns `None` + /// outside a TA entry. + fn current_session_id(&self) -> Option { + match self.thread.init_state.get() { + ThreadInitState::Ta { session_id, .. } => Some(session_id.trunc()), + _ => None, + } + } + + /// The client identity of the session currently executing in this task. + /// Falls back to the anonymous public client outside a TA entry. + fn current_client_identity(&self) -> TeeIdentity { + self.current_session_id().map_or( + TeeIdentity { + login: TeeLogin::Public, + uuid: TeeUuid::NIL, + }, + |session_id| crate::session::session_manager().client_identity(session_id), + ) + } + /// Allocate the guest TLS for an OP-TEE TA. /// /// This function is required to overcome the compatibility issue coming from @@ -1303,16 +1318,14 @@ impl TaUuidMap { } } -/// TA/session-related information for the current task +/// Per-instance TA state which can be shared between sessions if it is +/// a single-instance TA. The active session id is carried per entry +/// (see [`Task::current_session_id`]). struct Task { global: Arc, thread: ThreadState, - /// Session ID - session_id: u32, /// TA UUID ta_app_id: TeeUuid, - /// Client identity (VTL0 process or another TA) - client_identity: TeeIdentity, /// TEE cryptography state map tee_cryp_state_map: TeeCrypStateMap, /// TEE object map @@ -1474,12 +1487,7 @@ mod test_utils { Task { global: self.clone(), thread: ThreadState::new(), - session_id: SessionIdPool::allocate().unwrap(), ta_app_id: TeeUuid::default(), - client_identity: TeeIdentity { - login: TeeLogin::Public, - uuid: TeeUuid::NIL, - }, tee_cryp_state_map: TeeCrypStateMap::new(), tee_obj_map: TeeObjMap::new(), ta_handle_map: TaHandleMap::new(), diff --git a/litebox_shim_optee/src/session.rs b/litebox_shim_optee/src/session.rs index 3e462da290..f530a13a22 100644 --- a/litebox_shim_optee/src/session.rs +++ b/litebox_shim_optee/src/session.rs @@ -106,12 +106,19 @@ use crate::{LoadedProgram, OpteeShim, SessionIdPool}; use alloc::sync::Arc; use core::sync::atomic::{AtomicBool, Ordering}; use hashbrown::{HashMap, HashSet}; -use litebox_common_optee::{OpteeSmcReturnCode, TaFlags, TeeUuid}; +use litebox_common_optee::{OpteeSmcReturnCode, TaFlags, TeeIdentity, TeeLogin, TeeUuid}; use spin::mutex::SpinMutex; /// Maximum number of concurrent TA instances to avoid out of memory situations. const MAX_TA_INSTANCES: usize = 16; +/// The anonymous public client identity. Used as the fallback when no per-session +/// identity is recorded, matching OP-TEE OS / the Linux driver. +const ANONYMOUS_CLIENT_IDENTITY: TeeIdentity = TeeIdentity { + login: TeeLogin::Public, + uuid: TeeUuid::NIL, +}; + /// A loaded TA instance. /// /// For single-instance TAs one instance is shared across all sessions; the @@ -414,6 +421,10 @@ impl Drop for SessionToken<'_> { if let Some(id) = self.active_session_id.take() { self.manager.active_sessions.lock().remove(&id); if self.owns_id_recycling { + // The session was never published (an OpenSession failure + // path), so the id is recycled here. Drop any client identity + // recorded for it so a future reuse of the id starts clean. + self.manager.clear_session_client_identity(id); recycle_session_id(id); } } @@ -463,6 +474,18 @@ pub struct SessionManager { /// Session ids currently being handled (Invoke/Close). Guards a session /// against concurrent SMC entry by another core that targets the same id. active_sessions: SpinMutex>, + /// Per-session client identity, Matching OP-TEE OS's `tee_ta_session.clnt_id`. + /// + /// Populated by the runner before the OpenSession entry point runs and + /// removed when the session is unregistered. + session_client_identities: SpinMutex>, +} + +/// Get the global session manager. +pub fn session_manager() -> &'static SessionManager { + static SESSION_MANAGER: once_cell::race::OnceBox = + once_cell::race::OnceBox::new(); + SESSION_MANAGER.get_or_init(|| alloc::boxed::Box::new(SessionManager::new())) } impl SessionManager { @@ -475,6 +498,7 @@ impl SessionManager { single_instance_locks: SpinMutex::new(HashMap::new()), ta_load_lock: AtomicBool::new(false), active_sessions: SpinMutex::new(HashSet::new()), + session_client_identities: SpinMutex::new(HashMap::new()), } } @@ -790,12 +814,37 @@ impl SessionManager { Ok(()) } + /// Record the client identity for `session_id`. + pub fn set_session_client_identity(&self, session_id: u32, identity: Option) { + self.session_client_identities + .lock() + .insert(session_id, identity.unwrap_or(ANONYMOUS_CLIENT_IDENTITY)); + } + + /// The client identity recorded for `session_id`, or the anonymous public + /// client if none was recorded. + pub fn client_identity(&self, session_id: u32) -> TeeIdentity { + self.session_client_identities + .lock() + .get(&session_id) + .copied() + .unwrap_or(ANONYMOUS_CLIENT_IDENTITY) + } + + /// Drop the recorded client identity for `session_id`. Used on OpenSession + /// rollback paths (the TA's OpenSession failed, so the session is never + /// published). The normal close path goes through [`Self::unregister_session`]. + pub fn clear_session_client_identity(&self, session_id: u32) { + self.session_client_identities.lock().remove(&session_id); + } + /// Unregister a session and recycle its session ID. Returns whether /// the session was registered and what flags it had (the latter for /// callers that need to dispatch on `is_single_instance` / /// `is_keep_alive` after removal). pub fn unregister_session(&self, session_id: u32) -> Option { let entry = self.sessions.remove(session_id); + self.session_client_identities.lock().remove(&session_id); if entry.is_some() { recycle_session_id(session_id); } diff --git a/litebox_shim_optee/src/syscalls/tee.rs b/litebox_shim_optee/src/syscalls/tee.rs index 1b5945c6ad..365f8487e8 100644 --- a/litebox_shim_optee/src/syscalls/tee.rs +++ b/litebox_shim_optee/src/syscalls/tee.rs @@ -95,7 +95,7 @@ impl Task { if prop_buf.len() < core::mem::size_of::() { return Err(TeeResult::ShortBuffer); } - let identity = self.client_identity; + let identity = self.current_client_identity(); prop_buf[..core::mem::size_of::()] .copy_from_slice(identity.as_bytes()); prop_len From c4c4184afb53bbd0add9d19613b115a332e9bee5 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 26 Jun 2026 13:32:58 +0000 Subject: [PATCH 4/9] nits --- litebox_common_optee/src/lib.rs | 2 +- litebox_runner_lvbs/src/lib.rs | 3 +++ litebox_shim_optee/src/session.rs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index 3214f1fd2f..9c5aea0cc2 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -626,7 +626,7 @@ pub struct TeeUuid { impl TeeUuid { /// The nil UUID (all zeros, RFC 4122 S4.1.7). /// - /// Used for anonymous clients (e.g. ,`TeeLogin::Public`) that carry no + /// Used for anonymous clients (e.g., `TeeLogin::Public`) that carry no /// REE-derived identity. pub const NIL: Self = Self { time_low: 0, diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index 13f0e71453..ac6a54c6b4 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -684,6 +684,9 @@ fn open_session_single_instance( teardown_ta_page_table(instance.shim(), task_pt_id); }; } else { + // The session id is forgotten (never recycled), so the token's drop + // won't clear the recorded identity. Remove the client identity here. + session_manager().clear_session_client_identity(runner_session_id); session_token.disarm(); } return Err(e); diff --git a/litebox_shim_optee/src/session.rs b/litebox_shim_optee/src/session.rs index f530a13a22..aa4f2c061c 100644 --- a/litebox_shim_optee/src/session.rs +++ b/litebox_shim_optee/src/session.rs @@ -474,7 +474,7 @@ pub struct SessionManager { /// Session ids currently being handled (Invoke/Close). Guards a session /// against concurrent SMC entry by another core that targets the same id. active_sessions: SpinMutex>, - /// Per-session client identity, Matching OP-TEE OS's `tee_ta_session.clnt_id`. + /// Per-session client identity, matching OP-TEE OS's `tee_ta_session.clnt_id`. /// /// Populated by the runner before the OpenSession entry point runs and /// removed when the session is unregistered. From f14c1a6e095377eca69876e7e183071b3434e257 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 26 Jun 2026 15:19:17 +0000 Subject: [PATCH 5/9] client_endian --- litebox_shim_optee/src/syscalls/tee.rs | 31 +++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/litebox_shim_optee/src/syscalls/tee.rs b/litebox_shim_optee/src/syscalls/tee.rs index 365f8487e8..088a05936a 100644 --- a/litebox_shim_optee/src/syscalls/tee.rs +++ b/litebox_shim_optee/src/syscalls/tee.rs @@ -106,6 +106,24 @@ impl Task { .ok_or(TeeResult::AccessDenied)?; Ok(()) } + GpdPropertyIndex::ClientEndian => { + const CLIENT_ENDIAN_LITTLE: u32 = 0; + if prop_set != TeePropSet::CurrentClient { + return Err(TeeResult::BadParameters); + } + if prop_buf.len() < core::mem::size_of::() { + return Err(TeeResult::ShortBuffer); + } + prop_buf[..core::mem::size_of::()] + .copy_from_slice(&CLIENT_ENDIAN_LITTLE.to_le_bytes()); + prop_len + .write_at_offset(0, core::mem::size_of::().trunc()) + .ok_or(TeeResult::AccessDenied)?; + prop_type + .write_at_offset(0, UserTaPropType::U32 as u32) + .ok_or(TeeResult::AccessDenied)?; + Ok(()) + } GpdPropertyIndex::CurrentTaUuid => { if prop_set != TeePropSet::CurrentTa { return Err(TeeResult::BadParameters); @@ -149,6 +167,16 @@ impl Task { Err(TeeResult::BadParameters) } } + "gpd.client.endian" => { + if prop_set == TeePropSet::CurrentClient { + index + .write_at_offset(0, GpdPropertyIndex::ClientEndian as u32) + .ok_or(TeeResult::AccessDenied)?; + Ok(()) + } else { + Err(TeeResult::BadParameters) + } + } "gpd.ta.appID" => { if prop_set == TeePropSet::CurrentTa { index @@ -299,6 +327,7 @@ impl Task { #[repr(u32)] pub enum GpdPropertyIndex { ClientIdentity = 0xffff_0000, - CurrentTaUuid = 0xffff_0001, + ClientEndian = 0xffff_0001, + CurrentTaUuid = 0xffff_0002, None = 0xffff_ffff, } From 70be3d17f95cfc18fcf9df73f749823c4f464b6c Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 26 Jun 2026 15:34:33 +0000 Subject: [PATCH 6/9] nits --- litebox_common_optee/src/lib.rs | 17 ++++++----------- .../src/tests.rs | 15 ++------------- litebox_shim_optee/src/lib.rs | 9 ++++----- litebox_shim_optee/src/session.rs | 8 ++++---- 4 files changed, 16 insertions(+), 33 deletions(-) diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index 9c5aea0cc2..d3426878a7 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -843,13 +843,10 @@ const TEE_LOGIN_APPLICATION: u32 = 0x4; const TEE_LOGIN_APPLICATION_USER: u32 = 0x5; const TEE_LOGIN_APPLICATION_GROUP: u32 = 0x6; const TEE_LOGIN_TRUSTED_APP: u32 = 0xf000_0000; -// Private OP-TEE login for in-kernel REE clients (from -// `tee_api_defines_extensions.h`). Userspace cannot send it. It uses -// the nil client UUID. +// Private OP-TEE login for in-kernel REE clients (`tee_api_defines_extensions.h`). const TEE_LOGIN_REE_KERNEL: u32 = 0x8000_0000; /// `TEE Login type` from `optee_os/lib/libutee/include/tee_api_defines.h` -/// (plus the `TEE_LOGIN_REE_KERNEL` extension). #[derive(Clone, Copy, PartialEq, Debug, TryFromPrimitive, Immutable, IntoBytes)] #[repr(u32)] pub enum TeeLogin { @@ -1486,9 +1483,8 @@ const OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: u8 = 0xb; // Note: `OPTEE_MSG_ATTR_TYPE_FMEM_*` are aliases of `OPTEE_MSG_ATTR_TYPE_RMEM_*`. // Whether it is RMEM of FMEM depends on the conduit. -/// Meta-parameter marker (bit 8) of the attribute word. Set on the `OpenSession` -/// TA-UUID and client-identity params, which are consumed by the secure world -/// and not delivered to the TA. +/// Meta-parameter marker of the attribute word. Set on the `OpenSession` +/// TA-UUID and client-identity params. const OPTEE_MSG_ATTR_META: u64 = 1 << 8; #[non_exhaustive] @@ -1789,10 +1785,9 @@ impl OpteeMsgArgs { /// Read a value parameter that must be tagged as an `OpenSession` meta parameter. /// /// `OpenSession` conveys the TA UUID and client identity in the first two - /// params, each marked exactly [`OpteeMsgAttr::META_VALUE_INPUT`]. This - /// mirrors OP-TEE OS `get_open_session_meta()`, which rejects the request - /// unless the full attr word of both meta params equals that value. Plain - /// `get_param_value` ignores these bits, so it must not be used for this. + /// params, each marked exactly [`OpteeMsgAttr::META_VALUE_INPUT`], mirroring + /// OP-TEE OS `get_open_session_meta()`. Plain `get_param_value` ignores + /// these bits, so it must not be used for this. pub fn get_meta_param_value( &self, index: usize, diff --git a/litebox_runner_optee_on_linux_userland/src/tests.rs b/litebox_runner_optee_on_linux_userland/src/tests.rs index 193bfc939b..645055431e 100644 --- a/litebox_runner_optee_on_linux_userland/src/tests.rs +++ b/litebox_runner_optee_on_linux_userland/src/tests.rs @@ -57,9 +57,7 @@ pub fn run_ta_with_test_commands( let mut session_token = session_manager().try_acquire_open_session_token().unwrap(); let open_session_id = session_token.session_id().unwrap(); session_id = Some(open_session_id); - // Emulate the client identity a real REE client would present. The - // secure world normally derives this from the OpenSession meta - // params; here the test file states it, defaulting to a `user` login. + // Emulate the client identity a real REE client would present. let client_identity = cmd.client_identity.as_ref().map_or( TeeIdentity { login: TeeLogin::User, @@ -197,12 +195,6 @@ pub struct TaCommandBase64 { cmd_id: u32, #[serde(default)] args: Vec, - /// Client identity presented for an `OpenSession`, mirroring the - /// `TEE_Identity` a real REE client would supply (the secure world derives - /// this from the message meta params; this userland harness has no such - /// client, so the test states it here). Ignored for other commands. When - /// absent it defaults to a `user` login with the nil UUID, which is what a - /// typical REE user client presents. #[serde(default)] client_identity: Option, } @@ -212,13 +204,11 @@ pub struct TaCommandBase64 { struct ClientIdentityJson { #[serde(default)] login: ClientLoginJson, - /// RFC 4122 UUID string (hyphens optional). Defaults to the nil UUID. #[serde(default)] uuid: Option, } -/// JSON mirror of [`TeeLogin`], so test files can name logins (e.g. `"user"`) -/// instead of raw `TEE_LOGIN_*` constants. +/// JSON mirror of [`TeeLogin`]. #[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(rename_all = "snake_case")] enum ClientLoginJson { @@ -261,7 +251,6 @@ impl ClientIdentityJson { } } -/// Parse an RFC 4122 UUID string (hyphens optional) into a [`TeeUuid`]. fn parse_uuid_or_panic(s: &str) -> TeeUuid { let hex: String = s.chars().filter(|&c| c != '-').collect(); assert_eq!(hex.len(), 32, "client uuid must be 32 hex digits: {s:?}"); diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index 5f64397228..7d979bd244 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -794,9 +794,8 @@ impl Task { }) } - /// The session id currently executing in this task, taken from the thread's - /// init state (set per entry by [`Self::load_ta_context`]). Returns `None` - /// outside a TA entry. + /// The session id currently executing in this task (set per entry by + /// [`Self::load_ta_context`]). Returns `None` outside a TA entry. fn current_session_id(&self) -> Option { match self.thread.init_state.get() { ThreadInitState::Ta { session_id, .. } => Some(session_id.trunc()), @@ -1319,8 +1318,8 @@ impl TaUuidMap { } /// Per-instance TA state which can be shared between sessions if it is -/// a single-instance TA. The active session id is carried per entry -/// (see [`Task::current_session_id`]). +/// a single-instance multi-session TA. The active session id is carried +/// per entry (see [`Task::current_session_id`]). struct Task { global: Arc, thread: ThreadState, diff --git a/litebox_shim_optee/src/session.rs b/litebox_shim_optee/src/session.rs index aa4f2c061c..5517d48d93 100644 --- a/litebox_shim_optee/src/session.rs +++ b/litebox_shim_optee/src/session.rs @@ -423,7 +423,7 @@ impl Drop for SessionToken<'_> { if self.owns_id_recycling { // The session was never published (an OpenSession failure // path), so the id is recycled here. Drop any client identity - // recorded for it so a future reuse of the id starts clean. + // recorded for it to avoid unnecessary memory leak. self.manager.clear_session_client_identity(id); recycle_session_id(id); } @@ -476,8 +476,8 @@ pub struct SessionManager { active_sessions: SpinMutex>, /// Per-session client identity, matching OP-TEE OS's `tee_ta_session.clnt_id`. /// - /// Populated by the runner before the OpenSession entry point runs and - /// removed when the session is unregistered. + /// Populated before the OpenSession entry point runs and removed when + /// the session is unregistered. session_client_identities: SpinMutex>, } @@ -823,7 +823,7 @@ impl SessionManager { /// The client identity recorded for `session_id`, or the anonymous public /// client if none was recorded. - pub fn client_identity(&self, session_id: u32) -> TeeIdentity { + pub(crate) fn client_identity(&self, session_id: u32) -> TeeIdentity { self.session_client_identities .lock() .get(&session_id) From 4fa2ddbac6aa77edff12640c1db0671e7712957f Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Wed, 1 Jul 2026 20:33:45 +0000 Subject: [PATCH 7/9] feedback --- litebox_shim_optee/src/session.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/litebox_shim_optee/src/session.rs b/litebox_shim_optee/src/session.rs index 5517d48d93..17fdc4edeb 100644 --- a/litebox_shim_optee/src/session.rs +++ b/litebox_shim_optee/src/session.rs @@ -831,9 +831,11 @@ impl SessionManager { .unwrap_or(ANONYMOUS_CLIENT_IDENTITY) } - /// Drop the recorded client identity for `session_id`. Used on OpenSession - /// rollback paths (the TA's OpenSession failed, so the session is never - /// published). The normal close path goes through [`Self::unregister_session`]. + /// Drop the recorded client identity for `session_id`. + /// + /// Called directly only on OpenSession rollback paths (the TA's OpenSession + /// failed, so the session is never published). The normal close path calls + /// this indirectly via [`Self::unregister_session`]. pub fn clear_session_client_identity(&self, session_id: u32) { self.session_client_identities.lock().remove(&session_id); } @@ -844,7 +846,7 @@ impl SessionManager { /// `is_keep_alive` after removal). pub fn unregister_session(&self, session_id: u32) -> Option { let entry = self.sessions.remove(session_id); - self.session_client_identities.lock().remove(&session_id); + self.clear_session_client_identity(session_id); if entry.is_some() { recycle_session_id(session_id); } From df3b1a5044cb5c88424784d51eef914ac9c2daa2 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Wed, 1 Jul 2026 20:44:26 +0000 Subject: [PATCH 8/9] rebase --- dev_tests/src/ratchet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_tests/src/ratchet.rs b/dev_tests/src/ratchet.rs index c94a856d64..68288b8e39 100644 --- a/dev_tests/src/ratchet.rs +++ b/dev_tests/src/ratchet.rs @@ -43,7 +43,7 @@ fn ratchet_globals() -> Result<()> { ("litebox_runner_lvbs/", 5), ("litebox_runner_snp/", 2), ("litebox_shim_linux/", 1), - ("litebox_shim_optee/", 4), + ("litebox_shim_optee/", 5), ], |file| { Ok(file From d0a75f02cdfa9ae4cff5760520ffd1b9e3ecf41d Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Wed, 1 Jul 2026 22:56:54 +0000 Subject: [PATCH 9/9] clear TA context and check meta bit --- litebox_common_optee/src/lib.rs | 4 ++++ litebox_shim_optee/src/lib.rs | 14 +++++++++++++- litebox_shim_optee/src/msg_handler.rs | 6 ++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index d3426878a7..aad03495e1 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -1549,6 +1549,10 @@ impl OpteeMsgParam { pub fn attr_type(&self) -> OpteeMsgAttrType { OpteeMsgAttrType::try_from(self.attr.attr_type()).unwrap_or(OpteeMsgAttrType::None) } + /// Returns `true` when the meta bit (bit 8) is set. + pub fn is_meta(&self) -> bool { + self.attr.meta() + } pub fn get_param_tmem(&self) -> Option { if matches!( self.attr.attr_type(), diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index 7d979bd244..e5aa2972de 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -80,6 +80,7 @@ impl litebox::shim::EnterShim for OpteeShimEntrypoints { return if result.is_ok() { ContinueOperation::Resume } else { + self.task.clear_ta_context(); ContinueOperation::Terminate }; } else if result.is_ok() { @@ -90,6 +91,7 @@ impl litebox::shim::EnterShim for OpteeShimEntrypoints { } // OP-TEE has no signal handling. Kill the TA on any non-PF exception. ctx.rax = (TeeResult::TargetDead as u32) as usize; + self.task.clear_ta_context(); ContinueOperation::Terminate } @@ -375,9 +377,11 @@ impl Task { if let SyscallRequest::Return { ret } = request { ctx.rax = self.sys_return(ret); + self.clear_ta_context(); return ContinueOperation::Terminate; } else if let SyscallRequest::Panic { code } = request { ctx.rax = self.sys_panic(code); + self.clear_ta_context(); return ContinueOperation::Terminate; } let res: Result<(), TeeResult> = match request { @@ -795,7 +799,8 @@ impl Task { } /// The session id currently executing in this task (set per entry by - /// [`Self::load_ta_context`]). Returns `None` outside a TA entry. + /// [`Self::load_ta_context`], cleared on entry termination by + /// [`Self::clear_ta_context`]). Returns `None` outside a TA entry. fn current_session_id(&self) -> Option { match self.thread.init_state.get() { ThreadInitState::Ta { session_id, .. } => Some(session_id.trunc()), @@ -815,6 +820,13 @@ impl Task { ) } + /// Clear the per-entry TA execution state once a TA entry has terminated. + fn clear_ta_context(&self) { + if matches!(self.thread.init_state.get(), ThreadInitState::Ta { .. }) { + self.thread.init_state.set(ThreadInitState::None); + } + } + /// Allocate the guest TLS for an OP-TEE TA. /// /// This function is required to overcome the compatibility issue coming from diff --git a/litebox_shim_optee/src/msg_handler.rs b/litebox_shim_optee/src/msg_handler.rs index 0a800d911b..303129239e 100644 --- a/litebox_shim_optee/src/msg_handler.rs +++ b/litebox_shim_optee/src/msg_handler.rs @@ -503,6 +503,12 @@ pub fn decode_ta_request( .skip(skip) .enumerate() { + // The meta bit marks the OpenSession TA-UUID/client-identity params, + // which were already consumed via `skip`. A client parameter must not + // carry it (mirrors OP-TEE OS `copy_in_params`). + if param.is_meta() { + return Err(OpteeSmcReturnCode::EBadCmd); + } ta_req_info.params[i] = match param.attr_type() { OpteeMsgAttrType::None => UteeParamOwned::None, OpteeMsgAttrType::ValueInput => {