2863 lines · 67.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * sysctl.c: General linux system control interface * * Begun 24 March 1995, Stephen Tweedie * Added /proc support, Dec 1995 * Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas. * Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver. * Added kernel/java-{interpreter,appletviewer}, 96/5/10, Mike Shaver. * Dynamic registration fixes, Stephen Tweedie. * Added kswapd-interval, ctrl-alt-del, printk stuff, 1/8/97, Chris Horn. * Made sysctl support optional via CONFIG_SYSCTL, 1/10/97, Chris *  Horn. * Added proc_doulongvec_ms_jiffies_minmax, 09/08/99, Carlos H. Bauer. * Added proc_doulongvec_minmax, 09/08/99, Carlos H. Bauer. * Changed linked lists to use list.h instead of lists.h, 02/24/00, Bill *  Wendling. * The list_for_each() macro wasn't appropriate for the sysctl loop. *  Removed it and replaced it with older style, 03/23/00, Bill Wendling */ #include <linux/module.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/slab.h>#include <linux/sysctl.h>#include <linux/bitmap.h>#include <linux/signal.h>#include <linux/panic.h>#include <linux/printk.h>#include <linux/proc_fs.h>#include <linux/security.h>#include <linux/ctype.h>#include <linux/kmemleak.h>#include <linux/filter.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/kobject.h>#include <linux/net.h>#include <linux/sysrq.h>#include <linux/highuid.h>#include <linux/writeback.h>#include <linux/ratelimit.h>#include <linux/compaction.h>#include <linux/hugetlb.h>#include <linux/initrd.h>#include <linux/key.h>#include <linux/times.h>#include <linux/limits.h>#include <linux/dcache.h>#include <linux/syscalls.h>#include <linux/vmstat.h>#include <linux/nfs_fs.h>#include <linux/acpi.h>#include <linux/reboot.h>#include <linux/ftrace.h>#include <linux/perf_event.h>#include <linux/oom.h>#include <linux/kmod.h>#include <linux/capability.h>#include <linux/binfmts.h>#include <linux/sched/sysctl.h>#include <linux/kexec.h>#include <linux/bpf.h>#include <linux/mount.h>#include <linux/userfaultfd_k.h>#include <linux/latencytop.h>#include <linux/pid.h>#include <linux/delayacct.h> #include "../lib/kstrtox.h" #include <linux/uaccess.h>#include <asm/processor.h> #ifdef CONFIG_X86#include <asm/nmi.h>#include <asm/stacktrace.h>#include <asm/io.h>#endif#ifdef CONFIG_SPARC#include <asm/setup.h>#endif#ifdef CONFIG_BSD_PROCESS_ACCT#include <linux/acct.h>#endif#ifdef CONFIG_RT_MUTEXES#include <linux/rtmutex.h>#endif#if defined(CONFIG_PROVE_LOCKING) || defined(CONFIG_LOCK_STAT)#include <linux/lockdep.h>#endif #if defined(CONFIG_SYSCTL) /* Constants used for minimum and  maximum */ #ifdef CONFIG_PERF_EVENTSstatic const int six_hundred_forty_kb = 640 * 1024;#endif /* this is needed for the proc_doulongvec_minmax of vm_dirty_bytes */static const unsigned long dirty_bytes_min = 2 * PAGE_SIZE; static const int ngroups_max = NGROUPS_MAX;static const int cap_last_cap = CAP_LAST_CAP; #ifdef CONFIG_PROC_SYSCTL /** * enum sysctl_writes_mode - supported sysctl write modes * * @SYSCTL_WRITES_LEGACY: each write syscall must fully contain the sysctl value *	to be written, and multiple writes on the same sysctl file descriptor *	will rewrite the sysctl value, regardless of file position. No warning *	is issued when the initial position is not 0. * @SYSCTL_WRITES_WARN: same as above but warn when the initial file position is *	not 0. * @SYSCTL_WRITES_STRICT: writes to numeric sysctl entries must always be at *	file position 0 and the value must be fully contained in the buffer *	sent to the write syscall. If dealing with strings respect the file *	position, but restrict this to the max length of the buffer, anything *	passed the max length will be ignored. Multiple writes will append *	to the buffer. * * These write modes control how current file position affects the behavior of * updating sysctl values through the proc interface on each write. */enum sysctl_writes_mode {	SYSCTL_WRITES_LEGACY		= -1,	SYSCTL_WRITES_WARN		= 0,	SYSCTL_WRITES_STRICT		= 1,}; static enum sysctl_writes_mode sysctl_writes_strict = SYSCTL_WRITES_STRICT;#endif /* CONFIG_PROC_SYSCTL */ #if defined(HAVE_ARCH_PICK_MMAP_LAYOUT) || \    defined(CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT)int sysctl_legacy_va_layout;#endif #ifdef CONFIG_COMPACTION/* min_extfrag_threshold is SYSCTL_ZERO */;static const int max_extfrag_threshold = 1000;#endif #endif /* CONFIG_SYSCTL */ #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_SYSCTL)static int bpf_stats_handler(struct ctl_table *table, int write,			     void *buffer, size_t *lenp, loff_t *ppos){	struct static_key *key = (struct static_key *)table->data;	static int saved_val;	int val, ret;	struct ctl_table tmp = {		.data   = &val,		.maxlen = sizeof(val),		.mode   = table->mode,		.extra1 = SYSCTL_ZERO,		.extra2 = SYSCTL_ONE,	}; 	if (write && !capable(CAP_SYS_ADMIN))		return -EPERM; 	mutex_lock(&bpf_stats_enabled_mutex);	val = saved_val;	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);	if (write && !ret && val != saved_val) {		if (val)			static_key_slow_inc(key);		else			static_key_slow_dec(key);		saved_val = val;	}	mutex_unlock(&bpf_stats_enabled_mutex);	return ret;} void __weak unpriv_ebpf_notify(int new_state){} static int bpf_unpriv_handler(struct ctl_table *table, int write,			      void *buffer, size_t *lenp, loff_t *ppos){	int ret, unpriv_enable = *(int *)table->data;	bool locked_state = unpriv_enable == 1;	struct ctl_table tmp = *table; 	if (write && !capable(CAP_SYS_ADMIN))		return -EPERM; 	tmp.data = &unpriv_enable;	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);	if (write && !ret) {		if (locked_state && unpriv_enable != 1)			return -EPERM;		*(int *)table->data = unpriv_enable;	} 	unpriv_ebpf_notify(unpriv_enable); 	return ret;}#endif /* CONFIG_BPF_SYSCALL && CONFIG_SYSCTL */ /* * /proc/sys support */ #ifdef CONFIG_PROC_SYSCTL static int _proc_do_string(char *data, int maxlen, int write,		char *buffer, size_t *lenp, loff_t *ppos){	size_t len;	char c, *p; 	if (!data || !maxlen || !*lenp) {		*lenp = 0;		return 0;	} 	if (write) {		if (sysctl_writes_strict == SYSCTL_WRITES_STRICT) {			/* Only continue writes not past the end of buffer. */			len = strlen(data);			if (len > maxlen - 1)				len = maxlen - 1; 			if (*ppos > len)				return 0;			len = *ppos;		} else {			/* Start writing from beginning of buffer. */			len = 0;		} 		*ppos += *lenp;		p = buffer;		while ((p - buffer) < *lenp && len < maxlen - 1) {			c = *(p++);			if (c == 0 || c == '\n')				break;			data[len++] = c;		}		data[len] = 0;	} else {		len = strlen(data);		if (len > maxlen)			len = maxlen; 		if (*ppos > len) {			*lenp = 0;			return 0;		} 		data += *ppos;		len  -= *ppos; 		if (len > *lenp)			len = *lenp;		if (len)			memcpy(buffer, data, len);		if (len < *lenp) {			buffer[len] = '\n';			len++;		}		*lenp = len;		*ppos += len;	}	return 0;} static void warn_sysctl_write(struct ctl_table *table){	pr_warn_once("%s wrote to %s when file position was not 0!\n"		"This will not be supported in the future. To silence this\n"		"warning, set kernel.sysctl_writes_strict = -1\n",		current->comm, table->procname);} /** * proc_first_pos_non_zero_ignore - check if first position is allowed * @ppos: file position * @table: the sysctl table * * Returns true if the first position is non-zero and the sysctl_writes_strict * mode indicates this is not allowed for numeric input types. String proc * handlers can ignore the return value. */static bool proc_first_pos_non_zero_ignore(loff_t *ppos,					   struct ctl_table *table){	if (!*ppos)		return false; 	switch (sysctl_writes_strict) {	case SYSCTL_WRITES_STRICT:		return true;	case SYSCTL_WRITES_WARN:		warn_sysctl_write(table);		return false;	default:		return false;	}} /** * proc_dostring - read a string sysctl * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes a string from/to the user buffer. If the kernel * buffer provided is not large enough to hold the string, the * string is truncated. The copied string is %NULL-terminated. * If the string is being read by the user process, it is copied * and a newline '\n' is added. It is truncated if the buffer is * not large enough. * * Returns 0 on success. */int proc_dostring(struct ctl_table *table, int write,		  void *buffer, size_t *lenp, loff_t *ppos){	if (write)		proc_first_pos_non_zero_ignore(ppos, table); 	return _proc_do_string(table->data, table->maxlen, write, buffer, lenp,			ppos);} static size_t proc_skip_spaces(char **buf){	size_t ret;	char *tmp = skip_spaces(*buf);	ret = tmp - *buf;	*buf = tmp;	return ret;} static void proc_skip_char(char **buf, size_t *size, const char v){	while (*size) {		if (**buf != v)			break;		(*size)--;		(*buf)++;	}} /** * strtoul_lenient - parse an ASCII formatted integer from a buffer and only *                   fail on overflow * * @cp: kernel buffer containing the string to parse * @endp: pointer to store the trailing characters * @base: the base to use * @res: where the parsed integer will be stored * * In case of success 0 is returned and @res will contain the parsed integer, * @endp will hold any trailing characters. * This function will fail the parse on overflow. If there wasn't an overflow * the function will defer the decision what characters count as invalid to the * caller. */static int strtoul_lenient(const char *cp, char **endp, unsigned int base,			   unsigned long *res){	unsigned long long result;	unsigned int rv; 	cp = _parse_integer_fixup_radix(cp, &base);	rv = _parse_integer(cp, base, &result);	if ((rv & KSTRTOX_OVERFLOW) || (result != (unsigned long)result))		return -ERANGE; 	cp += rv; 	if (endp)		*endp = (char *)cp; 	*res = (unsigned long)result;	return 0;} #define TMPBUFLEN 22/** * proc_get_long - reads an ASCII formatted integer from a user buffer * * @buf: a kernel buffer * @size: size of the kernel buffer * @val: this is where the number will be stored * @neg: set to %TRUE if number is negative * @perm_tr: a vector which contains the allowed trailers * @perm_tr_len: size of the perm_tr vector * @tr: pointer to store the trailer character * * In case of success %0 is returned and @buf and @size are updated with * the amount of bytes read. If @tr is non-NULL and a trailing * character exists (size is non-zero after returning from this * function), @tr is updated with the trailing character. */static int proc_get_long(char **buf, size_t *size,			  unsigned long *val, bool *neg,			  const char *perm_tr, unsigned perm_tr_len, char *tr){	int len;	char *p, tmp[TMPBUFLEN]; 	if (!*size)		return -EINVAL; 	len = *size;	if (len > TMPBUFLEN - 1)		len = TMPBUFLEN - 1; 	memcpy(tmp, *buf, len); 	tmp[len] = 0;	p = tmp;	if (*p == '-' && *size > 1) {		*neg = true;		p++;	} else		*neg = false;	if (!isdigit(*p))		return -EINVAL; 	if (strtoul_lenient(p, &p, 0, val))		return -EINVAL; 	len = p - tmp; 	/* We don't know if the next char is whitespace thus we may accept	 * invalid integers (e.g. 1234...a) or two integers instead of one	 * (e.g. 123...1). So lets not allow such large numbers. */	if (len == TMPBUFLEN - 1)		return -EINVAL; 	if (len < *size && perm_tr_len && !memchr(perm_tr, *p, perm_tr_len))		return -EINVAL; 	if (tr && (len < *size))		*tr = *p; 	*buf += len;	*size -= len; 	return 0;} /** * proc_put_long - converts an integer to a decimal ASCII formatted string * * @buf: the user buffer * @size: the size of the user buffer * @val: the integer to be converted * @neg: sign of the number, %TRUE for negative * * In case of success @buf and @size are updated with the amount of bytes * written. */static void proc_put_long(void **buf, size_t *size, unsigned long val, bool neg){	int len;	char tmp[TMPBUFLEN], *p = tmp; 	sprintf(p, "%s%lu", neg ? "-" : "", val);	len = strlen(tmp);	if (len > *size)		len = *size;	memcpy(*buf, tmp, len);	*size -= len;	*buf += len;}#undef TMPBUFLEN static void proc_put_char(void **buf, size_t *size, char c){	if (*size) {		char **buffer = (char **)buf;		**buffer = c; 		(*size)--;		(*buffer)++;		*buf = *buffer;	}} static int do_proc_dobool_conv(bool *negp, unsigned long *lvalp,				int *valp,				int write, void *data)

Lines 1–500 of 2863.