Replaces do_proc_dointvec with do_proc_dotypevec macro. For now, it only generates the integer function, but it will bring together the logic for int, uint and ulong proc vector functions. It is parametrized on the kernel pointer type being processed and generates a "do_proc_do##T##vec" (where T is the type) function. The parametrization is needed for advancing the for loop with "++" and affects the type of the k_ptr argument in the converter call back function. Initialize the neg variable to false to ensure that unsigned types do not inadvertently assign negative values to user space. Return an -EINVAL when user space tries to assign a negative value to an unsigned variable. Add a comment to clarify that the macro is **not** designed to be exported outside of sysctl.c. Signed-off-by: Joel Granados --- kernel/sysctl.c | 144 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 67 deletions(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e53e60213462fd1b99b8c36e19e1e5c47d20e6c5..da91fc037c66d70ed6923f1e6de045580359d04f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -572,75 +572,85 @@ static int do_proc_int_conv_minmax(bool *negp, unsigned long *u_ptr, int *k_ptr, 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 *u_ptr, int *k_ptr, - 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 && SYSCTL_KERN_TO_USER(dir))) { - *lenp = 0; - return 0; - } - - i = (int *) table->data; - vleft = table->maxlen / sizeof(*i); - left = *lenp; - - if (SYSCTL_USER_TO_KERN(dir)) { - if (proc_first_pos_non_zero_ignore(ppos, table)) - goto out; - - if (left > PAGE_SIZE - 1) - left = PAGE_SIZE - 1; - p = buffer; - } - - for (; left && vleft--; i++, first=0) { - unsigned long lval; - bool neg; - - if (SYSCTL_USER_TO_KERN(dir)) { - proc_skip_spaces(&p, &left); - - if (!left) - break; - err = proc_get_long(&p, &left, &lval, &neg, - proc_wspace_sep, - sizeof(proc_wspace_sep), NULL); - if (err) - break; - if (conv(&neg, &lval, i, 1, table)) { - err = -EINVAL; - break; - } - } else { - if (conv(&neg, &lval, i, 0, table)) { - err = -EINVAL; - break; - } - if (!first) - proc_put_char(&buffer, &left, '\t'); - proc_put_long(&buffer, &left, lval, neg); - } - } - - if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err) - proc_put_char(&buffer, &left, '\n'); - if (SYSCTL_USER_TO_KERN(dir) && !err && left) - proc_skip_spaces(&p, &left); - if (SYSCTL_USER_TO_KERN(dir) && first) - return err ? : -EINVAL; - *lenp -= left; -out: - *ppos += *lenp; - return err; +/* + * Do not export this macro outside the sysctl subsys. + * It is meant to generate static functions only + */ +#define do_proc_dotypevec(T) \ +static int do_proc_do##T##vec(const struct ctl_table *table, int dir, \ + void *buffer, size_t *lenp, loff_t *ppos, \ + int (*conv)(bool *negp, ulong *u_ptr, T *k_ptr, \ + int dir, const struct ctl_table *table)) \ +{ \ + T *i; \ + int vleft, first = 1, err = 0; \ + size_t left; \ + char *p; \ +\ + if (!table->data || !table->maxlen || !*lenp || \ + (*ppos && SYSCTL_KERN_TO_USER(dir))) { \ + *lenp = 0; \ + return 0; \ + } \ +\ + i = (typeof(i)) table->data; \ + vleft = table->maxlen / sizeof(*i); \ + left = *lenp; \ +\ + if (SYSCTL_USER_TO_KERN(dir)) { \ + if (proc_first_pos_non_zero_ignore(ppos, table)) \ + goto out; \ +\ + if (left > PAGE_SIZE - 1) \ + left = PAGE_SIZE - 1; \ + p = buffer; \ + } \ +\ + for (; left && vleft--; i++, first = 0) { \ + unsigned long lval; \ + bool neg = false; \ +\ + if (SYSCTL_USER_TO_KERN(dir)) { \ + proc_skip_spaces(&p, &left); \ +\ + if (!left) \ + break; \ + err = proc_get_long(&p, &left, &lval, &neg, \ + proc_wspace_sep, \ + sizeof(proc_wspace_sep), NULL); \ + if (!err && neg && is_unsigned_type(T)) \ + err = -EINVAL; \ + if (err) \ + break; \ + if (conv(&neg, &lval, i, dir, table)) { \ + err = -EINVAL; \ + break; \ + } \ + } else { \ + if (conv(&neg, &lval, i, dir, table)) { \ + err = -EINVAL; \ + break; \ + } \ + if (!first) \ + proc_put_char(&buffer, &left, '\t'); \ + proc_put_long(&buffer, &left, lval, neg); \ + } \ + } \ +\ + if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err) \ + proc_put_char(&buffer, &left, '\n'); \ + if (SYSCTL_USER_TO_KERN(dir) && !err && left) \ + proc_skip_spaces(&p, &left); \ + if (SYSCTL_USER_TO_KERN(dir) && first) \ + return err ? : -EINVAL; \ + *lenp -= left; \ +out: \ + *ppos += *lenp; \ + return err; \ } +do_proc_dotypevec(int) + static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer, size_t *lenp, loff_t *ppos, int (*conv)(unsigned long *u_ptr, -- 2.50.1