* [PATCH 1/8] sysctl: Replace void pointer with const pointer to ctl_table
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
2025-10-13 13:24 ` [PATCH 2/8] sysctl: Remove superfluous tbl_data param from "dovec" functions Joel Granados
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
* Replace void* data in the converter functions with a const struct
ctl_table* table as it was only getting forwarding values from
ctl_table->extra{1,2}.
* Remove the void* data in the do_proc_* functions as they already had a
pointer to the ctl_table.
* Remove min/max structures do_proc_do{uint,int}vec_minmax_conv_param;
the min/max values get passed directly in ctl_table.
* Keep min/max initialization in extra{1,2} in proc_dou8vec_minmax.
* The do_proc_douintvec was adjusted outside sysctl.c as it is exported
to fs/pipe.c.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
fs/pipe.c | 6 +-
include/linux/sysctl.h | 5 +-
kernel/sysctl.c | 180 ++++++++++++++++++-------------------------------
3 files changed, 71 insertions(+), 120 deletions(-)
diff --git a/fs/pipe.c b/fs/pipe.c
index 731622d0738d41a9d918dc5048e95e38b3b0e049..2431f05cb788f5bd89660f0fc6f4c4696e17d5dd 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1480,8 +1480,8 @@ static struct file_system_type pipe_fs_type = {
#ifdef CONFIG_SYSCTL
static int do_proc_dopipe_max_size_conv(unsigned long *lvalp,
- unsigned int *valp,
- int write, void *data)
+ unsigned int *valp, int write,
+ const struct ctl_table *table)
{
if (write) {
unsigned int val;
@@ -1503,7 +1503,7 @@ static int proc_dopipe_max_size(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_douintvec(table, write, buffer, lenp, ppos,
- do_proc_dopipe_max_size_conv, NULL);
+ do_proc_dopipe_max_size_conv);
}
static const struct ctl_table fs_pipe_sysctls[] = {
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 92e9146b1104123d3dc0ff004bd681861e297581..2d3d6c141b0b0aee21f2708450b7b41d8041a8cb 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -238,9 +238,8 @@ bool sysctl_is_alias(char *param);
int do_proc_douintvec(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, void *data),
- void *data);
+ unsigned int *valp, int write,
+ const struct ctl_table *table));
extern int unaligned_enabled;
extern int no_unaligned_warning;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index cb6196e3fa993daa21704d190baf366084e014f7..f0a691ffb29067a019a857a62fa56185aab06c61 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -355,8 +355,8 @@ static void proc_put_char(void **buf, size_t *size, char c)
}
static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
- int *valp,
- int write, void *data)
+ int *valp, int write,
+ const struct ctl_table *table)
{
if (write) {
if (*negp) {
@@ -382,8 +382,8 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
}
static int do_proc_douintvec_conv(unsigned long *lvalp,
- unsigned int *valp,
- int write, void *data)
+ unsigned int *valp, int write,
+ const struct ctl_table *table)
{
if (write) {
if (*lvalp > UINT_MAX)
@@ -402,8 +402,7 @@ static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table,
int write, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
- int write, void *data),
- void *data)
+ int write, const struct ctl_table *table))
{
int *i, vleft, first = 1, err = 0;
size_t left;
@@ -444,12 +443,12 @@ static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table,
sizeof(proc_wspace_sep), NULL);
if (err)
break;
- if (conv(&neg, &lval, i, 1, data)) {
+ if (conv(&neg, &lval, i, 1, table)) {
err = -EINVAL;
break;
}
} else {
- if (conv(&neg, &lval, i, 0, data)) {
+ if (conv(&neg, &lval, i, 0, table)) {
err = -EINVAL;
break;
}
@@ -474,11 +473,10 @@ static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table,
static int do_proc_dointvec(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
- int write, void *data),
- void *data)
+ int write, const struct ctl_table *table))
{
return __do_proc_dointvec(table->data, table, write,
- buffer, lenp, ppos, conv, data);
+ buffer, lenp, ppos, conv);
}
static int do_proc_douintvec_w(unsigned int *tbl_data,
@@ -486,9 +484,8 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
- unsigned int *valp,
- int write, void *data),
- void *data)
+ unsigned int *valp, int write,
+ const struct ctl_table *table))
{
unsigned long lval;
int err = 0;
@@ -518,7 +515,7 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
goto out_free;
}
- if (conv(&lval, tbl_data, 1, data)) {
+ if (conv(&lval, tbl_data, 1, table)) {
err = -EINVAL;
goto out_free;
}
@@ -538,12 +535,12 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
return err;
}
-static int do_proc_douintvec_r(unsigned int *tbl_data, void *buffer,
+static int do_proc_douintvec_r(unsigned int *tbl_data,
+ const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
- unsigned int *valp,
- int write, void *data),
- void *data)
+ unsigned int *valp, int write,
+ const struct ctl_table *table))
{
unsigned long lval;
int err = 0;
@@ -551,7 +548,7 @@ static int do_proc_douintvec_r(unsigned int *tbl_data, void *buffer,
left = *lenp;
- if (conv(&lval, tbl_data, 0, data)) {
+ if (conv(&lval, tbl_data, 0, table)) {
err = -EINVAL;
goto out;
}
@@ -573,9 +570,8 @@ static int __do_proc_douintvec(void *tbl_data, 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, void *data),
- void *data)
+ unsigned int *valp, int write,
+ const struct ctl_table *table))
{
unsigned int *i, vleft;
@@ -601,19 +597,18 @@ static int __do_proc_douintvec(void *tbl_data, const struct ctl_table *table,
if (write)
return do_proc_douintvec_w(i, table, buffer, lenp, ppos,
- conv, data);
- return do_proc_douintvec_r(i, buffer, lenp, ppos, conv, data);
+ conv);
+ return do_proc_douintvec_r(i, table, buffer, lenp, ppos, conv);
}
int do_proc_douintvec(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, void *data),
- void *data)
+ unsigned int *valp, int write,
+ const struct ctl_table *table))
{
- return __do_proc_douintvec(table->data, table, write,
- buffer, lenp, ppos, conv, data);
+ return __do_proc_douintvec(table->data, table, write, buffer, lenp,
+ ppos, conv);
}
/**
@@ -672,7 +667,7 @@ int proc_dobool(const struct ctl_table *table, int write, void *buffer,
int proc_dointvec(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL, NULL);
+ return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL);
}
/**
@@ -692,42 +687,28 @@ int proc_douintvec(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{
return do_proc_douintvec(table, write, buffer, lenp, ppos,
- do_proc_douintvec_conv, NULL);
+ do_proc_douintvec_conv);
}
-/**
- * struct do_proc_dointvec_minmax_conv_param - proc_dointvec_minmax() range checking structure
- * @min: pointer to minimum allowable value
- * @max: pointer to maximum allowable value
- *
- * The do_proc_dointvec_minmax_conv_param structure provides the
- * minimum and maximum values for doing range checking for those sysctl
- * parameters that use the proc_dointvec_minmax() handler.
- */
-struct do_proc_dointvec_minmax_conv_param {
- int *min;
- int *max;
-};
-
static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
- int *valp,
- int write, void *data)
+ int *valp, int write,
+ const struct ctl_table *table)
{
- int tmp, ret;
- struct do_proc_dointvec_minmax_conv_param *param = data;
+ int tmp, ret, *min, *max;
/*
* If writing, first do so via a temporary local int so we can
* bounds-check it before touching *valp.
*/
int *ip = write ? &tmp : valp;
- ret = do_proc_dointvec_conv(negp, lvalp, ip, write, data);
+ ret = do_proc_dointvec_conv(negp, lvalp, ip, write, table);
if (ret)
return ret;
if (write) {
- if ((param->min && *param->min > tmp) ||
- (param->max && *param->max < tmp))
+ min = (int *) table->extra1;
+ max = (int *) table->extra2;
+ if ((min && *min > tmp) || (max && *max < tmp))
return -EINVAL;
WRITE_ONCE(*valp, tmp);
}
@@ -754,45 +735,27 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
int proc_dointvec_minmax(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
- struct do_proc_dointvec_minmax_conv_param param = {
- .min = (int *) table->extra1,
- .max = (int *) table->extra2,
- };
return do_proc_dointvec(table, write, buffer, lenp, ppos,
- do_proc_dointvec_minmax_conv, ¶m);
+ do_proc_dointvec_minmax_conv);
}
-/**
- * struct do_proc_douintvec_minmax_conv_param - proc_douintvec_minmax() range checking structure
- * @min: pointer to minimum allowable value
- * @max: pointer to maximum allowable value
- *
- * The do_proc_douintvec_minmax_conv_param structure provides the
- * minimum and maximum values for doing range checking for those sysctl
- * parameters that use the proc_douintvec_minmax() handler.
- */
-struct do_proc_douintvec_minmax_conv_param {
- unsigned int *min;
- unsigned int *max;
-};
-
static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
- unsigned int *valp,
- int write, void *data)
+ unsigned int *valp, int write,
+ const struct ctl_table *table)
{
int ret;
- unsigned int tmp;
- struct do_proc_douintvec_minmax_conv_param *param = data;
+ unsigned int tmp, *min, *max;
/* write via temporary local uint for bounds-checking */
unsigned int *up = write ? &tmp : valp;
- ret = do_proc_douintvec_conv(lvalp, up, write, data);
+ ret = do_proc_douintvec_conv(lvalp, up, write, table);
if (ret)
return ret;
if (write) {
- if ((param->min && *param->min > tmp) ||
- (param->max && *param->max < tmp))
+ min = (unsigned int *) table->extra1;
+ max = (unsigned int *) table->extra2;
+ if ((min && *min > tmp) || (max && *max < tmp))
return -ERANGE;
WRITE_ONCE(*valp, tmp);
@@ -823,12 +786,8 @@ static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
int proc_douintvec_minmax(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
- struct do_proc_douintvec_minmax_conv_param param = {
- .min = (unsigned int *) table->extra1,
- .max = (unsigned int *) table->extra2,
- };
return do_proc_douintvec(table, write, buffer, lenp, ppos,
- do_proc_douintvec_minmax_conv, ¶m);
+ do_proc_douintvec_minmax_conv);
}
/**
@@ -854,28 +813,24 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int write,
struct ctl_table tmp;
unsigned int min = 0, max = 255U, val;
u8 *data = table->data;
- struct do_proc_douintvec_minmax_conv_param param = {
- .min = &min,
- .max = &max,
- };
int res;
/* Do not support arrays yet. */
if (table->maxlen != sizeof(u8))
return -EINVAL;
- if (table->extra1)
- min = *(unsigned int *) table->extra1;
- if (table->extra2)
- max = *(unsigned int *) table->extra2;
-
tmp = *table;
tmp.maxlen = sizeof(val);
tmp.data = &val;
+ if (!tmp.extra1)
+ tmp.extra1 = (unsigned int *) &min;
+ if (!tmp.extra2)
+ tmp.extra2 = (unsigned int *) &max;
+
val = READ_ONCE(*data);
res = do_proc_douintvec(&tmp, write, buffer, lenp, ppos,
- do_proc_douintvec_minmax_conv, ¶m);
+ do_proc_douintvec_minmax_conv);
if (res)
return res;
if (write)
@@ -1014,8 +969,8 @@ int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int write,
static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp,
- int write, void *data)
+ int *valp, int write,
+ const struct ctl_table *table)
{
if (write) {
if (*lvalp > INT_MAX / HZ)
@@ -1040,8 +995,8 @@ static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
}
static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp,
- int write, void *data)
+ int *valp, int write,
+ const struct ctl_table *table)
{
if (write) {
if (USER_HZ < HZ && *lvalp > (LONG_MAX / HZ) * USER_HZ)
@@ -1063,8 +1018,8 @@ static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp
}
static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp,
- int write, void *data)
+ int *valp, int write,
+ const struct ctl_table *table)
{
if (write) {
unsigned long jif = msecs_to_jiffies(*negp ? -*lvalp : *lvalp);
@@ -1088,23 +1043,24 @@ static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
}
static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lvalp,
- int *valp, int write, void *data)
+ int *valp, int write,
+ const struct ctl_table *table)
{
- int tmp, ret;
- struct do_proc_dointvec_minmax_conv_param *param = data;
+ int tmp, ret, *min, *max;
/*
* If writing, first do so via a temporary local int so we can
* bounds-check it before touching *valp.
*/
int *ip = write ? &tmp : valp;
- ret = do_proc_dointvec_ms_jiffies_conv(negp, lvalp, ip, write, data);
+ ret = do_proc_dointvec_ms_jiffies_conv(negp, lvalp, ip, write, table);
if (ret)
return ret;
if (write) {
- if ((param->min && *param->min > tmp) ||
- (param->max && *param->max < tmp))
+ min = (int *) table->extra1;
+ max = (int *) table->extra2;
+ if ((min && *min > tmp) || (max && *max < tmp))
return -EINVAL;
*valp = tmp;
}
@@ -1130,18 +1086,14 @@ int proc_dointvec_jiffies(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table,write,buffer,lenp,ppos,
- do_proc_dointvec_jiffies_conv,NULL);
+ do_proc_dointvec_jiffies_conv);
}
int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
- struct do_proc_dointvec_minmax_conv_param param = {
- .min = (int *) table->extra1,
- .max = (int *) table->extra2,
- };
return do_proc_dointvec(table, write, buffer, lenp, ppos,
- do_proc_dointvec_ms_jiffies_minmax_conv, ¶m);
+ do_proc_dointvec_ms_jiffies_minmax_conv);
}
/**
@@ -1163,7 +1115,7 @@ int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, write, buffer, lenp, ppos,
- do_proc_dointvec_userhz_jiffies_conv, NULL);
+ do_proc_dointvec_userhz_jiffies_conv);
}
/**
@@ -1185,7 +1137,7 @@ int proc_dointvec_ms_jiffies(const struct ctl_table *table, int write, void *buf
size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, write, buffer, lenp, ppos,
- do_proc_dointvec_ms_jiffies_conv, NULL);
+ do_proc_dointvec_ms_jiffies_conv);
}
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 2/8] sysctl: Remove superfluous tbl_data param from "dovec" functions
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
2025-10-13 13:24 ` [PATCH 1/8] sysctl: Replace void pointer with const pointer to ctl_table Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
2025-10-13 13:24 ` [PATCH 3/8] sysctl: Remove superfluous __do_proc_* indirection Joel Granados
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
Remove superfluous tbl_data param from do_proc_douintvec{,_r,_w}
and __do_proc_do{intvec,uintvec,ulongvec_minmax}. There is no need to
pass it as it is always contained within the ctl_table struct.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
kernel/sysctl.c | 61 ++++++++++++++++++++++++---------------------------------
1 file changed, 26 insertions(+), 35 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f0a691ffb29067a019a857a62fa56185aab06c61..0e249a1f99fec084db649782f5ef8b37e40c6a7c 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -398,22 +398,22 @@ static int do_proc_douintvec_conv(unsigned long *lvalp,
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
-static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table,
- int write, void *buffer,
- size_t *lenp, loff_t *ppos,
- int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
- int write, const struct ctl_table *table))
+static int __do_proc_dointvec(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos,
+ int (*conv)(bool *negp, unsigned long *lvalp,
+ int *valp, int write,
+ const struct ctl_table *table))
{
int *i, vleft, first = 1, err = 0;
size_t left;
char *p;
- if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
- i = (int *) tbl_data;
+ i = (int *) table->data;
vleft = table->maxlen / sizeof(*i);
left = *lenp;
@@ -475,13 +475,10 @@ static int do_proc_dointvec(const struct ctl_table *table, int write,
int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
int write, const struct ctl_table *table))
{
- return __do_proc_dointvec(table->data, table, write,
- buffer, lenp, ppos, conv);
+ return __do_proc_dointvec(table, write, buffer, lenp, ppos, conv);
}
-static int do_proc_douintvec_w(unsigned int *tbl_data,
- const struct ctl_table *table,
- void *buffer,
+static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
unsigned int *valp, int write,
@@ -515,7 +512,7 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
goto out_free;
}
- if (conv(&lval, tbl_data, 1, table)) {
+ if (conv(&lval, (unsigned int *) table->data, 1, table)) {
err = -EINVAL;
goto out_free;
}
@@ -535,8 +532,7 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
return err;
}
-static int do_proc_douintvec_r(unsigned int *tbl_data,
- const struct ctl_table *table, void *buffer,
+static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
unsigned int *valp, int write,
@@ -548,7 +544,7 @@ static int do_proc_douintvec_r(unsigned int *tbl_data,
left = *lenp;
- if (conv(&lval, tbl_data, 0, table)) {
+ if (conv(&lval, (unsigned int *) table->data, 0, table)) {
err = -EINVAL;
goto out;
}
@@ -566,22 +562,20 @@ static int do_proc_douintvec_r(unsigned int *tbl_data,
return err;
}
-static int __do_proc_douintvec(void *tbl_data, const struct ctl_table *table,
- int write, void *buffer,
- size_t *lenp, loff_t *ppos,
+static int __do_proc_douintvec(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))
{
- unsigned int *i, vleft;
+ unsigned int vleft;
- if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
- i = (unsigned int *) tbl_data;
- vleft = table->maxlen / sizeof(*i);
+ vleft = table->maxlen / sizeof(unsigned int);
/*
* Arrays are not supported, keep this simple. *Do not* add
@@ -596,9 +590,8 @@ static int __do_proc_douintvec(void *tbl_data, const struct ctl_table *table,
conv = do_proc_douintvec_conv;
if (write)
- return do_proc_douintvec_w(i, table, buffer, lenp, ppos,
- conv);
- return do_proc_douintvec_r(i, table, buffer, lenp, ppos, conv);
+ return do_proc_douintvec_w(table, buffer, lenp, ppos, conv);
+ return do_proc_douintvec_r(table, buffer, lenp, ppos, conv);
}
int do_proc_douintvec(const struct ctl_table *table, int write,
@@ -607,8 +600,7 @@ int do_proc_douintvec(const struct ctl_table *table, int write,
unsigned int *valp, int write,
const struct ctl_table *table))
{
- return __do_proc_douintvec(table->data, table, write, buffer, lenp,
- ppos, conv);
+ return __do_proc_douintvec(table, write, buffer, lenp, ppos, conv);
}
/**
@@ -839,9 +831,8 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int write,
}
EXPORT_SYMBOL_GPL(proc_dou8vec_minmax);
-static int __do_proc_doulongvec_minmax(void *data,
- const struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos,
+static int __do_proc_doulongvec_minmax(const struct ctl_table *table,
+ int write, void *buffer, size_t *lenp, loff_t *ppos,
unsigned long convmul, unsigned long convdiv)
{
unsigned long *i, *min, *max;
@@ -849,12 +840,12 @@ static int __do_proc_doulongvec_minmax(void *data,
size_t left;
char *p;
- if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
- i = data;
+ i = table->data;
min = table->extra1;
max = table->extra2;
vleft = table->maxlen / sizeof(unsigned long);
@@ -917,8 +908,8 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul,
unsigned long convdiv)
{
- return __do_proc_doulongvec_minmax(table->data, table, write,
- buffer, lenp, ppos, convmul, convdiv);
+ return __do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos,
+ convmul, convdiv);
}
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/8] sysctl: Remove superfluous __do_proc_* indirection
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
2025-10-13 13:24 ` [PATCH 1/8] sysctl: Replace void pointer with const pointer to ctl_table Joel Granados
2025-10-13 13:24 ` [PATCH 2/8] sysctl: Remove superfluous tbl_data param from "dovec" functions Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
2025-10-13 13:24 ` [PATCH 4/8] sysctl: Indicate the direction of operation with macro names Joel Granados
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
Remove "__" from __do_proc_do{intvec,uintvec,ulongvec_minmax} internal
functions and delete their corresponding do_proc_do* wrappers. These
indirections are unnecessary as they do not add extra logic nor do they
indicate a layer separation.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
kernel/sysctl.c | 52 +++++++++++++---------------------------------------
1 file changed, 13 insertions(+), 39 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 0e249a1f99fec084db649782f5ef8b37e40c6a7c..9b042d81fd1c6a32f60e2834a98d48c1bc348de0 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -398,11 +398,11 @@ static int do_proc_douintvec_conv(unsigned long *lvalp,
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
-static int __do_proc_dointvec(const struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos,
- int (*conv)(bool *negp, unsigned long *lvalp,
- int *valp, int write,
- const struct ctl_table *table))
+
+static int do_proc_dointvec(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos,
+ int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
+ int write, const struct ctl_table *table))
{
int *i, vleft, first = 1, err = 0;
size_t left;
@@ -470,14 +470,6 @@ static int __do_proc_dointvec(const struct ctl_table *table, int write,
return err;
}
-static int do_proc_dointvec(const struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos,
- int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
- int write, const struct ctl_table *table))
-{
- return __do_proc_dointvec(table, write, buffer, lenp, ppos, conv);
-}
-
static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
@@ -526,7 +518,6 @@ static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
return 0;
- /* This is in keeping with old __do_proc_dointvec() */
bail_early:
*ppos += *lenp;
return err;
@@ -562,11 +553,10 @@ static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer,
return err;
}
-static int __do_proc_douintvec(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 do_proc_douintvec(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))
{
unsigned int vleft;
@@ -594,15 +584,6 @@ static int __do_proc_douintvec(const struct ctl_table *table, int write,
return do_proc_douintvec_r(table, buffer, lenp, ppos, conv);
}
-int do_proc_douintvec(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 __do_proc_douintvec(table, write, buffer, lenp, ppos, conv);
-}
-
/**
* proc_dobool - read/write a bool
* @table: the sysctl table
@@ -831,9 +812,10 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int write,
}
EXPORT_SYMBOL_GPL(proc_dou8vec_minmax);
-static int __do_proc_doulongvec_minmax(const struct ctl_table *table,
- int write, void *buffer, size_t *lenp, loff_t *ppos,
- unsigned long convmul, unsigned long convdiv)
+static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos,
+ unsigned long convmul,
+ unsigned long convdiv)
{
unsigned long *i, *min, *max;
int vleft, first = 1, err = 0;
@@ -904,14 +886,6 @@ static int __do_proc_doulongvec_minmax(const struct ctl_table *table,
return err;
}
-static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul,
- unsigned long convdiv)
-{
- return __do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos,
- convmul, convdiv);
-}
-
/**
* proc_doulongvec_minmax - read a vector of long integers with min/max values
* @table: the sysctl table
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/8] sysctl: Indicate the direction of operation with macro names
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
` (2 preceding siblings ...)
2025-10-13 13:24 ` [PATCH 3/8] sysctl: Remove superfluous __do_proc_* indirection Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
2025-10-13 13:24 ` [PATCH 5/8] sysctl: Discriminate between kernel and user converter params Joel Granados
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
Replace the "write" integer parameter with SYSCTL_USER_TO_KERN() and
SYSCTL_KERN_TO_USER() that clearly indicate data flow direction in
sysctl operations.
"write" originates in proc_sysctl.c (proc_sys_{read,write}) and can take
one of two values: "0" or "1" when called from proc_sys_read and
proc_sys_write respectively. When write has a value of zero, data is
"written" to a user space buffer from a kernel variable (usually
ctl_table->data). Whereas when write has a value greater than zero, data
is "written" to an internal kernel variable from a user space buffer.
Remove this ambiguity by introducing macros that clearly indicate the
direction of the "write".
The write mode names in sysctl_writes_mode are left unchanged as these
directly relate to the sysctl_write_strict file in /proc/sys where the
word "write" unambiguously refers to writing to a file.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
kernel/sysctl.c | 252 ++++++++++++++++++++++++++++++--------------------------
1 file changed, 136 insertions(+), 116 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 9b042d81fd1c6a32f60e2834a98d48c1bc348de0..69148fe7359994b85c076b7b6750792b2d1c751e 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -30,6 +30,19 @@ EXPORT_SYMBOL(sysctl_vals);
const unsigned long sysctl_long_vals[] = { 0, 1, LONG_MAX };
EXPORT_SYMBOL_GPL(sysctl_long_vals);
+/**
+ *
+ * "dir" originates from read_iter (dir = 0) or write_iter (dir = 1)
+ * in the file_operations struct at proc/proc_sysctl.c. Its value means
+ * one of two things for sysctl:
+ * 1. SYSCTL_USER_TO_KERN(dir) Writing to an internal kernel variable from user
+ * space (dir > 0)
+ * 2. SYSCTL_KERN_TO_USER(dir) Writing to a user space buffer from a kernel
+ * variable (dir == 0).
+ */
+#define SYSCTL_USER_TO_KERN(dir) (!!(dir))
+#define SYSCTL_KERN_TO_USER(dir) (!dir)
+
#if defined(CONFIG_SYSCTL)
/* Constants used for minimum and maximum */
@@ -55,7 +68,8 @@ static const int cap_last_cap = CAP_LAST_CAP;
* 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.
+ * updating internal kernel (SYSCTL_USER_TO_KERN) sysctl values through the proc
+ * interface on each write.
*/
enum sysctl_writes_mode {
SYSCTL_WRITES_LEGACY = -1,
@@ -73,7 +87,7 @@ static enum sysctl_writes_mode sysctl_writes_strict = SYSCTL_WRITES_STRICT;
#ifdef CONFIG_PROC_SYSCTL
-static int _proc_do_string(char *data, int maxlen, int write,
+static int _proc_do_string(char *data, int maxlen, int dir,
char *buffer, size_t *lenp, loff_t *ppos)
{
size_t len;
@@ -84,7 +98,7 @@ static int _proc_do_string(char *data, int maxlen, int write,
return 0;
}
- if (write) {
+ 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);
@@ -172,7 +186,7 @@ static bool proc_first_pos_non_zero_ignore(loff_t *ppos,
/**
* proc_dostring - read a string sysctl
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -186,13 +200,13 @@ static bool proc_first_pos_non_zero_ignore(loff_t *ppos,
*
* Returns 0 on success.
*/
-int proc_dostring(const struct ctl_table *table, int write,
+int proc_dostring(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- if (write)
+ if (SYSCTL_USER_TO_KERN(dir))
proc_first_pos_non_zero_ignore(ppos, table);
- return _proc_do_string(table->data, table->maxlen, write, buffer, lenp,
+ return _proc_do_string(table->data, table->maxlen, dir, buffer, lenp,
ppos);
}
@@ -355,10 +369,10 @@ static void proc_put_char(void **buf, size_t *size, char c)
}
static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
- int *valp, int write,
+ int *valp, int dir,
const struct ctl_table *table)
{
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
if (*negp) {
if (*lvalp > (unsigned long) INT_MAX + 1)
return -EINVAL;
@@ -382,10 +396,10 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
}
static int do_proc_douintvec_conv(unsigned long *lvalp,
- unsigned int *valp, int write,
+ unsigned int *valp, int dir,
const struct ctl_table *table)
{
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
if (*lvalp > UINT_MAX)
return -EINVAL;
WRITE_ONCE(*valp, *lvalp);
@@ -399,16 +413,17 @@ static int do_proc_douintvec_conv(unsigned long *lvalp,
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
-static int do_proc_dointvec(const struct ctl_table *table, int write,
+static int do_proc_dointvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
- int write, const struct ctl_table *table))
+ int dir, const struct ctl_table *table))
{
int *i, vleft, first = 1, err = 0;
size_t left;
char *p;
- if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ if (!table->data || !table->maxlen || !*lenp ||
+ (*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
@@ -420,7 +435,7 @@ static int do_proc_dointvec(const struct ctl_table *table, int write,
if (!conv)
conv = do_proc_dointvec_conv;
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
if (proc_first_pos_non_zero_ignore(ppos, table))
goto out;
@@ -433,7 +448,7 @@ static int do_proc_dointvec(const struct ctl_table *table, int write,
unsigned long lval;
bool neg;
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
proc_skip_spaces(&p, &left);
if (!left)
@@ -458,11 +473,11 @@ static int do_proc_dointvec(const struct ctl_table *table, int write,
}
}
- if (!write && !first && left && !err)
+ if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err)
proc_put_char(&buffer, &left, '\n');
- if (write && !err && left)
+ if (SYSCTL_USER_TO_KERN(dir) && !err && left)
proc_skip_spaces(&p, &left);
- if (write && first)
+ if (SYSCTL_USER_TO_KERN(dir) && first)
return err ? : -EINVAL;
*lenp -= left;
out:
@@ -473,7 +488,7 @@ static int do_proc_dointvec(const struct ctl_table *table, int write,
static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
- unsigned int *valp, int write,
+ unsigned int *valp, int dir,
const struct ctl_table *table))
{
unsigned long lval;
@@ -526,7 +541,7 @@ static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
- unsigned int *valp, int write,
+ unsigned int *valp, int dir,
const struct ctl_table *table))
{
unsigned long lval;
@@ -553,14 +568,15 @@ static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer,
return err;
}
-int do_proc_douintvec(const struct ctl_table *table, int write, void *buffer,
+int do_proc_douintvec(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp, unsigned int *valp,
- int write, const struct ctl_table *table))
+ int dir, const struct ctl_table *table))
{
unsigned int vleft;
- if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ if (!table->data || !table->maxlen || !*lenp ||
+ (*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
@@ -579,7 +595,7 @@ int do_proc_douintvec(const struct ctl_table *table, int write, void *buffer,
if (!conv)
conv = do_proc_douintvec_conv;
- if (write)
+ if (SYSCTL_USER_TO_KERN(dir))
return do_proc_douintvec_w(table, buffer, lenp, ppos, conv);
return do_proc_douintvec_r(table, buffer, lenp, ppos, conv);
}
@@ -587,7 +603,7 @@ int do_proc_douintvec(const struct ctl_table *table, int write, void *buffer,
/**
* proc_dobool - read/write a bool
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -600,7 +616,7 @@ int do_proc_douintvec(const struct ctl_table *table, int write, void *buffer,
*
* Returns 0 on success.
*/
-int proc_dobool(const struct ctl_table *table, int write, void *buffer,
+int proc_dobool(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
struct ctl_table tmp;
@@ -616,10 +632,10 @@ int proc_dobool(const struct ctl_table *table, int write, void *buffer,
tmp.data = &val;
val = READ_ONCE(*data);
- res = proc_dointvec(&tmp, write, buffer, lenp, ppos);
+ res = proc_dointvec(&tmp, dir, buffer, lenp, ppos);
if (res)
return res;
- if (write)
+ if (SYSCTL_USER_TO_KERN(dir))
WRITE_ONCE(*data, val);
return 0;
}
@@ -627,7 +643,7 @@ int proc_dobool(const struct ctl_table *table, int write, void *buffer,
/**
* proc_dointvec - read a vector of integers
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -637,16 +653,16 @@ int proc_dobool(const struct ctl_table *table, int write, void *buffer,
*
* Returns 0 on success.
*/
-int proc_dointvec(const struct ctl_table *table, int write, void *buffer,
+int proc_dointvec(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL);
+ return do_proc_dointvec(table, dir, buffer, lenp, ppos, NULL);
}
/**
* proc_douintvec - read a vector of unsigned integers
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -656,15 +672,15 @@ int proc_dointvec(const struct ctl_table *table, int write, void *buffer,
*
* Returns 0 on success.
*/
-int proc_douintvec(const struct ctl_table *table, int write, void *buffer,
+int proc_douintvec(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
- return do_proc_douintvec(table, write, buffer, lenp, ppos,
+ return do_proc_douintvec(table, dir, buffer, lenp, ppos,
do_proc_douintvec_conv);
}
static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
- int *valp, int write,
+ int *valp, int dir,
const struct ctl_table *table)
{
int tmp, ret, *min, *max;
@@ -672,13 +688,13 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
* If writing, first do so via a temporary local int so we can
* bounds-check it before touching *valp.
*/
- int *ip = write ? &tmp : valp;
+ int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : valp;
- ret = do_proc_dointvec_conv(negp, lvalp, ip, write, table);
+ ret = do_proc_dointvec_conv(negp, lvalp, ip, dir, table);
if (ret)
return ret;
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
min = (int *) table->extra1;
max = (int *) table->extra2;
if ((min && *min > tmp) || (max && *max < tmp))
@@ -692,7 +708,7 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
/**
* proc_dointvec_minmax - read a vector of integers with min/max values
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -703,29 +719,30 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
* This routine will ensure the values are within the range specified by
* table->extra1 (min) and table->extra2 (max).
*
- * Returns 0 on success or -EINVAL on write when the range check fails.
+ * Returns 0 on success or -EINVAL when the range check fails and
+ * SYSCTL_USER_TO_KERN(dir) == true
*/
-int proc_dointvec_minmax(const struct ctl_table *table, int write,
+int proc_dointvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table, write, buffer, lenp, ppos,
+ return do_proc_dointvec(table, dir, buffer, lenp, ppos,
do_proc_dointvec_minmax_conv);
}
static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
- unsigned int *valp, int write,
+ unsigned int *valp, int dir,
const struct ctl_table *table)
{
int ret;
unsigned int tmp, *min, *max;
- /* write via temporary local uint for bounds-checking */
- unsigned int *up = write ? &tmp : valp;
+ /* When writing to the kernel use a temp local uint for bounds-checking */
+ unsigned int *up = SYSCTL_USER_TO_KERN(dir) ? &tmp : valp;
- ret = do_proc_douintvec_conv(lvalp, up, write, table);
+ ret = do_proc_douintvec_conv(lvalp, up, dir, table);
if (ret)
return ret;
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
min = (unsigned int *) table->extra1;
max = (unsigned int *) table->extra2;
if ((min && *min > tmp) || (max && *max < tmp))
@@ -740,7 +757,7 @@ static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
/**
* proc_douintvec_minmax - read a vector of unsigned ints with min/max values
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -754,19 +771,20 @@ static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
* check for UINT_MAX to avoid having to support wrap around uses from
* userspace.
*
- * Returns 0 on success or -ERANGE on write when the range check fails.
+ * Returns 0 on success or -ERANGE when range check failes and
+ * SYSCTL_USER_TO_KERN(dir) == true
*/
-int proc_douintvec_minmax(const struct ctl_table *table, int write,
+int proc_douintvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_douintvec(table, write, buffer, lenp, ppos,
+ return do_proc_douintvec(table, dir, buffer, lenp, ppos,
do_proc_douintvec_minmax_conv);
}
/**
* proc_dou8vec_minmax - read a vector of unsigned chars with min/max values
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -778,9 +796,10 @@ int proc_douintvec_minmax(const struct ctl_table *table, int write,
* This routine will ensure the values are within the range specified by
* table->extra1 (min) and table->extra2 (max).
*
- * Returns 0 on success or an error on write when the range check fails.
+ * Returns 0 on success or an error on SYSCTL_USER_TO_KERN(dir) == true
+ * and the range check fails.
*/
-int proc_dou8vec_minmax(const struct ctl_table *table, int write,
+int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct ctl_table tmp;
@@ -802,17 +821,17 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int write,
tmp.extra2 = (unsigned int *) &max;
val = READ_ONCE(*data);
- res = do_proc_douintvec(&tmp, write, buffer, lenp, ppos,
+ res = do_proc_douintvec(&tmp, dir, buffer, lenp, ppos,
do_proc_douintvec_minmax_conv);
if (res)
return res;
- if (write)
+ if (SYSCTL_USER_TO_KERN(dir))
WRITE_ONCE(*data, val);
return 0;
}
EXPORT_SYMBOL_GPL(proc_dou8vec_minmax);
-static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
+static int do_proc_doulongvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
unsigned long convmul,
unsigned long convdiv)
@@ -822,7 +841,8 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
size_t left;
char *p;
- if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ if (!table->data || !table->maxlen || !*lenp ||
+ (*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
@@ -833,7 +853,7 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
vleft = table->maxlen / sizeof(unsigned long);
left = *lenp;
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
if (proc_first_pos_non_zero_ignore(ppos, table))
goto out;
@@ -845,7 +865,7 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
for (; left && vleft--; i++, first = 0) {
unsigned long val;
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
bool neg;
proc_skip_spaces(&p, &left);
@@ -874,11 +894,11 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
}
}
- if (!write && !first && left && !err)
+ if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err)
proc_put_char(&buffer, &left, '\n');
- if (write && !err)
+ if (SYSCTL_USER_TO_KERN(dir) && !err)
proc_skip_spaces(&p, &left);
- if (write && first)
+ if (SYSCTL_USER_TO_KERN(dir) && first)
return err ? : -EINVAL;
*lenp -= left;
out:
@@ -889,7 +909,7 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
/**
* proc_doulongvec_minmax - read a vector of long integers with min/max values
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -902,16 +922,16 @@ static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
*
* Returns 0 on success.
*/
-int proc_doulongvec_minmax(const struct ctl_table *table, int write,
+int proc_doulongvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, 1l, 1l);
+ return do_proc_doulongvec_minmax(table, dir, buffer, lenp, ppos, 1l, 1l);
}
/**
* proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -925,19 +945,19 @@ int proc_doulongvec_minmax(const struct ctl_table *table, int write,
*
* Returns 0 on success.
*/
-int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int write,
+int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_doulongvec_minmax(table, write, buffer,
- lenp, ppos, HZ, 1000l);
+ return do_proc_doulongvec_minmax(table, dir, buffer,
+ lenp, ppos, HZ, 1000l);
}
static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp, int write,
+ int *valp, int dir,
const struct ctl_table *table)
{
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
if (*lvalp > INT_MAX / HZ)
return 1;
if (*negp)
@@ -960,10 +980,10 @@ static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
}
static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp, int write,
+ int *valp, int dir,
const struct ctl_table *table)
{
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
if (USER_HZ < HZ && *lvalp > (LONG_MAX / HZ) * USER_HZ)
return 1;
*valp = clock_t_to_jiffies(*negp ? -*lvalp : *lvalp);
@@ -983,10 +1003,10 @@ static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp
}
static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp, int write,
+ int *valp, int dir,
const struct ctl_table *table)
{
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
unsigned long jif = msecs_to_jiffies(*negp ? -*lvalp : *lvalp);
if (jif > INT_MAX)
@@ -1008,7 +1028,7 @@ static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
}
static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lvalp,
- int *valp, int write,
+ int *valp, int dir,
const struct ctl_table *table)
{
int tmp, ret, *min, *max;
@@ -1016,13 +1036,13 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv
* If writing, first do so via a temporary local int so we can
* bounds-check it before touching *valp.
*/
- int *ip = write ? &tmp : valp;
+ int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : valp;
- ret = do_proc_dointvec_ms_jiffies_conv(negp, lvalp, ip, write, table);
+ ret = do_proc_dointvec_ms_jiffies_conv(negp, lvalp, ip, dir, table);
if (ret)
return ret;
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
min = (int *) table->extra1;
max = (int *) table->extra2;
if ((min && *min > tmp) || (max && *max < tmp))
@@ -1035,7 +1055,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv
/**
* proc_dointvec_jiffies - read a vector of integers as seconds
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -1047,24 +1067,24 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv
*
* Returns 0 on success.
*/
-int proc_dointvec_jiffies(const struct ctl_table *table, int write,
+int proc_dointvec_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table,write,buffer,lenp,ppos,
- do_proc_dointvec_jiffies_conv);
+ return do_proc_dointvec(table, dir, buffer, lenp, ppos,
+ do_proc_dointvec_jiffies_conv);
}
-int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write,
+int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table, write, buffer, lenp, ppos,
+ return do_proc_dointvec(table, dir, buffer, lenp, ppos,
do_proc_dointvec_ms_jiffies_minmax_conv);
}
/**
* proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: pointer to the file position
@@ -1076,17 +1096,17 @@ int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write,
*
* Returns 0 on success.
*/
-int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int write,
+int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table, write, buffer, lenp, ppos,
+ return do_proc_dointvec(table, dir, buffer, lenp, ppos,
do_proc_dointvec_userhz_jiffies_conv);
}
/**
* proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: the current position in the file
@@ -1098,17 +1118,17 @@ int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int write,
*
* Returns 0 on success.
*/
-int proc_dointvec_ms_jiffies(const struct ctl_table *table, int write, void *buffer,
+int proc_dointvec_ms_jiffies(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table, write, buffer, lenp, ppos,
+ return do_proc_dointvec(table, dir, buffer, lenp, ppos,
do_proc_dointvec_ms_jiffies_conv);
}
/**
* proc_do_large_bitmap - read/write from/to a large bitmap
* @table: the sysctl table
- * @write: %TRUE if this is a write to the sysctl file
+ * @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
@@ -1122,7 +1142,7 @@ int proc_dointvec_ms_jiffies(const struct ctl_table *table, int write, void *buf
*
* Returns 0 on success.
*/
-int proc_do_large_bitmap(const struct ctl_table *table, int write,
+int proc_do_large_bitmap(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
int err = 0;
@@ -1132,12 +1152,12 @@ int proc_do_large_bitmap(const struct ctl_table *table, int write,
unsigned long *tmp_bitmap = NULL;
char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c;
- if (!bitmap || !bitmap_len || !left || (*ppos && !write)) {
+ if (!bitmap || !bitmap_len || !left || (*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
char *p = buffer;
size_t skipped = 0;
@@ -1238,7 +1258,7 @@ int proc_do_large_bitmap(const struct ctl_table *table, int write,
}
if (!err) {
- if (write) {
+ if (SYSCTL_USER_TO_KERN(dir)) {
if (*ppos)
bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len);
else
@@ -1254,85 +1274,85 @@ int proc_do_large_bitmap(const struct ctl_table *table, int write,
#else /* CONFIG_PROC_SYSCTL */
-int proc_dostring(const struct ctl_table *table, int write,
+int proc_dostring(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dobool(const struct ctl_table *table, int write,
+int proc_dobool(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dointvec(const struct ctl_table *table, int write,
+int proc_dointvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_douintvec(const struct ctl_table *table, int write,
+int proc_douintvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dointvec_minmax(const struct ctl_table *table, int write,
+int proc_dointvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_douintvec_minmax(const struct ctl_table *table, int write,
+int proc_douintvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dou8vec_minmax(const struct ctl_table *table, int write,
+int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dointvec_jiffies(const struct ctl_table *table, int write,
+int proc_dointvec_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write,
+int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int write,
+int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_dointvec_ms_jiffies(const struct ctl_table *table, int write,
+int proc_dointvec_ms_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_doulongvec_minmax(const struct ctl_table *table, int write,
+int proc_doulongvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int write,
+int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
-int proc_do_large_bitmap(const struct ctl_table *table, int write,
+int proc_do_large_bitmap(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
@@ -1341,7 +1361,7 @@ int proc_do_large_bitmap(const struct ctl_table *table, int write,
#endif /* CONFIG_PROC_SYSCTL */
#if defined(CONFIG_SYSCTL)
-int proc_do_static_key(const struct ctl_table *table, int write,
+int proc_do_static_key(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct static_key *key = (struct static_key *)table->data;
@@ -1355,13 +1375,13 @@ int proc_do_static_key(const struct ctl_table *table, int write,
.extra2 = SYSCTL_ONE,
};
- if (write && !capable(CAP_SYS_ADMIN))
+ if (SYSCTL_USER_TO_KERN(dir) && !capable(CAP_SYS_ADMIN))
return -EPERM;
mutex_lock(&static_key_mutex);
val = static_key_enabled(key);
- ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
- if (write && !ret) {
+ ret = proc_dointvec_minmax(&tmp, dir, buffer, lenp, ppos);
+ if (SYSCTL_USER_TO_KERN(dir) && !ret) {
if (val)
static_key_enable(key);
else
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/8] sysctl: Discriminate between kernel and user converter params
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
` (3 preceding siblings ...)
2025-10-13 13:24 ` [PATCH 4/8] sysctl: Indicate the direction of operation with macro names Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
2025-10-13 13:24 ` [PATCH 6/8] sysctl: Create converter functions with two new macros Joel Granados
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
Rename converter parameter to indicate data flow direction: "lvalp" to
"u_ptr" indicating a user space parsed value pointer. "valp" to "k_ptr"
indicating a kernel storage value pointer. This facilitates the
identification of discrepancies between direction (copy to kernel or
copy to user space) and the modified variable. This is a preparation
commit for when the converter functions are exposed to the rest of the
kernel.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
kernel/sysctl.c | 118 ++++++++++++++++++++++++++++----------------------------
1 file changed, 59 insertions(+), 59 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 69148fe7359994b85c076b7b6750792b2d1c751e..2091d2396c83ac68d621b3d158ce1c490c392c4b 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -368,44 +368,44 @@ static void proc_put_char(void **buf, size_t *size, char c)
}
}
-static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
- int *valp, int dir,
+static int do_proc_dointvec_conv(bool *negp, unsigned long *u_ptr,
+ int *k_ptr, int dir,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
if (*negp) {
- if (*lvalp > (unsigned long) INT_MAX + 1)
+ if (*u_ptr > (unsigned long) INT_MAX + 1)
return -EINVAL;
- WRITE_ONCE(*valp, -*lvalp);
+ WRITE_ONCE(*k_ptr, -*u_ptr);
} else {
- if (*lvalp > (unsigned long) INT_MAX)
+ if (*u_ptr > (unsigned long) INT_MAX)
return -EINVAL;
- WRITE_ONCE(*valp, *lvalp);
+ WRITE_ONCE(*k_ptr, *u_ptr);
}
} else {
- int val = READ_ONCE(*valp);
+ int val = READ_ONCE(*k_ptr);
if (val < 0) {
*negp = true;
- *lvalp = -(unsigned long)val;
+ *u_ptr = -(unsigned long)val;
} else {
*negp = false;
- *lvalp = (unsigned long)val;
+ *u_ptr = (unsigned long)val;
}
}
return 0;
}
-static int do_proc_douintvec_conv(unsigned long *lvalp,
- unsigned int *valp, int dir,
+static int do_proc_douintvec_conv(unsigned long *u_ptr,
+ unsigned int *k_ptr, int dir,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- if (*lvalp > UINT_MAX)
+ if (*u_ptr > UINT_MAX)
return -EINVAL;
- WRITE_ONCE(*valp, *lvalp);
+ WRITE_ONCE(*k_ptr, *u_ptr);
} else {
- unsigned int val = READ_ONCE(*valp);
- *lvalp = (unsigned long)val;
+ unsigned int val = READ_ONCE(*k_ptr);
+ *u_ptr = (unsigned long)val;
}
return 0;
}
@@ -415,7 +415,7 @@ static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
static int do_proc_dointvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
- int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
+ int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *table))
{
int *i, vleft, first = 1, err = 0;
@@ -487,8 +487,8 @@ static int do_proc_dointvec(const struct ctl_table *table, int dir,
static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
- int (*conv)(unsigned long *lvalp,
- unsigned int *valp, int dir,
+ int (*conv)(unsigned long *u_ptr,
+ unsigned int *k_ptr, int dir,
const struct ctl_table *table))
{
unsigned long lval;
@@ -540,8 +540,8 @@ static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
- int (*conv)(unsigned long *lvalp,
- unsigned int *valp, int dir,
+ int (*conv)(unsigned long *u_ptr,
+ unsigned int *k_ptr, int dir,
const struct ctl_table *table))
{
unsigned long lval;
@@ -570,7 +570,7 @@ static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer,
int do_proc_douintvec(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
- int (*conv)(unsigned long *lvalp, unsigned int *valp,
+ int (*conv)(unsigned long *u_ptr, unsigned int *k_ptr,
int dir, const struct ctl_table *table))
{
unsigned int vleft;
@@ -679,18 +679,18 @@ int proc_douintvec(const struct ctl_table *table, int dir, void *buffer,
do_proc_douintvec_conv);
}
-static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
- int *valp, int dir,
+static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *u_ptr,
+ int *k_ptr, int dir,
const struct ctl_table *table)
{
int tmp, ret, *min, *max;
/*
- * If writing, first do so via a temporary local int so we can
- * bounds-check it before touching *valp.
+ * If writing to a kernel variable, first do so via a temporary
+ * local int so we can bounds-check it before touching *k_ptr.
*/
- int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : valp;
+ int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr;
- ret = do_proc_dointvec_conv(negp, lvalp, ip, dir, table);
+ ret = do_proc_dointvec_conv(negp, u_ptr, ip, dir, table);
if (ret)
return ret;
@@ -699,7 +699,7 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
max = (int *) table->extra2;
if ((min && *min > tmp) || (max && *max < tmp))
return -EINVAL;
- WRITE_ONCE(*valp, tmp);
+ WRITE_ONCE(*k_ptr, tmp);
}
return 0;
@@ -729,16 +729,16 @@ int proc_dointvec_minmax(const struct ctl_table *table, int dir,
do_proc_dointvec_minmax_conv);
}
-static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
- unsigned int *valp, int dir,
+static int do_proc_douintvec_minmax_conv(unsigned long *u_ptr,
+ unsigned int *k_ptr, int dir,
const struct ctl_table *table)
{
int ret;
unsigned int tmp, *min, *max;
/* When writing to the kernel use a temp local uint for bounds-checking */
- unsigned int *up = SYSCTL_USER_TO_KERN(dir) ? &tmp : valp;
+ unsigned int *up = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr;
- ret = do_proc_douintvec_conv(lvalp, up, dir, table);
+ ret = do_proc_douintvec_conv(u_ptr, up, dir, table);
if (ret)
return ret;
@@ -748,7 +748,7 @@ static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
if ((min && *min > tmp) || (max && *max < tmp))
return -ERANGE;
- WRITE_ONCE(*valp, tmp);
+ WRITE_ONCE(*k_ptr, tmp);
}
return 0;
@@ -953,19 +953,19 @@ int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
}
-static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp, int dir,
+static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *u_ptr,
+ int *k_ptr, int dir,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- if (*lvalp > INT_MAX / HZ)
+ if (*u_ptr > INT_MAX / HZ)
return 1;
if (*negp)
- WRITE_ONCE(*valp, -*lvalp * HZ);
+ WRITE_ONCE(*k_ptr, -*u_ptr * HZ);
else
- WRITE_ONCE(*valp, *lvalp * HZ);
+ WRITE_ONCE(*k_ptr, *u_ptr * HZ);
} else {
- int val = READ_ONCE(*valp);
+ int val = READ_ONCE(*k_ptr);
unsigned long lval;
if (val < 0) {
*negp = true;
@@ -974,21 +974,21 @@ static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
*negp = false;
lval = (unsigned long)val;
}
- *lvalp = lval / HZ;
+ *u_ptr = lval / HZ;
}
return 0;
}
-static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp, int dir,
+static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *u_ptr,
+ int *k_ptr, int dir,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- if (USER_HZ < HZ && *lvalp > (LONG_MAX / HZ) * USER_HZ)
+ if (USER_HZ < HZ && (LONG_MAX / HZ) * USER_HZ < *u_ptr)
return 1;
- *valp = clock_t_to_jiffies(*negp ? -*lvalp : *lvalp);
+ *k_ptr = clock_t_to_jiffies(*negp ? -*u_ptr : *u_ptr);
} else {
- int val = *valp;
+ int val = *k_ptr;
unsigned long lval;
if (val < 0) {
*negp = true;
@@ -997,23 +997,23 @@ static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp
*negp = false;
lval = (unsigned long)val;
}
- *lvalp = jiffies_to_clock_t(lval);
+ *u_ptr = jiffies_to_clock_t(lval);
}
return 0;
}
-static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
- int *valp, int dir,
+static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *u_ptr,
+ int *k_ptr, int dir,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- unsigned long jif = msecs_to_jiffies(*negp ? -*lvalp : *lvalp);
+ unsigned long jif = msecs_to_jiffies(*negp ? -*u_ptr : *u_ptr);
if (jif > INT_MAX)
return 1;
- WRITE_ONCE(*valp, (int)jif);
+ WRITE_ONCE(*k_ptr, (int)jif);
} else {
- int val = READ_ONCE(*valp);
+ int val = READ_ONCE(*k_ptr);
unsigned long lval;
if (val < 0) {
*negp = true;
@@ -1022,23 +1022,23 @@ static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
*negp = false;
lval = (unsigned long)val;
}
- *lvalp = jiffies_to_msecs(lval);
+ *u_ptr = jiffies_to_msecs(lval);
}
return 0;
}
-static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lvalp,
- int *valp, int dir,
+static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *u_ptr,
+ int *k_ptr, int dir,
const struct ctl_table *table)
{
int tmp, ret, *min, *max;
/*
- * If writing, first do so via a temporary local int so we can
- * bounds-check it before touching *valp.
+ * If writing to a kernel var, first do so via a temporary local
+ * int so we can bounds-check it before touching *k_ptr.
*/
- int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : valp;
+ int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr;
- ret = do_proc_dointvec_ms_jiffies_conv(negp, lvalp, ip, dir, table);
+ ret = do_proc_dointvec_ms_jiffies_conv(negp, u_ptr, ip, dir, table);
if (ret)
return ret;
@@ -1047,7 +1047,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv
max = (int *) table->extra2;
if ((min && *min > tmp) || (max && *max < tmp))
return -EINVAL;
- *valp = tmp;
+ *k_ptr = tmp;
}
return 0;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 6/8] sysctl: Create converter functions with two new macros
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
` (4 preceding siblings ...)
2025-10-13 13:24 ` [PATCH 5/8] sysctl: Discriminate between kernel and user converter params Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
2025-10-13 13:24 ` [PATCH 7/8] sysctl: Create integer converters with one macro Joel Granados
2025-10-13 13:24 ` [PATCH 8/8] sysctl: Add optional range checking to SYSCTL_INT_CONV_CUSTOM Joel Granados
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
Eight converter functions are created using two new macros
(SYSCTL_USER_TO_KERN_INT_CONV & SYSCTL_KERN_TO_USER_INT_CONV); they are
called from four pre-existing converter functions: do_proc_dointvec_conv
and do_proc_dointvec{,_userhz,_ms}_jiffies_conv. The function names
generated by the macros are differentiated by a string suffix passed as
the first macro argument.
The SYSCTL_USER_TO_KERN_INT_CONV macro first executes the u_ptr_op
operation, then checks for overflow, assigns sign (-, +) and finally
writes to the kernel var with WRITE_ONCE; it always returns an -EINVAL
when an overflow is detected. The SYSCTL_KERN_TO_USER_INT_CONV uses
READ_ONCE, casts to unsigned long, then executes the k_ptr_op before
assigning the value to the user space buffer.
The overflow check is always done against MAX_INT after applying
{k,u}_ptr_op. This approach avoids rounding or precision errors that
might occur when using the inverse operations.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
kernel/sysctl.c | 131 ++++++++++++++++++++++++++------------------------------
1 file changed, 61 insertions(+), 70 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 2091d2396c83ac68d621b3d158ce1c490c392c4b..f47bb8af33fb1d2d1a2a7c13d7ca093af82c6400 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -368,31 +368,65 @@ static void proc_put_char(void **buf, size_t *size, char c)
}
}
+#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; \
+}
+
+#define SYSCTL_CONV_IDENTITY(val) val
+#define SYSCTL_CONV_MULT_HZ(val) ((val) * HZ)
+#define SYSCTL_CONV_DIV_HZ(val) ((val) / HZ)
+
+static SYSCTL_USER_TO_KERN_INT_CONV(, SYSCTL_CONV_IDENTITY)
+static SYSCTL_KERN_TO_USER_INT_CONV(, SYSCTL_CONV_IDENTITY)
+
+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 int do_proc_dointvec_conv(bool *negp, unsigned long *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- if (*negp) {
- if (*u_ptr > (unsigned long) INT_MAX + 1)
- return -EINVAL;
- WRITE_ONCE(*k_ptr, -*u_ptr);
- } else {
- if (*u_ptr > (unsigned long) INT_MAX)
- return -EINVAL;
- WRITE_ONCE(*k_ptr, *u_ptr);
- }
- } else {
- int val = READ_ONCE(*k_ptr);
- if (val < 0) {
- *negp = true;
- *u_ptr = -(unsigned long)val;
- } else {
- *negp = false;
- *u_ptr = (unsigned long)val;
- }
+ return sysctl_user_to_kern_int_conv(negp, u_ptr, k_ptr);
}
- return 0;
+
+ return sysctl_kern_to_user_int_conv(negp, u_ptr, k_ptr);
}
static int do_proc_douintvec_conv(unsigned long *u_ptr,
@@ -952,31 +986,14 @@ int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
lenp, ppos, HZ, 1000l);
}
-
static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- if (*u_ptr > INT_MAX / HZ)
- return 1;
- if (*negp)
- WRITE_ONCE(*k_ptr, -*u_ptr * HZ);
- else
- WRITE_ONCE(*k_ptr, *u_ptr * HZ);
- } else {
- int val = READ_ONCE(*k_ptr);
- unsigned long lval;
- if (val < 0) {
- *negp = true;
- lval = -(unsigned long)val;
- } else {
- *negp = false;
- lval = (unsigned long)val;
- }
- *u_ptr = lval / HZ;
+ return sysctl_user_to_kern_int_conv_hz(negp, u_ptr, k_ptr);
}
- return 0;
+ return sysctl_kern_to_user_int_conv_hz(negp, u_ptr, k_ptr);
}
static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *u_ptr,
@@ -984,22 +1001,11 @@ static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *u_ptr
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- if (USER_HZ < HZ && (LONG_MAX / HZ) * USER_HZ < *u_ptr)
- return 1;
- *k_ptr = clock_t_to_jiffies(*negp ? -*u_ptr : *u_ptr);
- } else {
- int val = *k_ptr;
- unsigned long lval;
- if (val < 0) {
- *negp = true;
- lval = -(unsigned long)val;
- } else {
- *negp = false;
- lval = (unsigned long)val;
- }
- *u_ptr = jiffies_to_clock_t(lval);
+ if (USER_HZ < HZ)
+ return -EINVAL;
+ return sysctl_user_to_kern_int_conv_userhz(negp, u_ptr, k_ptr);
}
- return 0;
+ return sysctl_kern_to_user_int_conv_userhz(negp, u_ptr, k_ptr);
}
static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *u_ptr,
@@ -1007,24 +1013,9 @@ static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *u_ptr,
const struct ctl_table *table)
{
if (SYSCTL_USER_TO_KERN(dir)) {
- unsigned long jif = msecs_to_jiffies(*negp ? -*u_ptr : *u_ptr);
-
- if (jif > INT_MAX)
- return 1;
- WRITE_ONCE(*k_ptr, (int)jif);
- } else {
- int val = READ_ONCE(*k_ptr);
- unsigned long lval;
- if (val < 0) {
- *negp = true;
- lval = -(unsigned long)val;
- } else {
- *negp = false;
- lval = (unsigned long)val;
- }
- *u_ptr = jiffies_to_msecs(lval);
+ return sysctl_user_to_kern_int_conv_ms(negp, u_ptr, k_ptr);
}
- return 0;
+ return sysctl_kern_to_user_int_conv_ms(negp, u_ptr, k_ptr);
}
static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *u_ptr,
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7/8] sysctl: Create integer converters with one macro
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
` (5 preceding siblings ...)
2025-10-13 13:24 ` [PATCH 6/8] sysctl: Create converter functions with two new macros Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
2025-10-13 13:24 ` [PATCH 8/8] sysctl: Add optional range checking to SYSCTL_INT_CONV_CUSTOM Joel Granados
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
New SYSCTL_INT_CONV_CUSTOM macro creates "bi-directional" converters
from a user-to-kernel and a kernel-to-user functions. Replace integer
versions of do_proc_*_conv functions with the ones from the new macro.
Rename "_dointvec_" to just "_int_" as these converters are not applied
to vectors and the "do" is already in the name.
Move the USER_HZ validation directly into proc_dointvec_userhz_jiffies()
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
kernel/sysctl.c | 72 ++++++++++++++++++++-------------------------------------
1 file changed, 25 insertions(+), 47 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f47bb8af33fb1d2d1a2a7c13d7ca093af82c6400..e7dc4b79e93ea9ab929ce0465143aed74be444e5 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -418,17 +418,25 @@ 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 int do_proc_dointvec_conv(bool *negp, unsigned long *u_ptr,
- int *k_ptr, int dir,
- const struct ctl_table *table)
-{
- if (SYSCTL_USER_TO_KERN(dir)) {
- return sysctl_user_to_kern_int_conv(negp, u_ptr, k_ptr);
- }
-
- return sysctl_kern_to_user_int_conv(negp, u_ptr, k_ptr);
+#define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user) \
+int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\
+ int dir, const struct ctl_table *table) \
+{ \
+ if (SYSCTL_USER_TO_KERN(dir)) \
+ return user_to_kern(negp, u_ptr, k_ptr); \
+ return kern_to_user(negp, u_ptr, k_ptr); \
}
+static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv,
+ sysctl_kern_to_user_int_conv)
+static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_user_to_kern_int_conv_hz,
+ sysctl_kern_to_user_int_conv_hz)
+static SYSCTL_INT_CONV_CUSTOM(_userhz_jiffies,
+ sysctl_user_to_kern_int_conv_userhz,
+ sysctl_kern_to_user_int_conv_userhz)
+static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_user_to_kern_int_conv_ms,
+ sysctl_kern_to_user_int_conv_ms)
+
static int do_proc_douintvec_conv(unsigned long *u_ptr,
unsigned int *k_ptr, int dir,
const struct ctl_table *table)
@@ -467,7 +475,7 @@ static int do_proc_dointvec(const struct ctl_table *table, int dir,
left = *lenp;
if (!conv)
- conv = do_proc_dointvec_conv;
+ conv = do_proc_int_conv;
if (SYSCTL_USER_TO_KERN(dir)) {
if (proc_first_pos_non_zero_ignore(ppos, table))
@@ -724,7 +732,7 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *u_ptr,
*/
int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr;
- ret = do_proc_dointvec_conv(negp, u_ptr, ip, dir, table);
+ ret = do_proc_int_conv(negp, u_ptr, ip, dir, table);
if (ret)
return ret;
@@ -986,38 +994,6 @@ int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
lenp, ppos, HZ, 1000l);
}
-static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *u_ptr,
- int *k_ptr, int dir,
- const struct ctl_table *table)
-{
- if (SYSCTL_USER_TO_KERN(dir)) {
- return sysctl_user_to_kern_int_conv_hz(negp, u_ptr, k_ptr);
- }
- return sysctl_kern_to_user_int_conv_hz(negp, u_ptr, k_ptr);
-}
-
-static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *u_ptr,
- int *k_ptr, int dir,
- const struct ctl_table *table)
-{
- if (SYSCTL_USER_TO_KERN(dir)) {
- if (USER_HZ < HZ)
- return -EINVAL;
- return sysctl_user_to_kern_int_conv_userhz(negp, u_ptr, k_ptr);
- }
- return sysctl_kern_to_user_int_conv_userhz(negp, u_ptr, k_ptr);
-}
-
-static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *u_ptr,
- int *k_ptr, int dir,
- const struct ctl_table *table)
-{
- if (SYSCTL_USER_TO_KERN(dir)) {
- return sysctl_user_to_kern_int_conv_ms(negp, u_ptr, k_ptr);
- }
- return sysctl_kern_to_user_int_conv_ms(negp, u_ptr, k_ptr);
-}
-
static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *table)
@@ -1029,7 +1005,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *u_
*/
int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr;
- ret = do_proc_dointvec_ms_jiffies_conv(negp, u_ptr, ip, dir, table);
+ ret = do_proc_int_conv_ms_jiffies(negp, u_ptr, ip, dir, table);
if (ret)
return ret;
@@ -1062,7 +1038,7 @@ int proc_dointvec_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos,
- do_proc_dointvec_jiffies_conv);
+ do_proc_int_conv_jiffies);
}
int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
@@ -1090,8 +1066,10 @@ int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
+ if (USER_HZ < HZ)
+ return -EINVAL;
return do_proc_dointvec(table, dir, buffer, lenp, ppos,
- do_proc_dointvec_userhz_jiffies_conv);
+ do_proc_int_conv_userhz_jiffies);
}
/**
@@ -1113,7 +1091,7 @@ int proc_dointvec_ms_jiffies(const struct ctl_table *table, int dir, void *buffe
size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos,
- do_proc_dointvec_ms_jiffies_conv);
+ do_proc_int_conv_ms_jiffies);
}
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 8/8] sysctl: Add optional range checking to SYSCTL_INT_CONV_CUSTOM
2025-10-13 13:24 [PATCH 0/8] sysctl: Generalize proc handler converter creation Joel Granados
` (6 preceding siblings ...)
2025-10-13 13:24 ` [PATCH 7/8] sysctl: Create integer converters with one macro Joel Granados
@ 2025-10-13 13:24 ` Joel Granados
7 siblings, 0 replies; 9+ messages in thread
From: Joel Granados @ 2025-10-13 13:24 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner, Jan Kara, Kees Cook
Cc: linux-fsdevel, linux-kernel, Joel Granados
Extend the SYSCTL_INT_CONV_CUSTOM macro with a k_ptr_range_check
parameter to conditionally generate range validation code. When enabled,
validation is done against table->extra1 (min) and table->extra2 (max)
bounds before assignment. Add base minmax and ms_jiffies_minmax
converter instances that utilize the range checking functionality.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
kernel/sysctl.c | 106 +++++++++++++++++++++-----------------------------------
1 file changed, 40 insertions(+), 66 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index e7dc4b79e93ea9ab929ce0465143aed74be444e5..60f7618083516a24530f46f6eabccd108e90c74f 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -402,6 +402,34 @@ int sysctl_kern_to_user_int_conv##name(bool *negp, \
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_CONV_IDENTITY(val) val
#define SYSCTL_CONV_MULT_HZ(val) ((val) * HZ)
#define SYSCTL_CONV_DIV_HZ(val) ((val) / HZ)
@@ -418,24 +446,21 @@ 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)
-#define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user) \
-int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\
- int dir, const struct ctl_table *table) \
-{ \
- if (SYSCTL_USER_TO_KERN(dir)) \
- return user_to_kern(negp, u_ptr, k_ptr); \
- return kern_to_user(negp, u_ptr, k_ptr); \
-}
-
static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv,
- sysctl_kern_to_user_int_conv)
+ sysctl_kern_to_user_int_conv, false)
static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_user_to_kern_int_conv_hz,
- sysctl_kern_to_user_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)
+ 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)
+ sysctl_kern_to_user_int_conv_ms, false)
+
+static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv,
+ sysctl_kern_to_user_int_conv, true)
+static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies_minmax,
+ sysctl_user_to_kern_int_conv_ms,
+ sysctl_kern_to_user_int_conv_ms, true)
static int do_proc_douintvec_conv(unsigned long *u_ptr,
unsigned int *k_ptr, int dir,
@@ -721,32 +746,6 @@ int proc_douintvec(const struct ctl_table *table, int dir, void *buffer,
do_proc_douintvec_conv);
}
-static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *u_ptr,
- int *k_ptr, int dir,
- const struct ctl_table *table)
-{
- int tmp, ret, *min, *max;
- /*
- * If writing to a kernel variable, first do so via a temporary
- * local int so we can bounds-check it before touching *k_ptr.
- */
- int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr;
-
- ret = do_proc_int_conv(negp, u_ptr, ip, dir, table);
- if (ret)
- return ret;
-
- if (SYSCTL_USER_TO_KERN(dir)) {
- min = (int *) table->extra1;
- max = (int *) table->extra2;
- if ((min && *min > tmp) || (max && *max < tmp))
- return -EINVAL;
- WRITE_ONCE(*k_ptr, tmp);
- }
-
- return 0;
-}
-
/**
* proc_dointvec_minmax - read a vector of integers with min/max values
* @table: the sysctl table
@@ -768,7 +767,7 @@ int proc_dointvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos,
- do_proc_dointvec_minmax_conv);
+ do_proc_int_conv_minmax);
}
static int do_proc_douintvec_minmax_conv(unsigned long *u_ptr,
@@ -994,31 +993,6 @@ int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
lenp, ppos, HZ, 1000l);
}
-static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *u_ptr,
- int *k_ptr, int dir,
- const struct ctl_table *table)
-{
- int tmp, ret, *min, *max;
- /*
- * If writing to a kernel var, first do so via a temporary local
- * int so we can bounds-check it before touching *k_ptr.
- */
- int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr;
-
- ret = do_proc_int_conv_ms_jiffies(negp, u_ptr, ip, dir, table);
- if (ret)
- return ret;
-
- if (SYSCTL_USER_TO_KERN(dir)) {
- min = (int *) table->extra1;
- max = (int *) table->extra2;
- if ((min && *min > tmp) || (max && *max < tmp))
- return -EINVAL;
- *k_ptr = tmp;
- }
- return 0;
-}
-
/**
* proc_dointvec_jiffies - read a vector of integers as seconds
* @table: the sysctl table
@@ -1045,7 +1019,7 @@ int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos,
- do_proc_dointvec_ms_jiffies_minmax_conv);
+ do_proc_int_conv_ms_jiffies_minmax);
}
/**
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread