public inbox for tpm2@lists.linux.dev
 help / color / mirror / Atom feed
* [PATCH] perf(message): avoid buffer copy during command parsing
@ 2025-08-31 15:01 Jarkko Sakkinen
  2025-08-31 15:19 ` Jarkko Sakkinen
  0 siblings, 1 reply; 3+ messages in thread
From: Jarkko Sakkinen @ 2025-08-31 15:01 UTC (permalink / raw)
  To: tpm-protocol; +Cc: tpm2, Jarkko Sakkinen

`tpm_parse_command` copies handles and parameters to a fixed-size temporary
4 KiB buffer on the stack before invoking a command parser.

Introduce `TpmParseCommandBody` trait implemented by `tpm_struct!`,
providing accessors for handles and parameters in the original command
buffer, thus zeroing out the large stack allocation.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
I plan to backport this also to 0.10.x branch, given signficancy.
 src/macro/mod.rs     |  7 +++++--
 src/macro/struct.rs  | 20 +++++++++++++++-----
 src/message/mod.rs   | 14 +++++++++++++-
 src/message/parse.rs | 15 +++------------
 4 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/src/macro/mod.rs b/src/macro/mod.rs
index 2ca6a15..782d3f7 100644
--- a/src/macro/mod.rs
+++ b/src/macro/mod.rs
@@ -150,7 +150,10 @@ macro_rules! tpm_dispatch {
                     <$value as $crate::message::TpmHeader>::NO_SESSIONS,
                     <$value as $crate::message::TpmHeader>::WITH_SESSIONS,
                     <$value as $crate::message::TpmHeader>::HANDLES,
-                    |buf| <$value>::parse(buf).map(|(c, r)| (TpmCommandBody::$name(c), r)),
+                    |handles, params| {
+                        <$value as $crate::message::TpmCommandBodyParse>::parse_body(handles, params)
+                            .map(|(c, r)| (TpmCommandBody::$name(c), r))
+                    },
                 )
             };
         }
@@ -199,7 +202,7 @@ macro_rules! tpm_dispatch {
             )*
         }
 
-        pub type TpmCommandParser = for<'a> fn(&'a [u8]) -> $crate::TpmResult<(TpmCommandBody, &'a [u8])>;
+        pub type TpmCommandParser = for<'a> fn(&'a [u8], &'a [u8]) -> $crate::TpmResult<(TpmCommandBody, &'a [u8])>;
         pub type TpmResponseParser = for<'a> fn(&'a [u8]) -> $crate::TpmResult<(TpmResponseBody, &'a [u8])>;
 
         pub(crate) static PARSE_COMMAND_MAP: &[($crate::data::TpmCc, bool, bool, usize, TpmCommandParser)] =
diff --git a/src/macro/struct.rs b/src/macro/struct.rs
index d9bc593..01e2cfb 100644
--- a/src/macro/struct.rs
+++ b/src/macro/struct.rs
@@ -55,18 +55,28 @@ macro_rules! tpm_struct {
             }
         }
 
-        impl $crate::TpmParse for $name {
+        impl $crate::message::TpmCommandBodyParse for $name {
             #[allow(unused_mut)]
-            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
-                let mut cursor = buf;
+            fn parse_body<'a>(
+                handles: &'a [u8],
+                params: &'a [u8],
+            ) -> $crate::TpmResult<(Self, &'a [u8])> {
+                let mut cursor = handles;
                 $(
-                    let ($handle_field, tail) = <$handle_type>::parse(cursor)?;
+                    let ($handle_field, tail) = <$handle_type as $crate::TpmParse>::parse(cursor)?;
                     cursor = tail;
                 )*
+
+                if !cursor.is_empty() {
+                    return Err($crate::TpmErrorKind::TrailingData);
+                }
+
+                let mut cursor = params;
                 $(
-                    let ($param_field, tail) = <$param_type>::parse(cursor)?;
+                    let ($param_field, tail) = <$param_type as $crate::TpmParse>::parse(cursor)?;
                     cursor = tail;
                 )*
+
                 Ok((
                     Self {
                         $($handle_field,)*
diff --git a/src/message/mod.rs b/src/message/mod.rs
index aff0933..eb0d3f0 100644
--- a/src/message/mod.rs
+++ b/src/message/mod.rs
@@ -52,7 +52,7 @@ pub type TpmAuthCommands = TpmList<data::TpmsAuthCommand, MAX_SESSIONS>;
 /// A fixed-capacity list for response authorization sessions.
 pub type TpmAuthResponses = TpmList<data::TpmsAuthResponse, MAX_SESSIONS>;
 /// A trait for TPM commands and responses that provides header information.
-pub trait TpmHeader: TpmBuild + TpmParse + Debug {
+pub trait TpmHeader: TpmBuild + Debug {
     const COMMAND: data::TpmCc;
     const NO_SESSIONS: bool;
     const WITH_SESSIONS: bool;
@@ -77,6 +77,18 @@ pub trait TpmHeaderCommand: TpmHeader {
     fn build_parameters(&self, writer: &mut TpmWriter) -> TpmResult<()>;
 }
 
+/// Parses a command body from the slices point out to the handle area and
+/// parameter area of the original buffer.
+pub(crate) trait TpmCommandBodyParse: Sized {
+    /// Parses the command body from the handle and parameter area.
+    ///
+    /// # Errors
+    ///
+    /// * `TpmErrorKind::ParseCapacity` if the capacity limit is exceeded
+    /// * `TpmErrorKind::ParseUnderflow` if the parser runs out of bytes
+    fn parse_body<'a>(handles: &'a [u8], params: &'a [u8]) -> TpmResult<(Self, &'a [u8])>;
+}
+
 pub const TPM_HEADER_SIZE: usize = 10;
 
 tpm_dispatch! {
diff --git a/src/message/parse.rs b/src/message/parse.rs
index cc63802..f55d7a8 100644
--- a/src/message/parse.rs
+++ b/src/message/parse.rs
@@ -8,7 +8,7 @@ use super::{
 };
 use crate::{
     data::{TpmCc, TpmRc, TpmSt, TpmsAuthCommand, TpmsAuthResponse},
-    TpmErrorKind, TpmNotDiscriminant, TpmParse, TpmResult, TPM_MAX_COMMAND_SIZE,
+    TpmErrorKind, TpmNotDiscriminant, TpmParse, TpmResult,
 };
 use core::{convert::TryFrom, mem::size_of};
 
@@ -94,18 +94,9 @@ pub fn tpm_parse_command(buf: &[u8]) -> TpmResult<(TpmHandles, TpmCommandBody, T
         after_handles
     };
 
-    let mut temp_body_buf = [0u8; TPM_MAX_COMMAND_SIZE];
-    let full_body_len = handle_area.len() + param_area.len();
-    if full_body_len > temp_body_buf.len() {
-        return Err(TpmErrorKind::ParseCapacity);
-    }
-    let full_body_for_parser = &mut temp_body_buf[..full_body_len];
-    full_body_for_parser[..handle_area.len()].copy_from_slice(handle_area);
-    full_body_for_parser[handle_area.len()..].copy_from_slice(param_area);
-
-    let (command_data, remainder) = (dispatch.4)(full_body_for_parser)?;
+    let (command_data, param_remainder) = (dispatch.4)(handle_area, param_area)?;
 
-    if !remainder.is_empty() {
+    if !param_remainder.is_empty() {
         return Err(TpmErrorKind::TrailingData);
     }
 
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-08-31 15:35 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-31 15:01 [PATCH] perf(message): avoid buffer copy during command parsing Jarkko Sakkinen
2025-08-31 15:19 ` Jarkko Sakkinen
2025-08-31 15:35   ` Jarkko Sakkinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox