@samitouri / QOSamiQemu / commits / ec917cd499

hw/nvme: fix FDP set FDP events

Addresses an issue reported whereby user-provided event type values could trigger two issues: 1. if provided event_type == 0xff -> out-of-bounds access 2. if provided event_type > 7 -> generate a value too large for the u8 event mask. This patch fixes (1) by correctly adjusting the length of the look-up array to be 256 values. This patch fixes (2) by: a. changing the event_type mask to 64bit, matching NvmeRuHandle.event_filter b. Matching the behavior of Get Feature - FDP Events by skipping event type values which we do not support. 5.2.26.1.21 of the 2.3 Base specification does not explicitly tell us to reject unsupported event type values. c. Documenting in the event type lookup table, that supporting event types greater than 63 requires refactoring the masking code. Cc: qemu-stable@nongnu.org Reported-by: jaeyeong <fin@spl.team> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3631 Signed-off-by: Jesper Wendel Devantier <foss@defmacro.it> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com>

Jesper Wendel Devantier committed May 20, 2026 at 09:35 UTC ec917cd49918b6135546b4f6c02658a1913e3d7c
2 files changed +24 -7
hw/nvme/ctrl.c
+16 -6
@@ -6244,10 +6244,6 @@ static uint16_t nvme_get_feature_fdp_events(NvmeCtrl *n, NvmeNamespace *ns,
6244 for (uint8_t event_type = 0; event_type < FDP_EVT_MAX; event_type++) {
6245 uint8_t shift = nvme_fdp_evf_shifts[event_type];
6246 if (!shift && event_type) {
6247 - /*
6248 - * only first entry (event_type == 0) has a shift value of 0
6249 - * other entries are simply unpopulated.
6250 - */
6247 continue;
6248 }
6249
@@ -6492,9 +6488,9 @@ static uint16_t nvme_set_feature_fdp_events(NvmeCtrl *n, NvmeNamespace *ns,
6488 uint8_t noet = (cdw11 >> 16) & 0xff;
6489 uint16_t ret, ruhid;
6490 uint8_t enable = le32_to_cpu(cmd->cdw12) & 0x1;
6495 - uint8_t event_mask = 0;
6491 + uint64_t event_mask = 0;
6492 unsigned int i;
6497 - g_autofree uint8_t *events = g_malloc0(noet);
6493 + g_autofree uint8_t *events = NULL;
6494 NvmeRuHandle *ruh = NULL;
6495
6496 assert(ns);
@@ -6507,15 +6503,29 @@ static uint16_t nvme_set_feature_fdp_events(NvmeCtrl *n, NvmeNamespace *ns,
6503 return NVME_INVALID_FIELD | NVME_DNR;
6504 }
6505
6506 + if (unlikely(noet == 0)) {
6507 + return NVME_SUCCESS;
6508 + }
6509 +
6510 ruhid = ns->fdp.phs[ph];
6511 ruh = &n->subsys->endgrp.fdp.ruhs[ruhid];
6512
6513 + events = g_malloc0(noet);
6514 +
6515 ret = nvme_h2c(n, events, noet, req);
6516 if (ret) {
6517 return ret;
6518 }
6519
6520 for (i = 0; i < noet; i++) {
6521 + /*
6522 + * We ignore requests to enable tracking of unsupported FDP event types
6523 + */
6524 + uint8_t event_type = events[i];
6525 + uint8_t shift = nvme_fdp_evf_shifts[event_type];
6526 + if (!shift && event_type) {
6527 + continue;
6528 + }
6529 event_mask |= (1 << nvme_fdp_evf_shifts[events[i]]);
6530 }
6531
hw/nvme/nvme.h
+8 -1
@@ -160,7 +160,14 @@ typedef struct NvmeZone {
160 #define NVME_FDP_MAX_NS_RUHS 32u
161 #define FDPVSS 0
162
163 -static const uint8_t nvme_fdp_evf_shifts[FDP_EVT_MAX] = {
163 +/*
164 + * NOTE: Apart from event type 0, any event type with a shift value of 0 is
165 + * considered unsupported and thus skipped in get/set features calls.
166 + *
167 + * NOTE: NvmeRuHandle uses a 64bit event mask - refactor to support event types
168 + * of 63 or greater.
169 + */
170 +static const uint8_t nvme_fdp_evf_shifts[FDP_EVT_MAX + 1] = {
171 /* Host events */
172 [FDP_EVT_RU_NOT_FULLY_WRITTEN] = 0,
173 [FDP_EVT_RU_ATL_EXCEEDED] = 1,