From cab50d5adb84bc1315205399e4b9c7c724b5a729 Mon Sep 17 00:00:00 2001 From: Tony Hutter Date: Fri, 5 Jun 2026 09:48:55 -0700 Subject: [PATCH] Add additional verification of size fields and strings (#18623) - Check for size fields that convert to smaller integers. - Explicitly terminate bootenv string. - Initialize variables that could be returned in an error case. Reviewed-by: Brian Behlendorf Reviewed-by: Chris Longros Reviewed-by: Alexander Motin Signed-off-by: Tony Hutter Closes #18623 --- module/zfs/dmu_recv.c | 44 +++++++++++++++++++++++++++++------------ module/zfs/vdev_label.c | 1 + module/zfs/zfs_ioctl.c | 2 +- module/zfs/zfs_quota.c | 8 ++++++-- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/module/zfs/dmu_recv.c b/module/zfs/dmu_recv.c index fa18a2056bb..74874bb65d3 100644 --- a/module/zfs/dmu_recv.c +++ b/module/zfs/dmu_recv.c @@ -2901,16 +2901,20 @@ receive_read_record(dmu_recv_cookie_t *drc) { struct drr_object *drro = &drc->drc_rrd->header.drr_u.drr_object; - uint32_t size = DRR_OBJECT_PAYLOAD_SIZE(drro); + uint32_t size; void *buf = NULL; dmu_object_info_t doi; + size = DRR_OBJECT_PAYLOAD_SIZE(drro); + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + if (size != 0) - buf = kmem_zalloc(size, KM_SLEEP); + buf = vmem_zalloc(size, KM_SLEEP); err = receive_read_payload_and_next_header(drc, size, buf); if (err != 0) { - kmem_free(buf, size); + vmem_free(buf, size); return (err); } err = dmu_object_info(drc->drc_os, drro->drr_object, &doi); @@ -2934,7 +2938,11 @@ receive_read_record(dmu_recv_cookie_t *drc) case DRR_WRITE: { struct drr_write *drrw = &drc->drc_rrd->header.drr_u.drr_write; - int size = DRR_WRITE_PAYLOAD_SIZE(drrw); + uint64_t size = DRR_WRITE_PAYLOAD_SIZE(drrw); + + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + abd_t *abd = abd_alloc_linear(size, B_FALSE); err = receive_read_payload_and_next_header(drc, size, abd_to_buf(abd)); @@ -2951,12 +2959,18 @@ receive_read_record(dmu_recv_cookie_t *drc) { struct drr_write_embedded *drrwe = &drc->drc_rrd->header.drr_u.drr_write_embedded; - uint32_t size = P2ROUNDUP(drrwe->drr_psize, 8); - void *buf = kmem_zalloc(size, KM_SLEEP); + uint32_t size; + void *buf; + + size = P2ROUNDUP(drrwe->drr_psize, 8); + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + + buf = vmem_zalloc(size, KM_SLEEP); err = receive_read_payload_and_next_header(drc, size, buf); if (err != 0) { - kmem_free(buf, size); + vmem_free(buf, size); return (err); } @@ -2985,7 +2999,11 @@ receive_read_record(dmu_recv_cookie_t *drc) case DRR_SPILL: { struct drr_spill *drrs = &drc->drc_rrd->header.drr_u.drr_spill; - int size = DRR_SPILL_PAYLOAD_SIZE(drrs); + uint64_t size = DRR_SPILL_PAYLOAD_SIZE(drrs); + + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + abd_t *abd = abd_alloc_linear(size, B_FALSE); err = receive_read_payload_and_next_header(drc, size, abd_to_buf(abd)); @@ -3136,7 +3154,7 @@ receive_process_record(struct receive_writer_arg *rwa, abd_free(rrd->abd); rrd->abd = NULL; } else if (rrd->payload != NULL) { - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; } return (0); @@ -3150,7 +3168,7 @@ receive_process_record(struct receive_writer_arg *rwa, rrd->abd = NULL; rrd->payload = NULL; } else if (rrd->payload != NULL) { - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; } @@ -3163,7 +3181,7 @@ receive_process_record(struct receive_writer_arg *rwa, { struct drr_object *drro = &rrd->header.drr_u.drr_object; err = receive_object(rwa, drro, rrd->payload); - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; break; } @@ -3201,7 +3219,7 @@ receive_process_record(struct receive_writer_arg *rwa, struct drr_write_embedded *drrwe = &rrd->header.drr_u.drr_write_embedded; err = receive_write_embedded(rwa, drrwe, rrd->payload); - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; break; } @@ -3270,7 +3288,7 @@ receive_writer_thread(void *arg) rrd->abd = NULL; rrd->payload = NULL; } else if (rrd->payload != NULL) { - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; } /* diff --git a/module/zfs/vdev_label.c b/module/zfs/vdev_label.c index 54d253c1b7d..e6da5c1707a 100644 --- a/module/zfs/vdev_label.c +++ b/module/zfs/vdev_label.c @@ -1405,6 +1405,7 @@ vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *bootenv) VB_NVLIST); break; } + vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0'; fnvlist_add_string(bootenv, FREEBSD_BOOTONCE, buf); } diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index aeefab4fa64..a23f397e698 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -7180,7 +7180,7 @@ zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) dsl_pool_t *dp; dsl_dataset_t *new, *old; const char *firstsnap; - uint64_t used, comp, uncomp; + uint64_t used = 0, comp = 0, uncomp = 0; firstsnap = fnvlist_lookup_string(innvl, "firstsnap"); diff --git a/module/zfs/zfs_quota.c b/module/zfs/zfs_quota.c index 85b7a549b9a..0b51f8669cb 100644 --- a/module/zfs/zfs_quota.c +++ b/module/zfs/zfs_quota.c @@ -86,10 +86,14 @@ zpl_get_file_info(dmu_object_type_t bonustype, const void *data, sa.sa_layout_info = BSWAP_16(sa.sa_layout_info); swap = B_TRUE; } - VERIFY3U(sa.sa_magic, ==, SA_MAGIC); + + if (unlikely(sa.sa_magic != SA_MAGIC)) + return (SET_ERROR(EINVAL)); int hdrsize = sa_hdrsize(&sa); - VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t)); + + if (unlikely(hdrsize < sizeof (sa_hdr_phys_t))) + return (SET_ERROR(EINVAL)); uintptr_t data_after_hdr = (uintptr_t)data + hdrsize; zoi->zfi_user = *((uint64_t *)(data_after_hdr + SA_UID_OFFSET));