Illumos#16463 zfs_ioc_recv leaks nvlist

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.

Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Igor Kozhukhov <igor@dilos.org>
Signed-off-by: Andy Fiddaman <illumos@fiddaman.net>
Closes #16077
This commit is contained in:
Andy Fiddaman 2024-04-11 22:38:22 +01:00 committed by GitHub
parent e5ddecd1a7
commit 44f337be30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 19 additions and 11 deletions

View File

@ -40,6 +40,7 @@
* Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved. * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
* Copyright (c) 2019, 2021, Klara Inc. * Copyright (c) 2019, 2021, Klara Inc.
* Copyright (c) 2019, Allan Jude * Copyright (c) 2019, Allan Jude
* Copyright 2024 Oxide Computer Company
*/ */
/* /*
@ -5345,8 +5346,9 @@ zfs_ioc_recv(zfs_cmd_t *zc)
if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
strchr(zc->zc_value, '@') == NULL || strchr(zc->zc_value, '@') == NULL ||
strchr(zc->zc_value, '%')) strchr(zc->zc_value, '%') != NULL) {
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
}
(void) strlcpy(tofs, zc->zc_value, sizeof (tofs)); (void) strlcpy(tofs, zc->zc_value, sizeof (tofs));
tosnap = strchr(tofs, '@'); tosnap = strchr(tofs, '@');
@ -5354,13 +5356,15 @@ zfs_ioc_recv(zfs_cmd_t *zc)
if (zc->zc_nvlist_src != 0 && if (zc->zc_nvlist_src != 0 &&
(error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &recvdprops)) != 0) zc->zc_iflags, &recvdprops)) != 0) {
return (error); goto out;
}
if (zc->zc_nvlist_conf != 0 && if (zc->zc_nvlist_conf != 0 &&
(error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
zc->zc_iflags, &localprops)) != 0) zc->zc_iflags, &localprops)) != 0) {
return (error); goto out;
}
if (zc->zc_string[0]) if (zc->zc_string[0])
origin = zc->zc_string; origin = zc->zc_string;
@ -5372,8 +5376,6 @@ zfs_ioc_recv(zfs_cmd_t *zc)
error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvdprops, localprops, error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvdprops, localprops,
NULL, zc->zc_guid, B_FALSE, B_FALSE, zc->zc_cookie, &begin_record, NULL, zc->zc_guid, B_FALSE, B_FALSE, zc->zc_cookie, &begin_record,
&zc->zc_cookie, &zc->zc_obj, &errors); &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 * Now that all props, initial and delayed, are set, report the prop
@ -5389,7 +5391,10 @@ zfs_ioc_recv(zfs_cmd_t *zc)
error = SET_ERROR(EINVAL); error = SET_ERROR(EINVAL);
} }
out:
nvlist_free(errors); nvlist_free(errors);
nvlist_free(recvdprops);
nvlist_free(localprops);
return (error); return (error);
} }
@ -5456,8 +5461,9 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
if (dataset_namecheck(snapname, NULL, NULL) != 0 || if (dataset_namecheck(snapname, NULL, NULL) != 0 ||
strchr(snapname, '@') == NULL || strchr(snapname, '@') == NULL ||
strchr(snapname, '%')) strchr(snapname, '%') != NULL) {
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
}
(void) strlcpy(tofs, snapname, sizeof (tofs)); (void) strlcpy(tofs, snapname, sizeof (tofs));
tosnap = strchr(tofs, '@'); tosnap = strchr(tofs, '@');
@ -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 */ /* we still use "props" here for backwards compatibility */
error = nvlist_lookup_nvlist(innvl, "props", &recvprops); error = nvlist_lookup_nvlist(innvl, "props", &recvprops);
if (error && error != ENOENT) if (error && error != ENOENT)
return (error); goto out;
error = nvlist_lookup_nvlist(innvl, "localprops", &localprops); error = nvlist_lookup_nvlist(innvl, "localprops", &localprops);
if (error && error != ENOENT) if (error && error != ENOENT)
return (error); goto out;
error = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); error = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args);
if (error && error != ENOENT) if (error && error != ENOENT)
return (error); goto out;
error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvprops, localprops, error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvprops, localprops,
hidden_args, force, heal, resumable, input_fd, begin_record, hidden_args, force, heal, resumable, input_fd, begin_record,
@ -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_uint64(outnvl, "error_flags", errflags);
fnvlist_add_nvlist(outnvl, "errors", errors); fnvlist_add_nvlist(outnvl, "errors", errors);
out:
nvlist_free(errors); nvlist_free(errors);
nvlist_free(recvprops); nvlist_free(recvprops);
nvlist_free(localprops); nvlist_free(localprops);
nvlist_free(hidden_args);
return (error); return (error);
} }