libzfs: don't read a dataset handle after closing it in resume send

zfs_send_resume_impl_cb_impl() closes the dataset handle before its
error switch and then reads zhp->zfs_name again in the ESRCH case.
zfs_name is an array declared inside struct zfs_handle, so the free()
at the end of zfs_close() releases it along with the handle, and
lzc_exists() copies out of the freed block.

The close dates from the original resume send.  The ESRCH case was added
three years later with redacted send, below a handle that was no longer
live.  The path is reachable: dsl_bookmark_lookup() returns ESRCH when
the incremental source is a bookmark that has gone away, which a resume
can lose a race with.

Keep a copy of the name alongside the error message that is already
formatted before the close, and test that instead.

Reported-by: RigelYoung <[email protected]>
Reviewed-by: Rob Norris <[email protected]>
Reviewed-by: Alexander Motin <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Michael Heller <[email protected]>
Closes #18870
Closes #18883
This commit is contained in:
mkhllr authored and GitHub committed 2026-08-04 13:25:41 -07:00
1 parent 674b1ae5cd
commit 3bd8cefdc9
1 file changed
+3 -1
+3 -1
View File
@@ -1986,8 +1986,10 @@ zfs_send_resume_impl_cb_impl(libzfs_handle_t *hdl, sendflags_t *flags,
}
char errbuf[ERRBUFLEN];
char sendname[ZFS_MAX_DATASET_NAME_LEN];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"warning: cannot send '%s'"), zhp->zfs_name);
(void) strlcpy(sendname, zhp->zfs_name, sizeof (sendname));
zfs_close(zhp);
@@ -1999,7 +2001,7 @@ zfs_send_resume_impl_cb_impl(libzfs_handle_t *hdl, sendflags_t *flags,
"source key must be loaded"));
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
case ESRCH:
if (lzc_exists(zhp->zfs_name)) {
if (lzc_exists(sendname)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"incremental source could not be found"));
}