mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	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:
		
							parent
							
								
									663a070c92
								
							
						
					
					
						commit
						009cc8e884
					
				@ -162,8 +162,6 @@ extern "C" {
 | 
				
			|||||||
#define	O_RSYNC 0
 | 
					#define	O_RSYNC 0
 | 
				
			||||||
#define	O_DSYNC 0
 | 
					#define	O_DSYNC 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define	KMALLOC_MAX_SIZE MAXPHYS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef LOCORE
 | 
					#ifndef LOCORE
 | 
				
			||||||
#ifndef HAVE_RPC_TYPES
 | 
					#ifndef HAVE_RPC_TYPES
 | 
				
			||||||
typedef int bool_t;
 | 
					typedef int bool_t;
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
extern kmutex_t zfsdev_state_lock;
 | 
					extern kmutex_t zfsdev_state_lock;
 | 
				
			||||||
extern zfsdev_state_t *zfsdev_state_list;
 | 
					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_legacy_func_t(zfs_cmd_t *);
 | 
				
			||||||
typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_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,
 | 
					    zfs_secpolicy_func_t *, zfs_ioc_namecheck_t, zfs_ioc_poolcheck_t,
 | 
				
			||||||
    boolean_t, boolean_t, const zfs_ioc_key_t *, size_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);
 | 
					void zfs_ioctl_init_os(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
boolean_t zfs_vfs_held(zfsvfs_t *);
 | 
					boolean_t zfs_vfs_held(zfsvfs_t *);
 | 
				
			||||||
 | 
				
			|||||||
@ -1074,6 +1074,23 @@ pool import (only in read-only mode).
 | 
				
			|||||||
Default value: \fB0\fR
 | 
					Default value: \fB0\fR
 | 
				
			||||||
.RE
 | 
					.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
 | 
					.sp
 | 
				
			||||||
.ne 2
 | 
					.ne 2
 | 
				
			||||||
.na
 | 
					.na
 | 
				
			||||||
 | 
				
			|||||||
@ -35,9 +35,14 @@ __FBSDID("$FreeBSD$");
 | 
				
			|||||||
#include <sys/vdev_os.h>
 | 
					#include <sys/vdev_os.h>
 | 
				
			||||||
#include <sys/zfs_vfsops.h>
 | 
					#include <sys/zfs_vfsops.h>
 | 
				
			||||||
#include <sys/zone.h>
 | 
					#include <sys/zone.h>
 | 
				
			||||||
 | 
					#include <vm/vm_pageout.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <sys/zfs_ioctl_impl.h>
 | 
					#include <sys/zfs_ioctl_impl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if __FreeBSD_version < 1201517
 | 
				
			||||||
 | 
					#define	vm_page_max_user_wired	vm_page_max_wired
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
zfs_vfs_ref(zfsvfs_t **zfvp)
 | 
					zfs_vfs_ref(zfsvfs_t **zfvp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -133,6 +138,14 @@ zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl)
 | 
				
			|||||||
	return (error);
 | 
						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
 | 
					void
 | 
				
			||||||
zfs_ioctl_init_os(void)
 | 
					zfs_ioctl_init_os(void)
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
					void
 | 
				
			||||||
zfs_ioctl_init_os(void)
 | 
					zfs_ioctl_init_os(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
				
			|||||||
@ -225,8 +225,9 @@ zfsdev_state_t *zfsdev_state_list;
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * Limit maximum nvlist size.  We don't want users passing in insane values
 | 
					 * 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.
 | 
					 * 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_fsyncer_key;
 | 
				
			||||||
uint_t zfs_allow_log_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;
 | 
						int error, cmd;
 | 
				
			||||||
	const zfs_ioc_vec_t *vec;
 | 
						const zfs_ioc_vec_t *vec;
 | 
				
			||||||
	char *saved_poolname = NULL;
 | 
						char *saved_poolname = NULL;
 | 
				
			||||||
 | 
						uint64_t max_nvlist_src_size;
 | 
				
			||||||
	size_t saved_poolname_len = 0;
 | 
						size_t saved_poolname_len = 0;
 | 
				
			||||||
	nvlist_t *innvl = NULL;
 | 
						nvlist_t *innvl = NULL;
 | 
				
			||||||
	fstrans_cookie_t cookie;
 | 
						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));
 | 
							return (SET_ERROR(ZFS_ERR_IOC_CMD_UNAVAIL));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	zc->zc_iflags = flag & FKIOCTL;
 | 
						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
 | 
							 * Make sure the user doesn't pass in an insane value for
 | 
				
			||||||
		 * zc_nvlist_src_size.  We have to check, since we will end
 | 
							 * 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(&rrw_tsd_key);
 | 
				
			||||||
	tsd_destroy(&zfs_allow_log_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 */
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user