Skip to content

libnvme, nvme: guarantee terminated discovery log entry strings - #3842

Open
martin-belanger wants to merge 3 commits into
linux-nvme:masterfrom
martin-belanger:dlp-entry-buffer-safety
Open

libnvme, nvme: guarantee terminated discovery log entry strings#3842
martin-belanger wants to merge 3 commits into
linux-nvme:masterfrom
martin-belanger:dlp-entry-buffer-safety

Conversation

@martin-belanger

@martin-belanger martin-belanger commented Aug 14, 2026

Copy link
Copy Markdown

This is something I noticed while working on #3837.

Discovery Log Page Entry fields (trsvcid, subnqn, traddr) are fixed-size char arrays copied off the wire with nothing guaranteeing a NUL terminator -- trsvcid/traddr are normally space-padded per spec, not NUL-padded. Every consumer (strlen(), strdup(), printf("%s"), json_object_new_string()) assumes a real C string anyway. Usually harmless (the bytes right after a field are normally zero), but a DC that doesn't zero its reserved bytes can trigger a real out-of-bounds read, most concretely on the last entry's traddr. Not new -- traceable back to before sanitize_discovery_log_entry() even existed.

  1. shared: split shr_trim() into shr_rtrim() and shr_ltrim() -- needed to sanitize trsvcid, subnqn, traddr.
  2. libnvme: guarantee terminated discovery log entry strings -- force-NUL-terminate + shr_rtrim() each field, replacing the old strchomp().
  3. nvme: harden discovery log print paths against unterminated fields -- the discovery_log hook has always handed over the raw, unsanitized log page (required for nvme discover --raw to stay truly raw), so the stdout/JSON backends were never defended against missing NULL. log is now const throughout the hook chain to make sure the hook/callback cannot modify the log page.

Martin Belanger added 3 commits August 14, 2026 14:41
A caller with a fixed-size buffer field, not a char *, cannot use
shr_trim()'s leading-whitespace skip: it returns a pointer further
into the buffer instead of moving the content, which only works for
callers that reassign the result to a plain pointer variable.
Right-trim alone, done in place, works for both cases.

shr_trim() itself is unchanged: it still trims both ends and returns
a pointer to the first non-whitespace character, now composed from
the two new primitives instead of its own duplicate logic.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
Discovery Log Page Entry fields (trsvcid, subnqn, traddr) are
fixed-size char arrays with no guaranteed NUL terminator on the wire.
A non-compliant or malicious discovery controller that returns one
fully packed can trigger an out-of-bounds read wherever the field is
later passed to strlen()/strdup()/printf("%s"). This is part of
hardening the discovery log parsing path so invalid data from a
remote peer cannot crash the code.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
The discovery_log hook receives the log page exactly as fetched,
raw and unsanitized. trsvcid/traddr are raw buffers that are space
padded and don't have a NUL-terminating character.
nvme_show_discovery_log()'s stdout and JSON backends read that raw
trsvcid/traddr with strlen()-based string functions and no defense
against a missing NUL.

This rarely showed up as a visible crash because the bytes right
after them (reserved fields, the TSAS union) are normally zero, so
a read past the field usually finds a NUL nearby instead of running
away.

Each backend now defends itself instead of relying on sanitization
elsewhere in the discovery log parsing path. subnqn is zero-padded
and doesn't need this, but gets the same treatment anyway, as a
belt-and-suspenders measure.

The discovery_log hook's log parameter is now a pointer to const, so
the compiler enforces that a hook must never modify the log page it
was handed.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
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.

1 participant