summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/pipe.c22
-rw-r--r--include/linux/fs.h1
-rw-r--r--include/linux/hugetlb.h2
-rw-r--r--include/linux/printk.h1
-rw-r--r--include/linux/sysctl.h120
-rw-r--r--include/net/ax25.h2
-rw-r--r--kernel/printk/internal.h2
-rw-r--r--kernel/printk/sysctl.c1
-rw-r--r--kernel/sysctl.c290
-rw-r--r--kernel/time/jiffies.c134
-rw-r--r--security/loadpin/loadpin.c37
11 files changed, 436 insertions, 176 deletions
diff --git a/fs/pipe.c b/fs/pipe.c
index 9e6a01475815..22647f50b286 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1481,10 +1481,24 @@ static struct file_system_type pipe_fs_type = {
};
#ifdef CONFIG_SYSCTL
-static SYSCTL_USER_TO_KERN_UINT_CONV(_pipe_maxsz, round_pipe_size)
-static SYSCTL_UINT_CONV_CUSTOM(_pipe_maxsz,
- sysctl_user_to_kern_uint_conv_pipe_maxsz,
- sysctl_kern_to_user_uint_conv, true)
+
+static ulong round_pipe_size_ul(ulong size)
+{
+ return round_pipe_size(size);
+}
+
+static int u2k_pipe_maxsz(const ulong *u_ptr, uint *k_ptr)
+{
+ return proc_uint_u2k_conv_uop(u_ptr, k_ptr, round_pipe_size_ul);
+}
+
+static int do_proc_uint_conv_pipe_maxsz(ulong *u_ptr, uint *k_ptr,
+ int dir, const struct ctl_table *table)
+{
+ return proc_uint_conv(u_ptr, k_ptr, dir, table, true,
+ u2k_pipe_maxsz,
+ proc_uint_k2u_conv);
+}
static int proc_dopipe_max_size(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a2af5ddd5323..8b3dd145b25e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3525,7 +3525,6 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf,
ssize_t simple_attr_write_signed(struct file *file, const char __user *buf,
size_t len, loff_t *ppos);
-struct ctl_table;
int __init list_bdev_fs_names(char *buf, size_t size);
#define __FMODE_EXEC ((__force int) FMODE_EXEC)
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 94a03591990c..d2e48fa5f72e 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -16,8 +16,6 @@
#include <linux/userfaultfd_k.h>
#include <linux/nodemask.h>
-struct ctl_table;
-struct user_struct;
struct mmu_gather;
struct node;
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 45c663124c9b..63d516c873b4 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -78,7 +78,6 @@ extern void console_verbose(void);
/* strlen("ratelimit") + 1 */
#define DEVKMSG_STR_MAX_SIZE 10
extern char devkmsg_log_str[DEVKMSG_STR_MAX_SIZE];
-struct ctl_table;
extern int suppress_printk;
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 288fe0055cd5..2886fbceb5d6 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -59,7 +59,6 @@ extern const int sysctl_vals[];
#define SYSCTL_LONG_ONE ((void *)&sysctl_long_vals[1])
#define SYSCTL_LONG_MAX ((void *)&sysctl_long_vals[2])
-#define SYSCTL_CONV_IDENTITY(val) (val)
/**
*
* "dir" originates from read_iter (dir = 0) or write_iter (dir = 1)
@@ -73,107 +72,6 @@ extern const int sysctl_vals[];
#define SYSCTL_USER_TO_KERN(dir) (!!(dir))
#define SYSCTL_KERN_TO_USER(dir) (!dir)
-#define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \
-int sysctl_user_to_kern_int_conv##name(const bool *negp, \
- const unsigned long *u_ptr,\
- int *k_ptr) \
-{ \
- unsigned long u = u_ptr_op(*u_ptr); \
- if (*negp) { \
- if (u > (unsigned long) INT_MAX + 1) \
- return -EINVAL; \
- WRITE_ONCE(*k_ptr, -u); \
- } else { \
- if (u > (unsigned long) INT_MAX) \
- return -EINVAL; \
- WRITE_ONCE(*k_ptr, u); \
- } \
- return 0; \
-}
-
-#define SYSCTL_KERN_TO_USER_INT_CONV(name, k_ptr_op) \
-int sysctl_kern_to_user_int_conv##name(bool *negp, \
- unsigned long *u_ptr, \
- const int *k_ptr) \
-{ \
- int val = READ_ONCE(*k_ptr); \
- if (val < 0) { \
- *negp = true; \
- *u_ptr = -k_ptr_op((unsigned long)val); \
- } else { \
- *negp = false; \
- *u_ptr = k_ptr_op((unsigned long)val); \
- } \
- return 0; \
-}
-
-/**
- * To range check on a converted value, use a temp k_ptr
- * When checking range, value should be within (tbl->extra1, tbl->extra2)
- */
-#define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \
- k_ptr_range_check) \
-int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\
- int dir, const struct ctl_table *tbl) \
-{ \
- if (SYSCTL_KERN_TO_USER(dir)) \
- return kern_to_user(negp, u_ptr, k_ptr); \
- \
- if (k_ptr_range_check) { \
- int tmp_k, ret; \
- if (!tbl) \
- return -EINVAL; \
- ret = user_to_kern(negp, u_ptr, &tmp_k); \
- if (ret) \
- return ret; \
- if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) || \
- (tbl->extra2 && *(int *)tbl->extra2 < tmp_k)) \
- return -EINVAL; \
- WRITE_ONCE(*k_ptr, tmp_k); \
- } else \
- return user_to_kern(negp, u_ptr, k_ptr); \
- return 0; \
-}
-
-#define SYSCTL_USER_TO_KERN_UINT_CONV(name, u_ptr_op) \
-int sysctl_user_to_kern_uint_conv##name(const unsigned long *u_ptr,\
- unsigned int *k_ptr) \
-{ \
- unsigned long u = u_ptr_op(*u_ptr); \
- if (u > UINT_MAX) \
- return -EINVAL; \
- WRITE_ONCE(*k_ptr, u); \
- return 0; \
-}
-
-#define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \
- k_ptr_range_check) \
-int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \
- int dir, const struct ctl_table *tbl) \
-{ \
- if (SYSCTL_KERN_TO_USER(dir)) \
- return kern_to_user(u_ptr, k_ptr); \
- \
- if (k_ptr_range_check) { \
- unsigned int tmp_k; \
- int ret; \
- if (!tbl) \
- return -EINVAL; \
- ret = user_to_kern(u_ptr, &tmp_k); \
- if (ret) \
- return ret; \
- if ((tbl->extra1 && \
- *(unsigned int *)tbl->extra1 > tmp_k) || \
- (tbl->extra2 && \
- *(unsigned int *)tbl->extra2 < tmp_k)) \
- return -ERANGE; \
- WRITE_ONCE(*k_ptr, tmp_k); \
- } else \
- return user_to_kern(u_ptr, k_ptr); \
- return 0; \
-}
-
-
extern const unsigned long sysctl_long_vals[];
typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer,
@@ -182,6 +80,7 @@ typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer,
int proc_dostring(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_dobool(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
+
int proc_dointvec(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_dointvec_minmax(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos);
@@ -189,6 +88,15 @@ int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *table));
+int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp,
+ ulong (*k_ptr_op)(const ulong));
+int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp,
+ ulong (*u_ptr_op)(const ulong));
+int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
+ const struct ctl_table *tbl, bool k_ptr_range_check,
+ int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr),
+ int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr));
+
int proc_douintvec(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_douintvec_minmax(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
@@ -196,6 +104,13 @@ int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp, unsigned int *valp,
int write, const struct ctl_table *table));
+int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr);
+int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
+ ulong (*u_ptr_op)(const ulong));
+int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
+ const struct ctl_table *tbl, bool k_ptr_range_check,
+ int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
+ int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr));
int proc_dou8vec_minmax(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
@@ -206,7 +121,6 @@ int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir,
int proc_do_large_bitmap(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_do_static_key(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
-int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr, const unsigned int *k_ptr);
/*
* Register a set of sysctl names by calling register_sysctl
diff --git a/include/net/ax25.h b/include/net/ax25.h
index ad3d7626130e..9fc6a6657266 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -211,8 +211,6 @@ typedef struct {
unsigned short slave_timeout; /* when? */
} ax25_dama_info;
-struct ctl_table;
-
typedef struct ax25_dev {
struct list_head list;
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 5fdea5682756..85fbf1801cbe 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -4,9 +4,9 @@
*/
#include <linux/console.h>
#include <linux/types.h>
+#include <linux/sysctl.h>
#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
-struct ctl_table;
void __init printk_sysctl_init(void);
int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos);
diff --git a/kernel/printk/sysctl.c b/kernel/printk/sysctl.c
index da77f3f5c1fe..f15732e93c2e 100644
--- a/kernel/printk/sysctl.c
+++ b/kernel/printk/sysctl.c
@@ -3,7 +3,6 @@
* sysctl.c: General linux system control interface
*/
-#include <linux/sysctl.h>
#include <linux/printk.h>
#include <linux/capability.h>
#include <linux/ratelimit.h>
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 2cd767b9680e..9d3a666ffde1 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -354,29 +354,221 @@ static void proc_put_char(void **buf, size_t *size, char c)
}
}
-static SYSCTL_USER_TO_KERN_INT_CONV(, SYSCTL_CONV_IDENTITY)
-static SYSCTL_KERN_TO_USER_INT_CONV(, SYSCTL_CONV_IDENTITY)
+/**
+ * proc_uint_u2k_conv_uop - Assign user value to a kernel pointer
+ *
+ * @u_ptr: pointer to user space variable
+ * @k_ptr: pointer to kernel variable
+ * @u_ptr_op: execute this function before assigning to k_ptr
+ *
+ * Uses WRITE_ONCE to assign value to k_ptr. Executes u_ptr_op if
+ * not NULL. Check that the values are less than UINT_MAX to avoid
+ * having to support wrap around from userspace.
+ *
+ * returns 0 on success.
+ */
+int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
+ ulong (*u_ptr_op)(const ulong))
+{
+ ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr;
-static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv,
- sysctl_kern_to_user_int_conv, false)
-static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv,
- sysctl_kern_to_user_int_conv, true)
+ if (u > UINT_MAX)
+ return -EINVAL;
+ WRITE_ONCE(*k_ptr, u);
+ return 0;
+}
+
+/**
+ * proc_uint_k2u_conv - Assign kernel value to a user space pointer
+ *
+ * @u_ptr: pointer to user space variable
+ * @k_ptr: pointer to kernel variable
+ *
+ * Uses READ_ONCE to assign value to u_ptr.
+ *
+ * returns 0 on success.
+ */
+int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr)
+{
+ uint val = READ_ONCE(*k_ptr);
+ *u_ptr = (ulong)val;
+ return 0;
+}
+/**
+ * proc_uint_conv - Change user or kernel pointer based on direction
+ *
+ * @u_ptr: pointer to user variable
+ * @k_ptr: pointer to kernel variable
+ * @dir: %TRUE if this is a write to the sysctl file
+ * @tbl: the sysctl table
+ * @k_ptr_range_check: Check range for k_ptr when %TRUE
+ * @user_to_kern: Callback used to assign value from user to kernel var
+ * @kern_to_user: Callback used to assign value from kernel to user var
+ *
+ * When direction is kernel to user, then the u_ptr is modified.
+ * When direction is user to kernel, then the k_ptr is modified.
+ *
+ * Returns 0 on success
+ */
+int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
+ const struct ctl_table *tbl, bool k_ptr_range_check,
+ int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
+ int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr))
+{
+ if (SYSCTL_KERN_TO_USER(dir))
+ return kern_to_user(u_ptr, k_ptr);
+
+ if (k_ptr_range_check) {
+ uint tmp_k;
+ int ret;
+
+ if (!tbl)
+ return -EINVAL;
+ ret = user_to_kern(u_ptr, &tmp_k);
+ if (ret)
+ return ret;
+ if ((tbl->extra1 &&
+ *(uint *)tbl->extra1 > tmp_k) ||
+ (tbl->extra2 &&
+ *(uint *)tbl->extra2 < tmp_k))
+ return -ERANGE;
+ WRITE_ONCE(*k_ptr, tmp_k);
+ } else
+ return user_to_kern(u_ptr, k_ptr);
+ return 0;
+}
-static SYSCTL_USER_TO_KERN_UINT_CONV(, SYSCTL_CONV_IDENTITY)
+static int proc_uint_u2k_conv(const ulong *u_ptr, uint *k_ptr)
+{
+ return proc_uint_u2k_conv_uop(u_ptr, k_ptr, NULL);
+}
-int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr,
- const unsigned int *k_ptr)
+static int do_proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
+ const struct ctl_table *tbl)
{
- unsigned int val = READ_ONCE(*k_ptr);
- *u_ptr = (unsigned long)val;
+ return proc_uint_conv(u_ptr, k_ptr, dir, tbl, false,
+ proc_uint_u2k_conv, proc_uint_k2u_conv);
+}
+
+static int do_proc_uint_conv_minmax(ulong *u_ptr, uint *k_ptr, int dir,
+ const struct ctl_table *tbl)
+{
+ return proc_uint_conv(u_ptr, k_ptr, dir, tbl, true,
+ proc_uint_u2k_conv, proc_uint_k2u_conv);
+}
+
+/**
+ * proc_int_k2u_conv_kop - Assign kernel value to a user space pointer
+ * @u_ptr: pointer to user space variable
+ * @k_ptr: pointer to kernel variable
+ * @negp: assigned %TRUE if the converted kernel value is negative;
+ * %FALSE otherweise
+ * @k_ptr_op: execute this function before assigning to u_ptr
+ *
+ * Uses READ_ONCE to get value from k_ptr. Executes k_ptr_op before assigning
+ * to u_ptr if not NULL. Does **not** check for overflow.
+ *
+ * Returns: 0 on success.
+ */
+int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp,
+ ulong (*k_ptr_op)(const ulong))
+{
+ int val = READ_ONCE(*k_ptr);
+
+ if (val < 0) {
+ *negp = true;
+ *u_ptr = k_ptr_op ? -k_ptr_op((ulong)val) : -(ulong)val;
+ } else {
+ *negp = false;
+ *u_ptr = k_ptr_op ? k_ptr_op((ulong)val) : (ulong) val;
+ }
+ return 0;
+}
+
+/**
+ * proc_int_u2k_conv_uop - Assign user value to a kernel pointer
+ * @u_ptr: pointer to user space variable
+ * @k_ptr: pointer to kernel variable
+ * @negp: If %TRUE, the converted user value is made negative.
+ * @u_ptr_op: execute this function before assigning to k_ptr
+ *
+ * Uses WRITE_ONCE to assign value to k_ptr. Executes u_ptr_op if
+ * not NULL. Check for overflow with UINT_MAX.
+ *
+ * Returns: 0 on success.
+ */
+int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp,
+ ulong (*u_ptr_op)(const ulong))
+{
+ ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr;
+
+ if (*negp) {
+ if (u > (ulong) INT_MAX + 1)
+ return -EINVAL;
+ WRITE_ONCE(*k_ptr, -u);
+ } else {
+ if (u > (ulong) INT_MAX)
+ return -EINVAL;
+ WRITE_ONCE(*k_ptr, u);
+ }
+ return 0;
+}
+
+int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
+ const struct ctl_table *tbl, bool k_ptr_range_check,
+ int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr),
+ int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr))
+{
+ if (SYSCTL_KERN_TO_USER(dir))
+ return kern_to_user(negp, u_ptr, k_ptr);
+
+ if (k_ptr_range_check) {
+ int tmp_k, ret;
+
+ if (!tbl)
+ return -EINVAL;
+ ret = user_to_kern(negp, u_ptr, &tmp_k);
+ if (ret)
+ return ret;
+ if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
+ (tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
+ return -EINVAL;
+ WRITE_ONCE(*k_ptr, tmp_k);
+ } else
+ return user_to_kern(negp, u_ptr, k_ptr);
return 0;
}
-static SYSCTL_UINT_CONV_CUSTOM(, sysctl_user_to_kern_uint_conv,
- sysctl_kern_to_user_uint_conv, false)
-static SYSCTL_UINT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_uint_conv,
- sysctl_kern_to_user_uint_conv, true)
+
+
+static int sysctl_user_to_kern_int_conv(const bool *negp, const ulong *u_ptr,
+ int *k_ptr)
+{
+ return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, NULL);
+}
+
+static int sysctl_kern_to_user_int_conv(bool *negp, ulong *u_ptr, const int *k_ptr)
+{
+ return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, NULL);
+}
+
+static int do_proc_int_conv(bool *negp, unsigned long *u_ptr, int *k_ptr,
+ int dir, const struct ctl_table *tbl)
+{
+ return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
+ sysctl_user_to_kern_int_conv,
+ sysctl_kern_to_user_int_conv);
+
+}
+
+static int do_proc_int_conv_minmax(bool *negp, unsigned long *u_ptr, int *k_ptr,
+ int dir, const struct ctl_table *tbl)
+{
+ return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,
+ sysctl_user_to_kern_int_conv,
+ sysctl_kern_to_user_int_conv);
+}
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
@@ -568,6 +760,22 @@ static int do_proc_douintvec(const struct ctl_table *table, int dir,
return do_proc_douintvec_r(table, buffer, lenp, ppos, conv);
}
+/**
+ * proc_douintvec_conv - read a vector of unsigned ints with a custom converter
+ *
+ * @table: the sysctl table
+ * @dir: %TRUE if this is a write to the sysctl file
+ * @buffer: the user buffer
+ * @lenp: the size of the user buffer
+ * @ppos: file position
+ * @conv: Custom converter call back
+ *
+ * Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer
+ * values from/to the user buffer, treated as an ASCII string. Negative
+ * strings are not allowed.
+ *
+ * Returns 0 on success
+ */
int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *u_ptr, unsigned int *k_ptr,
@@ -576,7 +784,6 @@ int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer,
return do_proc_douintvec(table, dir, buffer, lenp, ppos, conv);
}
-
/**
* proc_dobool - read/write a bool
* @table: the sysctl table
@@ -692,10 +899,10 @@ int proc_dointvec_minmax(const struct ctl_table *table, int dir,
* values from/to the user buffer, treated as an ASCII string. Negative
* strings are not allowed.
*
- * This routine will ensure the values are within the range specified by
- * table->extra1 (min) and table->extra2 (max). There is a final sanity
- * check for UINT_MAX to avoid having to support wrap around uses from
- * userspace.
+ * When changing the kernel variable, this routine will ensure the values
+ * are within the range specified by table->extra1 (min) and table->extra2
+ * (max). And Check that the values are less than UINT_MAX to avoid having to
+ * support wrap around uses from userspace.
*
* Returns 0 on success or -ERANGE when range check failes and
* SYSCTL_USER_TO_KERN(dir) == true
@@ -862,6 +1069,22 @@ int proc_doulongvec_minmax(const struct ctl_table *table, int dir,
return proc_doulongvec_minmax_conv(table, dir, buffer, lenp, ppos, 1l, 1l);
}
+/**
+ * proc_dointvec_conv - read a vector of ints with a custom converter
+ * @table: the sysctl table
+ * @dir: %TRUE if this is a write to the sysctl file
+ * @buffer: the user buffer
+ * @lenp: the size of the user buffer
+ * @ppos: file position
+ * @conv: Custom converter call back
+ *
+ * Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer
+ * values from/to the user buffer, treated as an ASCII string. Negative
+ * strings are not allowed.
+ *
+ * Returns: 0 on success
+ */
+
int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
@@ -1055,6 +1278,33 @@ int proc_douintvec_minmax(const struct ctl_table *table, int dir,
return -ENOSYS;
}
+int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer,
+ size_t *lenp, loff_t *ppos,
+ int (*conv)(unsigned long *lvalp, unsigned int *valp,
+ int write, const struct ctl_table *table))
+{
+ return -ENOSYS;
+}
+
+int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr)
+{
+ return -ENOSYS;
+}
+
+int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
+ ulong (*u_ptr_op)(const ulong))
+{
+ return -ENOSYS;
+}
+
+int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
+ const struct ctl_table *tbl, bool k_ptr_range_check,
+ int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
+ int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr))
+{
+ return -ENOSYS;
+}
+
int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index d31a6d40d38d..a5c7d15fce72 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -100,26 +100,120 @@ void __init register_refined_jiffies(long cycles_per_second)
__clocksource_register(&refined_jiffies);
}
-#define SYSCTL_CONV_MULT_HZ(val) ((val) * HZ)
-#define SYSCTL_CONV_DIV_HZ(val) ((val) / HZ)
-
-static SYSCTL_USER_TO_KERN_INT_CONV(_hz, SYSCTL_CONV_MULT_HZ)
-static SYSCTL_KERN_TO_USER_INT_CONV(_hz, SYSCTL_CONV_DIV_HZ)
-static SYSCTL_USER_TO_KERN_INT_CONV(_userhz, clock_t_to_jiffies)
-static SYSCTL_KERN_TO_USER_INT_CONV(_userhz, jiffies_to_clock_t)
-static SYSCTL_USER_TO_KERN_INT_CONV(_ms, msecs_to_jiffies)
-static SYSCTL_KERN_TO_USER_INT_CONV(_ms, jiffies_to_msecs)
-
-static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_user_to_kern_int_conv_hz,
- sysctl_kern_to_user_int_conv_hz, false)
-static SYSCTL_INT_CONV_CUSTOM(_userhz_jiffies,
- sysctl_user_to_kern_int_conv_userhz,
- sysctl_kern_to_user_int_conv_userhz, false)
-static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_user_to_kern_int_conv_ms,
- sysctl_kern_to_user_int_conv_ms, false)
-static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies_minmax,
- sysctl_user_to_kern_int_conv_ms,
- sysctl_kern_to_user_int_conv_ms, true)
+#ifdef CONFIG_PROC_SYSCTL
+static ulong mult_hz(const ulong val)
+{
+ return val * HZ;
+}
+
+static ulong div_hz(const ulong val)
+{
+ return val / HZ;
+}
+
+static int sysctl_u2k_int_conv_hz(const bool *negp, const ulong *u_ptr, int *k_ptr)
+{
+ return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, mult_hz);
+}
+
+static int sysctl_k2u_int_conv_hz(bool *negp, ulong *u_ptr, const int *k_ptr)
+{
+ return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, div_hz);
+}
+
+static int sysctl_u2k_int_conv_userhz(const bool *negp, const ulong *u_ptr, int *k_ptr)
+{
+ return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, clock_t_to_jiffies);
+}
+
+static ulong sysctl_jiffies_to_clock_t(const ulong val)
+{
+ return jiffies_to_clock_t(val);
+}
+
+static int sysctl_k2u_int_conv_userhz(bool *negp, ulong *u_ptr, const int *k_ptr)
+{
+ return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_clock_t);
+}
+
+static ulong sysctl_msecs_to_jiffies(const ulong val)
+{
+ return msecs_to_jiffies(val);
+}
+
+static int sysctl_u2k_int_conv_ms(const bool *negp, const ulong *u_ptr, int *k_ptr)
+{
+ return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, sysctl_msecs_to_jiffies);
+}
+
+static ulong sysctl_jiffies_to_msecs(const ulong val)
+{
+ return jiffies_to_msecs(val);
+}
+
+static int sysctl_k2u_int_conv_ms(bool *negp, ulong *u_ptr, const int *k_ptr)
+{
+ return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_msecs);
+}
+
+static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
+ int dir, const struct ctl_table *tbl)
+{
+ return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
+ sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz);
+}
+
+static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
+ int *k_ptr, int dir,
+ const struct ctl_table *tbl)
+{
+ return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
+ sysctl_u2k_int_conv_userhz,
+ sysctl_k2u_int_conv_userhz);
+}
+
+static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
+ int dir, const struct ctl_table *tbl)
+{
+ return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
+ sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
+}
+
+static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
+ int *k_ptr, int dir,
+ const struct ctl_table *tbl)
+{
+ return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
+ sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
+}
+
+#else // CONFIG_PROC_SYSCTL
+static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
+ int dir, const struct ctl_table *tbl)
+{
+ return -ENOSYS;
+}
+
+static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
+ int *k_ptr, int dir,
+ const struct ctl_table *tbl)
+{
+ return -ENOSYS;
+}
+
+static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
+ int dir, const struct ctl_table *tbl)
+{
+ return -ENOSYS;
+}
+
+static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
+ int *k_ptr, int dir,
+ const struct ctl_table *tbl)
+{
+ return -ENOSYS;
+}
+#endif
/**
* proc_dointvec_jiffies - read a vector of integers as seconds
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 019840006096..6d5f3208f9ca 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -53,32 +53,29 @@ static DEFINE_SPINLOCK(pinned_root_spinlock);
static bool deny_reading_verity_digests;
#endif
+// initialized to false
+static bool loadpin_root_writable;
#ifdef CONFIG_SYSCTL
-static struct ctl_table loadpin_sysctl_table[] = {
+
+static int proc_handler_loadpin(const struct ctl_table *table, int dir,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ if (!loadpin_root_writable && SYSCTL_USER_TO_KERN(dir))
+ return -EINVAL;
+ return proc_dointvec_minmax(table, dir, buffer, lenp, ppos);
+}
+
+static const struct ctl_table loadpin_sysctl_table[] = {
{
.procname = "enforce",
.data = &enforce,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ONE,
+ .proc_handler = proc_handler_loadpin,
+ .extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
};
-
-static void set_sysctl(bool is_writable)
-{
- /*
- * If load pinning is not enforced via a read-only block
- * device, allow sysctl to change modes for testing.
- */
- if (is_writable)
- loadpin_sysctl_table[0].extra1 = SYSCTL_ZERO;
- else
- loadpin_sysctl_table[0].extra1 = SYSCTL_ONE;
-}
-#else
-static inline void set_sysctl(bool is_writable) { }
#endif
static void report_writable(struct super_block *mnt_sb, bool writable)
@@ -132,7 +129,6 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
struct super_block *load_root;
const char *origin = kernel_read_file_id_str(id);
bool first_root_pin = false;
- bool load_root_writable;
/* If the file id is excluded, ignore the pinning. */
if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) &&
@@ -153,7 +149,6 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
}
load_root = file->f_path.mnt->mnt_sb;
- load_root_writable = sb_is_writable(load_root);
/* First loaded module/firmware defines the root for all others. */
spin_lock(&pinned_root_spinlock);
@@ -169,8 +164,8 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
spin_unlock(&pinned_root_spinlock);
if (first_root_pin) {
- report_writable(pinned_root, load_root_writable);
- set_sysctl(load_root_writable);
+ loadpin_root_writable = sb_is_writable(pinned_root);
+ report_writable(pinned_root, loadpin_root_writable);
report_load(origin, file, "pinned");
}