Skip to content

Commit

Permalink
illumos#16463 zfs_ioc_recv leaks nvlist
Browse files Browse the repository at this point in the history
In https://www.illumos.org/issues/16463 it was observed that
an nvlist was being leaked in zfs_ioc_recv() due a missing
call to nvlist_free for "hidden_args".
For OpenZFS the same issue exists in zfs_ioc_recv_new() and
is addressed by this PR.

This change also properly frees nvlists in the unlikely
event that a call to get_nvlist() fails.

Signed-off-by: Andy Fiddaman <[email protected]>
  • Loading branch information
citrus-it committed Apr 10, 2024
1 parent d98973d commit 1f977b4
Showing 1 changed file with 19 additions and 11 deletions.
30 changes: 19 additions & 11 deletions module/zfs/zfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
* Copyright (c) 2019, 2021, Klara Inc.
* Copyright (c) 2019, Allan Jude
* Copyright 2024 Oxide Computer Company
*/

/*
Expand Down Expand Up @@ -5345,22 +5346,25 @@ zfs_ioc_recv(zfs_cmd_t *zc)

if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
strchr(zc->zc_value, '@') == NULL ||
strchr(zc->zc_value, '%'))
strchr(zc->zc_value, '%') != NULL) {
return (SET_ERROR(EINVAL));
}

(void) strlcpy(tofs, zc->zc_value, sizeof (tofs));
tosnap = strchr(tofs, '@');
*tosnap++ = '\0';

if (zc->zc_nvlist_src != 0 &&
(error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &recvdprops)) != 0)
return (error);
zc->zc_iflags, &recvdprops)) != 0) {
goto out;
}

if (zc->zc_nvlist_conf != 0 &&
(error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
zc->zc_iflags, &localprops)) != 0)
return (error);
zc->zc_iflags, &localprops)) != 0) {
goto out;
}

if (zc->zc_string[0])
origin = zc->zc_string;
Expand All @@ -5372,8 +5376,6 @@ zfs_ioc_recv(zfs_cmd_t *zc)
error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvdprops, localprops,
NULL, zc->zc_guid, B_FALSE, B_FALSE, zc->zc_cookie, &begin_record,
&zc->zc_cookie, &zc->zc_obj, &errors);
nvlist_free(recvdprops);
nvlist_free(localprops);

/*
* Now that all props, initial and delayed, are set, report the prop
Expand All @@ -5389,7 +5391,10 @@ zfs_ioc_recv(zfs_cmd_t *zc)
error = SET_ERROR(EINVAL);
}

out:
nvlist_free(errors);
nvlist_free(recvdprops);
nvlist_free(localprops);

return (error);
}
Expand Down Expand Up @@ -5456,8 +5461,9 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)

if (dataset_namecheck(snapname, NULL, NULL) != 0 ||
strchr(snapname, '@') == NULL ||
strchr(snapname, '%'))
strchr(snapname, '%') != NULL) {
return (SET_ERROR(EINVAL));
}

(void) strlcpy(tofs, snapname, sizeof (tofs));
tosnap = strchr(tofs, '@');
Expand All @@ -5481,15 +5487,15 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
/* we still use "props" here for backwards compatibility */
error = nvlist_lookup_nvlist(innvl, "props", &recvprops);
if (error && error != ENOENT)
return (error);
goto out;

error = nvlist_lookup_nvlist(innvl, "localprops", &localprops);
if (error && error != ENOENT)
return (error);
goto out;

error = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args);
if (error && error != ENOENT)
return (error);
goto out;

error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvprops, localprops,
hidden_args, force, heal, resumable, input_fd, begin_record,
Expand All @@ -5499,9 +5505,11 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
fnvlist_add_uint64(outnvl, "error_flags", errflags);
fnvlist_add_nvlist(outnvl, "errors", errors);

out:
nvlist_free(errors);
nvlist_free(recvprops);
nvlist_free(localprops);
nvlist_free(hidden_args);

return (error);
}
Expand Down

0 comments on commit 1f977b4

Please sign in to comment.