* [PATCH v5 1/5] sysctl: simplify the min/max boundary check
2025-02-09 15:58 [PATCH v5 0/5] sysctl: encode the min/max values directly in the table entry Wen Yang
@ 2025-02-09 15:58 ` Wen Yang
2025-02-09 15:58 ` [PATCH v5 2/5] sysctl: add helper functions to extract table->extra1/extra2 Wen Yang
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Wen Yang @ 2025-02-09 15:58 UTC (permalink / raw)
To: Joel Granados, Luis Chamberlain, Kees Cook
Cc: Eric W . Biederman, Dave Young, Christian Brauner,
Thomas Weißschuh, linux-kernel, Wen Yang, Joel Granados
Eric pointed out:
- That is just replace extra1 and extra2 with min and max members. The
- members don't have any reason to be pointers. Without being pointers
- the min/max functions can just use long values to cap either ints or
- longs, and there is no room for error. The integer promotion rules
- will ensure that even negative values can be stored in unsigned long
- min and max values successfully. Plus it is all bog standard C
- so there is nothing special to learn.
In order to archive it, we first introduce a new struct
do_proc_minmax_conv_param to replace both
do_proc_dointvec_minmax_conv_param and do_proc_douintvec_minmax_conv_param,
both int *{min, max} and unsigned int *{min, max} are changed to
unsigned long {min, max}, both do_proc_dointvec_conv() and
do_proc_douintvec_conv() deleted.
Selftest were also made:
[23:16:38] ================ sysctl_test (11 subtests) =================
[23:16:38] [PASSED] sysctl_test_api_dointvec_null_tbl_data
[23:16:38] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
[23:16:38] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
[23:16:38] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
[23:16:38] [PASSED] sysctl_test_dointvec_read_happy_single_positive
[23:16:38] [PASSED] sysctl_test_dointvec_read_happy_single_negative
[23:16:38] [PASSED] sysctl_test_dointvec_write_happy_single_positive
[23:16:38] [PASSED] sysctl_test_dointvec_write_happy_single_negative
[23:16:38] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
[23:16:38] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
[23:16:38] [PASSED] sysctl_test_register_sysctl_sz_invalid_extra_value
[23:16:38] =================== [PASSED] sysctl_test ===================
[23:16:38] ============================================================
[23:16:38] Testing complete. Ran 11 tests: passed: 11
Signed-off-by: Wen Yang <wen.yang@linux.dev>
Cc: Joel Granados <joel.granados@kernel.org>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Dave Young <dyoung@redhat.com>
Cc: linux-kernel@vger.kernel.org
---
kernel/sysctl.c | 199 ++++++++++++++++++------------------------------
1 file changed, 73 insertions(+), 126 deletions(-)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 21190098c21d..4507795e3568 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -409,22 +409,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 write, void *data)
+/**
+ * struct do_proc_minmax_conv_param - proc_dointvec_minmax() range checking structure
+ * @min: the minimum allowable value
+ * @max: the maximum allowable value
+ *
+ * The do_proc_minmax_conv_param structure provides the
+ * minimum and maximum values for doing range checking for those sysctl
+ * parameters that use the proc_dointvec_minmax(), proc_dou8vec_minmax() and so on.
+ */
+struct do_proc_minmax_conv_param {
+ unsigned long min;
+ unsigned long max;
+};
+
+static inline int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
+ int *valp,
+ int write, void *data)
{
+ struct do_proc_minmax_conv_param *param = data;
+ long min, max;
+ long val;
+
if (write) {
if (*negp) {
- if (*lvalp > (unsigned long) INT_MAX + 1)
- return -EINVAL;
- WRITE_ONCE(*valp, -*lvalp);
+ max = param ? param->max : 0;
+ min = param ? param->min : INT_MIN;
+ val = -*lvalp;
} else {
- if (*lvalp > (unsigned long) INT_MAX)
- return -EINVAL;
- WRITE_ONCE(*valp, *lvalp);
+ max = param ? param->max : INT_MAX;
+ min = param ? param->min : 0;
+ val = *lvalp;
}
+
+ if (val > max || val < min)
+ return -EINVAL;
+ WRITE_ONCE(*valp, (int)val);
} else {
- int val = READ_ONCE(*valp);
+ val = (int)READ_ONCE(*valp);
if (val < 0) {
*negp = true;
*lvalp = -(unsigned long)val;
@@ -433,21 +455,30 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
*lvalp = (unsigned long)val;
}
}
+
return 0;
}
-static int do_proc_douintvec_conv(unsigned long *lvalp,
- unsigned int *valp,
- int write, void *data)
+static inline int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
+ unsigned int *valp,
+ int write, void *data)
{
+ struct do_proc_minmax_conv_param *param = data;
+ unsigned long min, max;
+
if (write) {
- if (*lvalp > UINT_MAX)
+ max = param ? param->max : UINT_MAX;
+ min = param ? param->min : 0;
+
+ if (*lvalp > max || *lvalp < min)
return -EINVAL;
- WRITE_ONCE(*valp, *lvalp);
+
+ WRITE_ONCE(*valp, (unsigned int)*lvalp);
} else {
unsigned int val = READ_ONCE(*valp);
*lvalp = (unsigned long)val;
}
+
return 0;
}
@@ -474,7 +505,7 @@ static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table,
left = *lenp;
if (!conv)
- conv = do_proc_dointvec_conv;
+ conv = do_proc_dointvec_minmax_conv;
if (write) {
if (proc_first_pos_non_zero_ignore(ppos, table))
@@ -652,7 +683,7 @@ static int __do_proc_douintvec(void *tbl_data, const struct ctl_table *table,
}
if (!conv)
- conv = do_proc_douintvec_conv;
+ conv = do_proc_douintvec_minmax_conv;
if (write)
return do_proc_douintvec_w(i, table, buffer, lenp, ppos,
@@ -747,7 +778,7 @@ 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_minmax_conv, NULL);
}
/*
@@ -793,46 +824,6 @@ static int proc_taint(const struct ctl_table *table, int write,
return err;
}
-/**
- * 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 tmp, ret;
- struct do_proc_dointvec_minmax_conv_param *param = data;
- /*
- * 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);
- if (ret)
- return ret;
-
- if (write) {
- if ((param->min && *param->min > tmp) ||
- (param->max && *param->max < tmp))
- return -EINVAL;
- WRITE_ONCE(*valp, tmp);
- }
-
- return 0;
-}
-
/**
* proc_dointvec_minmax - read a vector of integers with min/max values
* @table: the sysctl table
@@ -852,53 +843,14 @@ 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,
- };
+ struct do_proc_minmax_conv_param param;
+
+ param.min = (table->extra1) ? *(int *) table->extra1 : INT_MIN;
+ param.max = (table->extra2) ? *(int *) table->extra2 : INT_MAX;
return do_proc_dointvec(table, write, buffer, lenp, ppos,
do_proc_dointvec_minmax_conv, ¶m);
}
-/**
- * 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)
-{
- int ret;
- unsigned int tmp;
- struct do_proc_douintvec_minmax_conv_param *param = data;
- /* write via temporary local uint for bounds-checking */
- unsigned int *up = write ? &tmp : valp;
-
- ret = do_proc_douintvec_conv(lvalp, up, write, data);
- if (ret)
- return ret;
-
- if (write) {
- if ((param->min && *param->min > tmp) ||
- (param->max && *param->max < tmp))
- return -ERANGE;
-
- WRITE_ONCE(*valp, tmp);
- }
-
- return 0;
-}
-
/**
* proc_douintvec_minmax - read a vector of unsigned ints with min/max values
* @table: the sysctl table
@@ -921,10 +873,11 @@ 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,
- };
+ struct do_proc_minmax_conv_param param;
+
+ param.min = (table->extra1) ? *(unsigned int *) table->extra1 : 0;
+ param.max = (table->extra2) ? *(unsigned int *) table->extra2 : UINT_MAX;
+
return do_proc_douintvec(table, write, buffer, lenp, ppos,
do_proc_douintvec_minmax_conv, ¶m);
}
@@ -950,23 +903,17 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct ctl_table tmp;
- unsigned int min = 0, max = 255U, val;
+ unsigned int val;
u8 *data = table->data;
- struct do_proc_douintvec_minmax_conv_param param = {
- .min = &min,
- .max = &max,
- };
+ struct do_proc_minmax_conv_param param;
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;
-
+ param.min = (table->extra1) ? *(unsigned int *) table->extra1 : 0;
+ param.max = (table->extra2) ? *(unsigned int *) table->extra2 : 255U;
tmp = *table;
tmp.maxlen = sizeof(val);
@@ -1007,7 +954,7 @@ static int __do_proc_doulongvec_minmax(void *data,
void *buffer, size_t *lenp, loff_t *ppos,
unsigned long convmul, unsigned long convdiv)
{
- unsigned long *i, *min, *max;
+ unsigned long *i, min, max;
int vleft, first = 1, err = 0;
size_t left;
char *p;
@@ -1018,8 +965,9 @@ static int __do_proc_doulongvec_minmax(void *data,
}
i = data;
- min = table->extra1;
- max = table->extra2;
+ min = (table->extra1) ? *(unsigned long *) table->extra1 : 0;
+ max = (table->extra2) ? *(unsigned long *) table->extra2 : ULONG_MAX;
+
vleft = table->maxlen / sizeof(unsigned long);
left = *lenp;
@@ -1051,7 +999,7 @@ static int __do_proc_doulongvec_minmax(void *data,
}
val = convmul * val / convdiv;
- if ((min && val < *min) || (max && val > *max)) {
+ if ((val < min) || (val > max)) {
err = -EINVAL;
break;
}
@@ -1209,7 +1157,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv
int *valp, int write, void *data)
{
int tmp, ret;
- struct do_proc_dointvec_minmax_conv_param *param = data;
+ struct do_proc_minmax_conv_param *param = data;
/*
* If writing, first do so via a temporary local int so we can
* bounds-check it before touching *valp.
@@ -1221,8 +1169,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv
return ret;
if (write) {
- if ((param->min && *param->min > tmp) ||
- (param->max && *param->max < tmp))
+ if ((param->min > tmp) || (param->max < tmp))
return -EINVAL;
*valp = tmp;
}
@@ -1254,10 +1201,10 @@ int proc_dointvec_jiffies(const struct ctl_table *table, int write,
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,
- };
+ struct do_proc_minmax_conv_param param;
+
+ param.min = (table->extra1) ? *(int *) table->extra1 : INT_MIN;
+ param.max = (table->extra2) ? *(int *) table->extra2 : INT_MAX;
return do_proc_dointvec(table, write, buffer, lenp, ppos,
do_proc_dointvec_ms_jiffies_minmax_conv, ¶m);
}
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v5 4/5] sysctl: add kunit test code to check the min/max encoding of sysctl table entries
2025-02-09 15:58 [PATCH v5 0/5] sysctl: encode the min/max values directly in the table entry Wen Yang
` (2 preceding siblings ...)
2025-02-09 15:58 ` [PATCH v5 3/5] sysctl: support encoding values directly in the table entry Wen Yang
@ 2025-02-09 15:58 ` Wen Yang
2025-02-09 15:58 ` [PATCH v5 5/5] sysctl: delete six_hundred_forty_kb to save 4 bytes Wen Yang
4 siblings, 0 replies; 6+ messages in thread
From: Wen Yang @ 2025-02-09 15:58 UTC (permalink / raw)
To: Joel Granados, Luis Chamberlain, Kees Cook
Cc: Eric W . Biederman, Dave Young, Christian Brauner,
Thomas Weißschuh, linux-kernel, Wen Yang
Add kunit test code to check the impact of encoding the min/max values
directly in table entries on functions such as proc_rointvec,
proc_rou8vectminmax, proc_rouintvectminmax, and proc_roulongvectminmax,
including basic parsing testing and min/max overflow testing.
Signed-off-by: Wen Yang <wen.yang@linux.dev>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Joel Granados <j.granados@samsung.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Dave Young <dyoung@redhat.com>
Cc: linux-kernel@vger.kernel.org
---
kernel/sysctl-test.c | 581 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 581 insertions(+)
diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index 3ac98bb7fb82..47064ab8b7f3 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -415,6 +415,575 @@ static void sysctl_test_register_sysctl_sz_invalid_extra_value(
KUNIT_EXPECT_NOT_NULL(test, register_sysctl("foo", table_qux));
}
+static void sysctl_test_api_dointvec_write_with_minmax(
+ struct kunit *test)
+{
+ int data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_dointvec_minmax,
+ .min = -1,
+ .max = 100,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "10";
+ char input2[] = "-5";
+ char input3[] = "200";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 10, *((int *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 10, *((int *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 10, *((int *)table.data));
+}
+
+static void sysctl_test_api_dointvec_write_with_min(
+ struct kunit *test)
+{
+ int data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644 | SYSCTL_FLAG_MIN,
+ .proc_handler = proc_dointvec_minmax,
+ .min = -1,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "10";
+ char input2[] = "-5";
+ char input3[] = "2147483647";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 10, *((int *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 10, *((int *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len);
+ KUNIT_EXPECT_EQ(test, 2147483647, *((int *)table.data));
+}
+
+static void sysctl_test_api_dointvec_write_with_max(
+ struct kunit *test)
+{
+ int data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644 | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_dointvec_minmax,
+ .max = 100,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "10";
+ char input2[] = "2147483647";
+ char input3[] = "-2147483648";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 10, *((int *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 10, *((int *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len);
+ KUNIT_EXPECT_EQ(test, -2147483648, *((int *)table.data));
+}
+
+static void sysctl_test_api_douintvec_write_with_minmax(
+ struct kunit *test)
+{
+ unsigned int data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_douintvec_minmax,
+ .min = 4,
+ .max = 200,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "100";
+ char input2[] = "3";
+ char input3[] = "1000";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data));
+}
+
+static void sysctl_test_api_douintvec_write_with_min(
+ struct kunit *test)
+{
+ unsigned int data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644 | SYSCTL_FLAG_MIN,
+ .proc_handler = proc_douintvec_minmax,
+ .min = 4,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "100";
+ char input2[] = "3";
+ char input3[] = "4294967295";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len);
+ KUNIT_EXPECT_EQ(test, 4294967295, *((unsigned int *)table.data));
+}
+
+static void sysctl_test_api_douintvec_write_with_max(
+ struct kunit *test)
+{
+ unsigned int data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644 | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_douintvec_minmax,
+ .max = 1000,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "900";
+ char input2[] = "10000";
+ char input3[] = "0";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 900, *((unsigned int *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 900, *((unsigned int *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len);
+ KUNIT_EXPECT_EQ(test, 0, *((unsigned int *)table.data));
+}
+
+static void sysctl_test_api_dou8vec_write_with_minmax(
+ struct kunit *test)
+{
+ unsigned char data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(unsigned char),
+ .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_dou8vec_minmax,
+ .min = 3,
+ .max = 100,
+ };
+ size_t max_len = 8, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "32";
+ char input2[] = "1";
+ char input3[] = "200";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data));
+}
+
+static void sysctl_test_api_dou8vec_write_with_min(
+ struct kunit *test)
+{
+ unsigned char data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(unsigned char),
+ .mode = 0644 | SYSCTL_FLAG_MIN,
+ .proc_handler = proc_dou8vec_minmax,
+ .min = 3,
+ };
+ size_t max_len = 8, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "32";
+ char input2[] = "1";
+ char input3[] = "255";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len);
+ KUNIT_EXPECT_EQ(test, 255, *((unsigned char *)table.data));
+}
+
+static void sysctl_test_api_dou8vec_write_with_max(
+ struct kunit *test)
+{
+ unsigned char data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(unsigned char),
+ .mode = 0644 | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_dou8vec_minmax,
+ .max = 200,
+ };
+ size_t max_len = 8, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "32";
+ char input2[] = "0";
+ char input3[] = "255";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input2) - 1, len);
+ KUNIT_EXPECT_EQ(test, 0, *((unsigned char *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 0, *((unsigned char *)table.data));
+}
+
+static void sysctl_test_api_doulongvec_write_with_minmax(
+ struct kunit *test)
+{
+ unsigned long data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(unsigned long),
+ .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_doulongvec_minmax,
+ .min = 1000,
+ .max = 3000,
+ };
+ size_t max_len = 64, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "1024";
+ char input2[] = "100";
+ char input3[] = "4096";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data));
+}
+
+static void sysctl_test_api_doulongvec_write_with_min(
+ struct kunit *test)
+{
+ unsigned long data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(unsigned long),
+ .mode = 0644 | SYSCTL_FLAG_MIN,
+ .proc_handler = proc_doulongvec_minmax,
+ .min = 1000,
+ };
+ size_t max_len = 64, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "1000";
+ char input2[] = "10";
+ char input3[64] = {0};
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 1000, *((unsigned long *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 1000, *((unsigned long *)table.data));
+
+ // test for input3
+ snprintf(input3, sizeof(input3), "%lu", ULONG_MAX);
+ len = strlen(input3);
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, strlen(input3), len);
+ KUNIT_EXPECT_EQ(test, ULONG_MAX, *((unsigned long *)table.data));
+}
+
+static void sysctl_test_api_doulongvec_write_with_max(
+ struct kunit *test)
+{
+ unsigned long data = 0;
+ const struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(unsigned long),
+ .mode = 0644 | SYSCTL_FLAG_MAX,
+ .proc_handler = proc_doulongvec_minmax,
+ .max = 3000,
+ };
+ size_t max_len = 64, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ char input1[] = "1024";
+ char input2[] = "4096";
+ char input3[] = "0";
+
+ // test for input1
+ len = sizeof(input1) - 1;
+ memcpy(buffer, input1, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len);
+ KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data));
+
+ // test for input2
+ len = sizeof(input2) - 1;
+ pos = 0;
+ memcpy(buffer, input2, len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, 0, pos);
+ KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data));
+
+ // test for input3
+ len = sizeof(input3) - 1;
+ pos = 0;
+ memcpy(buffer, input3, len);
+ KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len);
+ KUNIT_EXPECT_EQ(test, 0, *((unsigned long *)table.data));
+}
+
static struct kunit_case sysctl_test_cases[] = {
KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
@@ -427,6 +996,18 @@ static struct kunit_case sysctl_test_cases[] = {
KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
KUNIT_CASE(sysctl_test_register_sysctl_sz_invalid_extra_value),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_with_minmax),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_with_min),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_with_max),
+ KUNIT_CASE(sysctl_test_api_douintvec_write_with_minmax),
+ KUNIT_CASE(sysctl_test_api_douintvec_write_with_min),
+ KUNIT_CASE(sysctl_test_api_douintvec_write_with_max),
+ KUNIT_CASE(sysctl_test_api_dou8vec_write_with_minmax),
+ KUNIT_CASE(sysctl_test_api_dou8vec_write_with_min),
+ KUNIT_CASE(sysctl_test_api_dou8vec_write_with_max),
+ KUNIT_CASE(sysctl_test_api_doulongvec_write_with_minmax),
+ KUNIT_CASE(sysctl_test_api_doulongvec_write_with_min),
+ KUNIT_CASE(sysctl_test_api_doulongvec_write_with_max),
{}
};
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread