From: Jarkko Sakkinen <jarkko@kernel.org>
To: tpm-protocol@lists.linux.dev
Cc: tpm2@lists.linux.dev, Jarkko Sakkinen <jarkko@kernel.org>
Subject: [PATCH] perf(message): avoid buffer copy during command parsing
Date: Sun, 31 Aug 2025 18:01:45 +0300 [thread overview]
Message-ID: <20250831150145.386502-1-jarkko@kernel.org> (raw)
`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
next reply other threads:[~2025-08-31 15:02 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-31 15:01 Jarkko Sakkinen [this message]
2025-08-31 15:19 ` [PATCH] perf(message): avoid buffer copy during command parsing Jarkko Sakkinen
2025-08-31 15:35 ` Jarkko Sakkinen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250831150145.386502-1-jarkko@kernel.org \
--to=jarkko@kernel.org \
--cc=tpm-protocol@lists.linux.dev \
--cc=tpm2@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox