diff --git a/cmd/zstream/zstream_decompress.c b/cmd/zstream/zstream_decompress.c index 726c3be6dc..0cef36c044 100644 --- a/cmd/zstream/zstream_decompress.c +++ b/cmd/zstream/zstream_decompress.c @@ -179,7 +179,10 @@ zstream_do_decompress(int argc, char *argv[]) VERIFY0(begin++); seen = B_TRUE; - int sz = drr->drr_payloadlen; + uint32_t sz = drr->drr_payloadlen; + + VERIFY3U(sz, <=, 1U << 28); + if (sz != 0) { if (sz > bufsz) { buf = realloc(buf, sz); diff --git a/cmd/zstream/zstream_recompress.c b/cmd/zstream/zstream_recompress.c index 38ef758f8e..8392ef3de7 100644 --- a/cmd/zstream/zstream_recompress.c +++ b/cmd/zstream/zstream_recompress.c @@ -160,7 +160,10 @@ zstream_do_recompress(int argc, char *argv[]) VERIFY0(begin++); seen = B_TRUE; - int sz = drr->drr_payloadlen; + uint32_t sz = drr->drr_payloadlen; + + VERIFY3U(sz, <=, 1U << 28); + if (sz != 0) { if (sz > bufsz) { buf = realloc(buf, sz); diff --git a/cmd/zstream/zstream_redup.c b/cmd/zstream/zstream_redup.c index 8b12303c5d..c56a09cee7 100644 --- a/cmd/zstream/zstream_redup.c +++ b/cmd/zstream/zstream_redup.c @@ -254,7 +254,10 @@ zfs_redup_stream(int infd, int outfd, boolean_t verbose) /* cppcheck-suppress syntaxError */ DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags); - int sz = drr->drr_payloadlen; + uint32_t sz = drr->drr_payloadlen; + + VERIFY3U(sz, <=, 1U << 28); + if (sz != 0) { if (sz > bufsz) { free(buf); diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 49ae7d449b..038613a1fc 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -5197,6 +5197,14 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, destsnap); *cp = '@'; break; + case E2BIG: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "zfs receive required kernel memory allocation " + "larger than the system can support. Please file " + "an issue at the OpenZFS issue tracker:\n" + "https://github.com/openzfs/zfs/issues/new")); + (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); + break; case EBUSY: if (hastoken) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, diff --git a/module/zfs/dmu_recv.c b/module/zfs/dmu_recv.c index 9461643cee..61cfe36515 100644 --- a/module/zfs/dmu_recv.c +++ b/module/zfs/dmu_recv.c @@ -31,6 +31,7 @@ * Copyright (c) 2022 Axcient. */ +#include #include #include #include @@ -1246,19 +1247,29 @@ dmu_recv_begin(char *tofs, char *tosnap, dmu_replay_record_t *drr_begin, uint32_t payloadlen = drc->drc_drr_begin->drr_payloadlen; void *payload = NULL; + + /* + * Since OpenZFS 2.0.0, we have enforced a 64MB limit in userspace + * configurable via ZFS_SENDRECV_MAX_NVLIST. We enforce 256MB as a hard + * upper limit. Systems with less than 1GB of RAM will see a lower + * limit from `arc_all_memory() / 4`. + */ + if (payloadlen > (MIN((1U << 28), arc_all_memory() / 4))) + return (E2BIG); + if (payloadlen != 0) - payload = kmem_alloc(payloadlen, KM_SLEEP); + payload = vmem_alloc(payloadlen, KM_SLEEP); err = receive_read_payload_and_next_header(drc, payloadlen, payload); if (err != 0) { - kmem_free(payload, payloadlen); + vmem_free(payload, payloadlen); return (err); } if (payloadlen != 0) { err = nvlist_unpack(payload, payloadlen, &drc->drc_begin_nvl, KM_SLEEP); - kmem_free(payload, payloadlen); + vmem_free(payload, payloadlen); if (err != 0) { kmem_free(drc->drc_next_rrd, sizeof (*drc->drc_next_rrd));