// SPDX-License-Identifier: GPL-2.0-only
/*
* sysctl.c: General linux system control interface
*/
#include <linux/sysctl.h>
#include <linux/bitmap.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/highuid.h>
#include <linux/writeback.h>
#include <linux/initrd.h>
#include <linux/limits.h>
#include <linux/syscalls.h>
#include <linux/capability.h>
#include "../lib/kstrtox.h"
#include <linux/uaccess.h>
#include <asm/processor.h>
/* shared constants to be used in various sysctls */
const int sysctl_vals[] = { 0, 1, 2, 3, 4, 100, 200, 1000, 3000, INT_MAX, 65535, -1 };
EXPORT_SYMBOL(sysctl_vals);
const unsigned long sysctl_long_vals[] = { 0, 1, LONG_MAX };
EXPORT_SYMBOL_GPL(sysctl_long_vals);
#if defined(CONFIG_SYSCTL)
/* Constants used for minimum and maximum */
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 internal kernel (SYSCTL_USER_TO_KERN) 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 */
#endif /* CONFIG_SYSCTL */
/*
* /proc/sys support
*/
#ifdef CONFIG_PROC_SYSCTL
static int _proc_do_string(char *data, int maxlen, int dir,
char *buffer, size_t *lenp, loff_t *ppos)
{
size_t len;
char c, *p;
if (!data || !maxlen || !*lenp) {
*lenp = 0;
return 0;
}
if (SYSCTL_USER_TO_KERN(dir)) {
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