Skip to content

Parse Security Profile descriptors - #3528

Merged
igaw merged 1 commit into
linux-nvme:masterfrom
flaviens:nbft-parse-security
Jul 28, 2026
Merged

Parse Security Profile descriptors#3528
igaw merged 1 commit into
linux-nvme:masterfrom
flaviens:nbft-parse-security

Conversation

@flaviens

@flaviens flaviens commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

This change parses NBFT Security Profile descriptors that are currently skipped by read_security().

The parser records the descriptor flags and secret type. Policy list references are exposed when their corresponding flags permit them. The optional secret key URI is exposed as well.

Range validation

This branch includes the range validation change from #3643 in the same commit. That change prevents wrapped descriptor offsets from reaching memory beyond the table. It also rejects an invalid Control Descriptor before a partly initialized NBFT object reaches connection handling.

All four descriptor readers share one helper for address calculation. Raw object lengths remain zero until their range check passes. The public structure states that its pointers belong to the parent NBFT object.

Tests

A valid Security Profile table checks the parsed fields and the SSNS reference. Two malformed tables cover the wrapped Security Profile list and the invalid Control Descriptor.

The production command completed without a signal in three runs for each malformed table. The same checks passed under AddressSanitizer with UndefinedBehaviorSanitizer.

Checkpatch reports zero errors and zero warnings.

Related to linux-nvme/libnvme#1111 and linux-nvme/libnvme#1112.

Copilot AI review requested due to automatic review settings July 1, 2026 06:02

Copilot AI 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.

Pull request overview

This PR adds parsing support for NBFT Security Profile descriptors in libnvme’s NBFT reader, exposing the descriptor’s flags/secret type and providing access to several heap-backed policy lists and the keypath URI.

Changes:

  • Extends struct libnbft_security to include descriptor flags, secret type, and heap-backed policy list/keypath fields.
  • Implements raw heap object extraction (__get_heap_obj_raw) for non-string heap objects (byte lists).
  • Implements read_security() to populate libnbft_security entries from raw NBFT security descriptors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
libnvme/src/nvme/nbft.h Extends the public libnbft_security structure with parsed Security Profile fields.
libnvme/src/nvme/nbft.c Adds raw heap object parsing helper and implements Security Profile descriptor parsing.

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

Comment thread libnvme/src/nvme/nbft.c Outdated
Comment thread libnvme/src/nvme/nbft.c
Comment on lines +133 to +144
*output = NULL;
*length = le16_to_cpu(obj.length);
if (*length == 0)
return 0;
if (!in_heap(header, obj)) {
libnvme_msg(ctx, LIBNVME_LOG_DEBUG,
"file %s: field '%s' in descriptor '%s' has invalid offset or length\n",
filename, fieldname, descriptorname);
return -EINVAL;
}
*output = (unsigned char *)header + le32_to_cpu(obj.offset);
return 0;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in the revised parser commit. The helper clears the pointer and length before checking the object. It assigns the length only after the range check succeeds.

Comment thread libnvme/src/nvme/nbft.h Outdated
/* TODO add fields */
__u16 flags;
__u8 secret_type;
/* list fields point into the raw NBFT; not separately freed */

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in the revised parser commit. The public structure now states that every pointer refers to storage owned by the parent NBFT object. This includes the secret key URI.

@flaviens

Copy link
Copy Markdown
Contributor Author

We found two crashes at commit c86bb84f4aacfde759c617d5306c4f44440ec17c.

The Security Profile list offset uses a 32-bit addition during its bounds check. A large offset can wrap to a small value. The production command then reads beyond the table and terminates with SIGSEGV. The 641-byte table has SHA-256 cce0fd1dfebb939d418e095862f2c5ec3f06541b84766504cfb6096e0d1f9f56.

An NBFT table with the Control Descriptor valid bit cleared is accepted as a partly initialized object. The added Security Profile code later reads its null Host ID. This case also terminates with SIGSEGV. The 641-byte table has SHA-256 9327c780a006d021997edc8dcf8f0fa903c9c224bbf49dc9a519f365c9e32950.

Each case terminated with SIGSEGV in all three release-build runs. Current master completes both commands without a signal because the Security Profile reader remains disabled.

I pushed a two-commit update on master at 962163f836cfe29be90a1766585da21e6f483210. The first commit contains the descriptor range validation from #3643. The second commit rebases the Security Profile parser and adds a valid-table regression test. It also addresses the three existing review comments.

After the update, each malformed table completed three release-build runs without a signal. The same runs completed under AddressSanitizer with UndefinedBehaviorSanitizer.

Once #3643 merges, the first commit can be removed during the rebase.

Comment thread libnvme/src/nvme/nbft.c Outdated
for (i = 0, cnt = 0; i < num_hfi; i++) {
if (read_hfi(ctx, nbft, &raw_hfi_array[i],
struct nbft_hfi *raw_hfi =
(struct nbft_hfi *)(raw_hfi_array + i * hfi_len);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would suggest to introduce a helper for this calculation and re-use it everywhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. All four descriptor readers now use one descriptor_at() helper to calculate each address from its recorded stride.

@flaviens
flaviens force-pushed the nbft-parse-security branch from e67a3fa to 13a1950 Compare July 27, 2026 14:33
@flaviens

Copy link
Copy Markdown
Contributor Author

The checkpatch warnings from run 30272756102 are fixed in 13a1950. Running the same command locally reports 0 errors and 0 warnings. The replacement GitHub run is waiting for maintainer approval.

@igaw

igaw commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Let's give @tbzatek a chance to look at the changes. As far I can tell all looks good.

@igaw igaw added this to the 3.0 milestone Jul 28, 2026
@igaw

igaw commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Once #3643 merges, the first commit can be removed during the rebase.

I've merged #3643, does this one need a rebase?

Security Profile descriptors are skipped by the current reader.

The associated subsystem records lose their security requirements.

Parse fields enabled by descriptor flags and keep inactive fields clear.

Add a valid parser fixture. Document pointer ownership.

Signed-off-by: Flavien Solt <flavien@nus.edu.sg>
@flaviens
flaviens force-pushed the nbft-parse-security branch from 13a1950 to bc35a2c Compare July 28, 2026 15:47
@flaviens

Copy link
Copy Markdown
Contributor Author

Yes. I rebased it onto current master in bc35a2c02. The branch now contains only the Security Profile parser change. All 18 NBFT tests pass. Checkpatch reports 0 errors and 0 warnings.

@igaw
igaw merged commit 29b151c into linux-nvme:master Jul 28, 2026
32 checks passed
@igaw

igaw commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Thanks a lot!

@tbzatek

tbzatek commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Let's give @tbzatek a chance to look at the changes. As far I can tell all looks good.

@tbzatek is not always around and @igaw is too fast...

So what's the purpose of the new __get_heap_obj_raw() function? Couldn't you just enhance existing __get_heap_obj() that shares most of the logic when is_string == FALSE with optional __u16 *length argument? You can also retrieve the length separately, if needed.

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.

4 participants