[PATCH] nvmet: clamp the last ANA group index against the real array size
Yuehong Wu
wuyuehong at cmss.chinamobile.com
Thu Nov 5 22:40:48 PST 2026
NVMET_MAX_ANAGRPS is the highest valid ANA group id, not the size of
nvmet_ana_group_enabled[]: the array has NVMET_MAX_ANAGRPS + 1 entries
and the controller advertises anagrpmax == NVMET_MAX_ANAGRPS. Both
array_index_nospec() calls however pass NVMET_MAX_ANAGRPS as the size,
so for the last group the mask is 0 and the index quietly becomes 0:
- nvmet_ana_groups_make_group() increments enabled[0] while the group
keeps grpid == 128, so the later nvmet_ana_group_release() decrements
enabled[128], which underflows to 0xffffffff. The ANA log page then
reports group 128 as inaccessible forever even though the configfs
group no longer exists, and the increment on enabled[0] is never
undone.
- nvmet_ns_ana_grpid_store() accepts 128, stores 0 in ns->anagrpid and
returns success, so the namespace silently stays in the default ANA
group instead of the one the user asked for.
Both failures are silent, and they change what the target reports in its
ANA log page, which is what a host uses to select paths.
Pass the real array size in both calls, so the masking is kept but the
last group is no longer folded onto index 0.
Fixes: 20dc66f2d76b ("nvme: prevent potential spectre v1 gadget")
Cc: stable at vger.kernel.org
Signed-off-by: Yuehong Wu <wuyuehong at cmss.chinamobile.com>
---
Verified with nvme-loop in QEMU/KVM on 7.3.0-next-20261027, with two
kernels built from the same tree and differing only in these two lines
(no instrumentation):
- before the patch, a group created as ports/1/ana_groups/128 is absent
from the ANA log page (ngrps=1) even though configfs accepted it, and
it is reported as expected afterwards (ngrps=2, group 128 optimized);
- 128 written to a namespace ana_grpid reads back as 0 before the patch
and as 128 after it, and the namespace is accounted to group 128;
- removing the group again adds a phantom group 128 (state inaccessible,
0 namespaces) to the ANA log page before the patch, and leaves no
trace after it, even when creating and removing it repeatedly.
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 8aea64d..48d3f68 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -701,7 +701,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]--;
@@ -2074,7 +2074,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);
--
2.43.0
More information about the Linux-nvme
mailing list