diff --git a/include/sys/fm/util.h b/include/sys/fm/util.h index 94947d67c6..a3a8c3f869 100644 --- a/include/sys/fm/util.h +++ b/include/sys/fm/util.h @@ -96,7 +96,7 @@ extern void zfs_zevent_post(nvlist_t *, nvlist_t *, zevent_cb_t *); extern void zfs_zevent_drain_all(int *); extern int zfs_zevent_fd_hold(int, minor_t *, zfs_zevent_t **); extern void zfs_zevent_fd_rele(int); -extern int zfs_zevent_next(zfs_zevent_t *, nvlist_t **, uint64_t *); +extern int zfs_zevent_next(zfs_zevent_t *, nvlist_t **, uint64_t *, uint64_t *); extern int zfs_zevent_wait(zfs_zevent_t *); extern void zfs_zevent_init(zfs_zevent_t **); extern void zfs_zevent_destroy(zfs_zevent_t *); diff --git a/module/zfs/fm.c b/module/zfs/fm.c index 67d0c1a6e4..d409b2974a 100644 --- a/module/zfs/fm.c +++ b/module/zfs/fm.c @@ -568,13 +568,18 @@ zfs_zevent_fd_rele(int fd) } /* - * Get the next zevent in the stream and place a copy in 'event'. + * Get the next zevent in the stream and place a copy in 'event'. This + * may fail with ENOMEM if the encoded nvlist size exceeds the passed + * 'event_size'. In this case the stream pointer is not advanced and + * and 'event_size' is set to the minimum required buffer size. */ int -zfs_zevent_next(zfs_zevent_t *ze, nvlist_t **event, uint64_t *dropped) +zfs_zevent_next(zfs_zevent_t *ze, nvlist_t **event, uint64_t *event_size, + uint64_t *dropped) { zevent_t *ev; - int error; + size_t size; + int error = 0; mutex_enter(&zevent_lock); if (ze->ze_zevent == NULL) { @@ -592,10 +597,18 @@ zfs_zevent_next(zfs_zevent_t *ze, nvlist_t **event, uint64_t *dropped) error = ENOENT; goto out; } - - list_remove(&ze->ze_zevent->ev_ze_list, ze); } + VERIFY(nvlist_size(ev->ev_nvl, &size, NV_ENCODE_NATIVE) == 0); + if (size > *event_size) { + *event_size = size; + error = ENOMEM; + goto out; + } + + if (ze->ze_zevent) + list_remove(&ze->ze_zevent->ev_ze_list, ze); + ze->ze_zevent = ev; list_insert_head(&ev->ev_ze_list, ze); nvlist_dup(ev->ev_nvl, event, KM_SLEEP); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index cc5e66f9a0..45e118e537 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -4741,11 +4741,12 @@ zfs_ioc_events_next(zfs_cmd_t *zc) return (error); do { - error = zfs_zevent_next(ze, &event, &dropped); + error = zfs_zevent_next(ze, &event, + &zc->zc_nvlist_dst_size, &dropped); if (event != NULL) { zc->zc_cookie = dropped; error = put_nvlist(zc, event); - nvlist_free(event); + nvlist_free(event); } if (zc->zc_guid & ZEVENT_NONBLOCK)