Skip to content

Stop READTRUSTED transmitting unwritten response bytes#459

Open
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:mainfrom
yosuke-wolfssl:fix/f_6020
Open

Stop READTRUSTED transmitting unwritten response bytes#459
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:mainfrom
yosuke-wolfssl:fix/f_6020

Conversation

@yosuke-wolfssl

@yosuke-wolfssl yosuke-wolfssl commented Jul 17, 2026

Copy link
Copy Markdown

Problem

The non-DMA READTRUSTED handler sized its response from a length the callee never
resolved. When wh_Server_CertReadTrusted() failed, no certificate data was staged, but
the handler still ran resp.cert_len = cert_len and
*out_resp_size = sizeof(resp) + resp.cert_len, so the server transmitted response-packet
bytes it had never written.

The handler's return code does not prevent this: wh_Server_HandleRequestMessage() sends
the response regardless (src/wh_server.c:720-726), and the response packet is the same
buffer that carried the request, so the untouched region holds prior comm-buffer contents.

Measured before the fix: a stored 8185-byte certificate produced resp_size = 8192 (the
whole comm buffer), of which 8184 bytes were never written. The correct response is the
8-byte header alone.

Addressed by f_6020.

Root cause

wh_Server_CertReadTrusted() returned WH_ERROR_BUFFER_SIZE before assigning
*inout_cert_len, contradicting its contract in wolfhsm/wh_server_cert.h:70-72, so the
handler had only the staging clamp to report. The handler then trusted that value
unconditionally, which also leaked on internal wh_Nvm_GetMetadata() and wh_Nvm_Read()
failures.

Fix (src/wh_server_cert.c)

  • wh_Server_CertReadTrusted() reports meta.len on both the success and
    BUFFER_SIZE paths, honouring its documented contract. The caller's buffer size is
    captured first so the size check is unaffected.
  • The handler sets resp.cert_len only for outcomes that resolve a length (OK or
    BUFFER_SIZE), and adds the payload to *out_resp_size only on success. Every other
    outcome yields a header-only response.

Other callers (CertVerifyMultiRoot, CertVerifyAcert, the DMA handler) never read the
length on a failure return, so the contract change is safe for them.

Tests

New test-refactor/server/wh_test_cert_readtrusted.c drives the handler directly against a
poisoned response packet, pinning the clamp from both sides: STAGED_LEN must read back
whole, STAGED_LEN + 1 must report the stored size with a header-only response and no byte
past the header touched. A second sub-test locks the WH_ERROR_ACCESS path to no length and
no payload. It is gated only on WOLFHSM_CFG_CERTIFICATE_MANAGER, so it runs in every
config.

The oversized case was confirmed to fail against the unfixed server, so it is a negative
control. The ACCESS case passes either way and is kept as a forward-looking guard.

Verification

  • test-refactor default: 44 passed, 26 skipped, 0 failed of 70.
  • test-refactor DMA=1 ASAN=1: 48 passed, 22 skipped, 0 failed of 70.
  • Legacy test/ DMA=1 ASAN=1: passing, unchanged by this PR.

Not in this PR

  • A client-side end-to-end test was dropped per review. Reaching the over-clamp window from
    the client requires either the inline add or the DMA add depending on COMM_DATA_LEN vs
    WOLFHSM_CFG_MAX_CERT_SIZE, and those two conditions are mutually exclusive — no single
    config can exercise both, so the test necessarily carried an arm that no build compiles.
    The server-side test covers the same contract in every config.

@yosuke-wolfssl yosuke-wolfssl self-assigned this Jul 17, 2026
Copilot AI review requested due to automatic review settings July 17, 2026 08:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an information-leak/response-corruption issue in the non-DMA READTRUSTED certificate handler where the server could transmit bytes from the shared comm buffer that were never written (e.g., after failures), by tightening length reporting and response sizing. It also updates wh_Server_CertReadTrusted() to honor its documented contract by reporting the stored certificate length on both success and WH_ERROR_BUFFER_SIZE.

Changes:

  • Update wh_Server_CertReadTrusted() to always report meta.len via *inout_cert_len, including on WH_ERROR_BUFFER_SIZE.
  • Fix non-DMA READTRUSTED response construction to only include payload bytes in *out_resp_size on success, and only set resp.cert_len when a length is actually resolved.
  • Add targeted regression tests (server handler-level poisoning test + client API end-to-end coverage under DMA).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/wh_server_cert.c Fixes the wh_Server_CertReadTrusted() length contract and prevents unwritten comm-buffer bytes from being included in READTRUSTED responses.
test-refactor/server/wh_test_cert_readtrusted.c Adds direct handler tests that poison the response packet and assert header-only sizing/no writes past header on non-success paths.
test-refactor/client-server/wh_test_client_certs.c Adds DMA-gated end-to-end client test asserting the reported stored length is preserved on WH_ERROR_BUFFER_SIZE.
test-refactor/wh_test_list.c Registers the new whTest_CertReadTrusted test in the server test group.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #459

Scan targets checked: wolfhsm-core-bugs, wolfhsm-src

No new issues found in the changed files. ✅

Frauschi
Frauschi previously approved these changes Jul 23, 2026
WH_TEST_RETURN_ON_FAIL(wh_Client_CertAddTrustedDma(
ctx, cert_id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONE, NULL, 0,
big_cert, stored_len, &out_rc));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm I don't like this: the particular add method (DMA vs regular) this test uses is decided at compile time. With our test config it will always takes the inline path, so the DMA path actually never actually runs in any build we test. The only way to hit it would be rebuilding with a different WOLFHSM_CFG_MAX_CERT_SIZE. I don't want a test that behaves differently depending on the build config, and I also don't want to merge test code that never runs. The server-side test already covers this fix in every config, so IMO this client-side test should just be removed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing.
You're right, and it's worse than "never runs in our config" — I checked the bounds:

Path Bound DMA=1 test config
inline add COMM_DATA_LEN - sizeof(AddTrustedRequest) 8156
DMA add WOLFHSM_CFG_MAX_CERT_SIZE 4096
READTRUSTED clamp min(MAX_CERT_SIZE, COMM_DATA_LEN - 8) 4096

Defining WOLFHSM_CFG_DMA is exactly what drops MAX_CERT_SIZE to 4096, which makes it the
binding bound, which puts stored_len (4097) inside inline_max (8156) — so the gate that
compiles the test in is the same thing that guarantees the inline arm is selected. The DMA
arm only wins when the clamp is transport-bound, i.e. the non-DMA build, where the whole
function is preprocessed away.

That also rules out just making the branch config-independent: the DMA add can outrun the
clamp only when COMM_DATA_LEN < MAX_CERT_SIZE + 8, and the inline add only when
MAX_CERT_SIZE < COMM_DATA_LEN - 36. Those are mutually exclusive, so no single config can
exercise both and any version of this test carries a dead arm.

Removed the client-side test as you suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants