[PATCH v2] nvmet: accept ANA group ID NVMET_MAX_ANAGRPS in configfs

Andrew Stellman astellman at stellman-greene.com
Fri Nov 6 05:43:13 PST 2026


nvmet_ns_ana_grpid_store() accepts an ANA group ID in the range
1..NVMET_MAX_ANAGRPS, and nvmet_ana_groups_make_group() accepts
2..NVMET_MAX_ANAGRPS (group 1 is the default group).  Both then pass the
ID through array_index_nospec() with NVMET_MAX_ANAGRPS as the size.
That helper treats the size as an exclusive upper bound, so the maximum
valid ID, 128, is rewritten to 0.

nvmet_ana_group_enabled[] is NVMET_MAX_ANAGRPS + 1 entries wide, so
index 128 is a valid slot, and the controller advertises ANAGRPMAX =
NVMET_MAX_ANAGRPS.  NVMe Base Specification 2.4, section 8.1.1
(Asymmetric Namespace Access Reporting), "ANA Groups", defines a valid
ANA Group Identifier as "a non-zero value that is less than or equal to
ANAGRPMAX".

There are two visible effects.  Writing 128 to a namespace's ana_grpid
succeeds but the namespace lands in the reserved group 0, which Identify
Namespace then reports.  Creating and removing ports/N/ana_groups/128
increments slot 0 on create but decrements slot 128 on release, leaving
nvmet_ana_group_enabled[128] at 0xffffffff, so every subsequent ANA log
page reports a group 128 with no namespaces in the Inaccessible state.

Use NVMET_MAX_ANAGRPS + 1 as the array_index_nospec() bound at both
sites, matching the array's actual size.  A namespace assigned to group
128 now follows the same path as any other group: its ANA state is
Inaccessible until ports/N/ana_groups/128 is created and configured,
where before it was placed in group 0, whose ana_state entry is never
set.  That entry stays 0, which isn't a valid ANA state, so
nvmet_check_ana_state() let I/O through.

Tested on v7.3-rc1 with and without this patch, in an arm64 QEMU guest
with nvmet over NVMe/TCP to 127.0.0.1.  Before: ana_grpid written as 128
reads back 0, and the ANA log after mkdir+rmdir of ana_groups/128 lists
group 128 with nnsids 0, state inaccessible.  After: ana_grpid reads
back 128 and the ANA log lists only group 1.

The issue was found by Claude Opus 5 running Quality Playbook, an
LLM-driven code review tool:
https://github.com/andrewstellman/quality-playbook
The fix and this changelog were drafted with the same tool and reviewed
by me.

Fixes: 20dc66f2d76b ("nvme: prevent potential spectre v1 gadget")
Signed-off-by: Andrew Stellman <astellman at stellman-greene.com>
---
v2:
- Rebased onto next-20261105.  No code change from v1.  On this tree
  it's only build-tested (nvmet as a module, W=1, no new warnings); the
  runtime testing in the changelog was done on v1.
- Changelog: noted that ana_groups/ accepts 2..NVMET_MAX_ANAGRPS, named
  what the tool drafted, and dropped the Assisted-by: trailer, which
  checkpatch now warns is a non-standard signature.
- Sagi asked on v1 whether this had been tested, and I didn't answer at
  the time, sorry.  Yes: the test described in the changelog was run on
  v7.3-rc1 in an arm64 QEMU guest over NVMe/TCP, with two kernels built
  from the same tree that differ only in these two lines.
- Yuehong Wu sent the same fix on next-20261027 and tested it over
  nvme-loop with the same results:
  https://lore.kernel.org/linux-nvme/?q=s%3A%22clamp+the+last+ANA+group+index+against+the+real+array+size%22
  He said he has no objection to the original being taken instead.
  Thanks, Yuehong.  If you're happy to reply with a Tested-by, it can be
  picked up when this is applied.

v1: https://lore.kernel.org/linux-nvme/20260910154849.66095-1-astellman@stellman-greene.com/

 drivers/nvme/target/configfs.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index b3a5242d0..85dda0ca7 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -709,7 +709,7 @@ static ssize_t nvmet_ns_ana_grpid_store(struct config_item *item,
 
 	down_write(&nvmet_ana_sem);
 	oldgrpid = ns->anagrpid;
-	newgrpid = array_index_nospec(newgrpid, NVMET_MAX_ANAGRPS);
+	newgrpid = array_index_nospec(newgrpid, NVMET_MAX_ANAGRPS + 1);
 	nvmet_ana_group_enabled[newgrpid]++;
 	ns->anagrpid = newgrpid;
 	nvmet_ana_group_enabled[oldgrpid]--;
@@ -2084,7 +2084,7 @@ static struct config_group *nvmet_ana_groups_make_group(
 	grp->grpid = grpid;
 
 	down_write(&nvmet_ana_sem);
-	grpid = array_index_nospec(grpid, NVMET_MAX_ANAGRPS);
+	grpid = array_index_nospec(grpid, NVMET_MAX_ANAGRPS + 1);
 	nvmet_ana_group_enabled[grpid]++;
 	up_write(&nvmet_ana_sem);
 

base-commit: ddfe51b0a0f5dc9e415cc2c8ad8db17532dd426b
-- 
2.43.0




More information about the Linux-nvme mailing list