Make zc_nvlist_src_size limit tunable

We limit the size of nvlists passed to the kernel so a user cannot make
the kernel do an unreasonably large allocation.  On FreeBSD this limit
was 128 kiB, which turns out to be a bit too small when doing some
operations involving a large number of datasets or snapshots, for
example replication.

Make this limit tunable, with a platform-specific auto default.
Linux keeps its limit at KMALLOC_MAX_SIZE. FreeBSD uses 1/4 of the
system limit on user wired memory, which allows it to scale depending
on system configuration.

Reviewed-by: Matt Macy <mmacy@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Ryan Moeller <freqlabs@FreeBSD.org>
Issue #6572 
Closes #10706
This commit is contained in:
Ryan Moeller 2020-08-18 12:33:55 -04:00 committed by GitHub
parent 663a070c92
commit 009cc8e884
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 4 deletions

View File

@ -162,8 +162,6 @@ extern "C" {
#define O_RSYNC 0
#define O_DSYNC 0
#define KMALLOC_MAX_SIZE MAXPHYS
#ifndef LOCORE
#ifndef HAVE_RPC_TYPES
typedef int bool_t;

View File

@ -25,6 +25,7 @@
extern kmutex_t zfsdev_state_lock;
extern zfsdev_state_t *zfsdev_state_list;
extern unsigned long zfs_max_nvlist_src_size;
typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *);
typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *);
@ -80,6 +81,7 @@ void zfs_ioctl_register(const char *, zfs_ioc_t, zfs_ioc_func_t *,
zfs_secpolicy_func_t *, zfs_ioc_namecheck_t, zfs_ioc_poolcheck_t,
boolean_t, boolean_t, const zfs_ioc_key_t *, size_t);
uint64_t zfs_max_nvlist_src_size_os(void);
void zfs_ioctl_init_os(void);
boolean_t zfs_vfs_held(zfsvfs_t *);

View File

@ -1074,6 +1074,23 @@ pool import (only in read-only mode).
Default value: \fB0\fR
.RE
.sp
.ne 2
.na
\fBzfs_max_nvlist_src_size\fR (ulong)
.ad
.RS 12n
Maximum size in bytes allowed to be passed as zc_nvlist_src_size for ioctls on
/dev/zfs. This prevents a user from causing the kernel to allocate an excessive
amount of memory. When the limit is exceeded, the ioctl fails with EINVAL and a
description of the error is sent to the zfs-dbgmsg log. This parameter should
not need to be touched under normal circumstances. On FreeBSD, the default is
based on the system limit on user wired memory. On Linux, the default is
\fBKMALLOC_MAX_SIZE\fR .
.sp
Default value: \fB0\fR (kernel decides)
.RE
.sp
.ne 2
.na

View File

@ -35,9 +35,14 @@ __FBSDID("$FreeBSD$");
#include <sys/vdev_os.h>
#include <sys/zfs_vfsops.h>
#include <sys/zone.h>
#include <vm/vm_pageout.h>
#include <sys/zfs_ioctl_impl.h>
#if __FreeBSD_version < 1201517
#define vm_page_max_user_wired vm_page_max_wired
#endif
int
zfs_vfs_ref(zfsvfs_t **zfvp)
{
@ -133,6 +138,14 @@ zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl)
return (error);
}
uint64_t
zfs_max_nvlist_src_size_os(void)
{
if (zfs_max_nvlist_src_size != 0)
return (zfs_max_nvlist_src_size);
return (ptob(vm_page_max_user_wired) / 4);
}
void
zfs_ioctl_init_os(void)

View File

@ -203,6 +203,15 @@ out:
}
uint64_t
zfs_max_nvlist_src_size_os(void)
{
if (zfs_max_nvlist_src_size != 0)
return (zfs_max_nvlist_src_size);
return (KMALLOC_MAX_SIZE);
}
void
zfs_ioctl_init_os(void)
{

View File

@ -225,8 +225,9 @@ zfsdev_state_t *zfsdev_state_list;
/*
* Limit maximum nvlist size. We don't want users passing in insane values
* for zc->zc_nvlist_src_size, since we will need to allocate that much memory.
* Defaults to 0=auto which is handled by platform code.
*/
#define MAX_NVLIST_SRC_SIZE KMALLOC_MAX_SIZE
unsigned long zfs_max_nvlist_src_size = 0;
uint_t zfs_fsyncer_key;
uint_t zfs_allow_log_key;
@ -7341,6 +7342,7 @@ zfsdev_ioctl_common(uint_t vecnum, zfs_cmd_t *zc, int flag)
int error, cmd;
const zfs_ioc_vec_t *vec;
char *saved_poolname = NULL;
uint64_t max_nvlist_src_size;
size_t saved_poolname_len = 0;
nvlist_t *innvl = NULL;
fstrans_cookie_t cookie;
@ -7360,7 +7362,8 @@ zfsdev_ioctl_common(uint_t vecnum, zfs_cmd_t *zc, int flag)
return (SET_ERROR(ZFS_ERR_IOC_CMD_UNAVAIL));
zc->zc_iflags = flag & FKIOCTL;
if (zc->zc_nvlist_src_size > MAX_NVLIST_SRC_SIZE) {
max_nvlist_src_size = zfs_max_nvlist_src_size_os();
if (zc->zc_nvlist_src_size > max_nvlist_src_size) {
/*
* Make sure the user doesn't pass in an insane value for
* zc_nvlist_src_size. We have to check, since we will end
@ -7593,3 +7596,8 @@ zfs_kmod_fini(void)
tsd_destroy(&rrw_tsd_key);
tsd_destroy(&zfs_allow_log_key);
}
/* BEGIN CSTYLED */
ZFS_MODULE_PARAM(zfs, zfs_, max_nvlist_src_size, ULONG, ZMOD_RW,
"Maximum size in bytes allowed for src nvlist passed with ZFS ioctls");
/* END CSTYLED */