[PATCH v3] nvme: bound FDP config log reads to the received buffer
Kanchan Joshi
joshi.k at samsung.com
Tue Oct 13 02:46:13 PDT 2026
On 10/8/2026 11:54 PM, Yehyeong Lee wrote:
> v3: read the descriptor fields with get_unaligned_le*() since a device-supplied
> dsze can leave subsequent descriptors unaligned, which could fault on
> architectures without efficient unaligned access.
Were you able to test this part.
> v2: allocate the log buffer with kvzalloc() so that if the controller completes
> the Get Log Page with a short transfer, the uninitialized tail reads as zero
> and is rejected by the existing dsze check instead of being parsed (hardening).
> v1:https://lore.kernel.org/linux-nvme/20261008132604.1168105-1-
> yhlee at isslab.korea.ac.kr/
>
> drivers/nvme/host/core.c | 24 ++++++++++++++++++++----
> 1 file changed, 20 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
> index 9bcab3dc4c118..094df0e6ed326 100644
> --- a/drivers/nvme/host/core.c
> +++ b/drivers/nvme/host/core.c
> @@ -2272,13 +2272,17 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
> }
>
> size = le32_to_cpu(hdr.sze);
> + if (size < sizeof(*h)) {
header plus at least one descriptor is expected. So this can changed to:
if (size < sizeof(hdr) + sizeof(*desc)) {
> + dev_warn(ctrl->device, "FDP config log too small\n");
> + return 0;
> + }
> if (size > PAGE_SIZE * MAX_ORDER_NR_PAGES) {
> dev_warn(ctrl->device, "FDP config size too large:%zu\n",
> size);
> return 0;
> }
>
> - h = kvmalloc(size, GFP_KERNEL);
> + h = kvzalloc(size, GFP_KERNEL);
> if (!h)
> return -ENOMEM;
>
> @@ -2304,8 +2308,12 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
> desc = log;
> end = log + size - sizeof(*h);
> for (i = 0; i < fdp_idx; i++) {
> - u16 dsze = le16_to_cpu(desc->dsze);
> + u16 dsze;
>
> + if ((u8 *)desc + sizeof(*desc) > (u8 *)h + size)
> + goto short_desc;
This and other short_desc check can be covered by extending the existing
check below
> +
> + dsze = get_unaligned_le16(&desc->dsze);
> if (!dsze || log + dsze > end) {
here, by checking that next descriptor fits completely.
Also !dsze does not cover other invalid values from 1 to 63.
Overall, something like this:
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 7eb0f9fe6b18..9991cff5c332 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2273,13 +2273,19 @@ static int nvme_query_fdp_granularity(struct
nvme_ctrl *ctrl,
}
size = le32_to_cpu(hdr.sze);
+ if (size < sizeof(hdr) + sizeof(*desc)) {
+ dev_warn(ctrl->device, "FDP config size too small:%zu\n",
+ size);
+ return 0;
+ }
+
if (size > PAGE_SIZE * MAX_ORDER_NR_PAGES) {
dev_warn(ctrl->device, "FDP config size too large:%zu\n",
size);
return 0;
}
- h = kvmalloc(size, GFP_KERNEL);
+ h = kvzalloc(size, GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -2305,9 +2311,10 @@ static int nvme_query_fdp_granularity(struct
nvme_ctrl *ctrl,
desc = log;
end = log + size - sizeof(*h);
for (i = 0; i < fdp_idx; i++) {
- u16 dsze = le16_to_cpu(desc->dsze);
+ u16 dsze = get_unaligned_le16(&desc->dsze);
- if (!dsze || log + dsze > end) {
+ /* next descriptor must fit within the log */
+ if (dsze < sizeof(*desc) || log + dsze + sizeof(*desc) >
end) {
dev_warn(ctrl->device,
"FDP invalid config descriptor at
index %d\n", i);
ret = 0;
@@ -2317,13 +2324,13 @@ static int nvme_query_fdp_granularity(struct
nvme_ctrl *ctrl,
desc = log;
}
- if (le32_to_cpu(desc->nrg) > 1) {
+ if (get_unaligned_le32(&desc->nrg) > 1) {
dev_warn(ctrl->device, "FDP NRG > 1 not supported\n");
ret = 0;
goto out;
}
- info->runs = le64_to_cpu(desc->runs);
+ info->runs = get_unaligned_le64(&desc->runs);
out:
kvfree(h);
return ret;
More information about the Linux-nvme
mailing list