summaryrefslogtreecommitdiffstats
path: root/kernel/sysctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/sysctl.c')
-rw-r--r--kernel/sysctl.c153
1 files changed, 146 insertions, 7 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 6f3bb1f099fa..d12078fc215f 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2175,19 +2175,18 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
2175 return 0; 2175 return 0;
2176} 2176}
2177 2177
2178static int do_proc_douintvec_conv(bool *negp, unsigned long *lvalp, 2178static int do_proc_douintvec_conv(unsigned long *lvalp,
2179 int *valp, 2179 unsigned int *valp,
2180 int write, void *data) 2180 int write, void *data)
2181{ 2181{
2182 if (write) { 2182 if (write) {
2183 if (*negp) 2183 if (*lvalp > UINT_MAX)
2184 return -EINVAL; 2184 return -EINVAL;
2185 if (*lvalp > UINT_MAX) 2185 if (*lvalp > UINT_MAX)
2186 return -EINVAL; 2186 return -EINVAL;
2187 *valp = *lvalp; 2187 *valp = *lvalp;
2188 } else { 2188 } else {
2189 unsigned int val = *valp; 2189 unsigned int val = *valp;
2190 *negp = false;
2191 *lvalp = (unsigned long)val; 2190 *lvalp = (unsigned long)val;
2192 } 2191 }
2193 return 0; 2192 return 0;
@@ -2287,6 +2286,146 @@ static int do_proc_dointvec(struct ctl_table *table, int write,
2287 buffer, lenp, ppos, conv, data); 2286 buffer, lenp, ppos, conv, data);
2288} 2287}
2289 2288
2289static int do_proc_douintvec_w(unsigned int *tbl_data,
2290 struct ctl_table *table,
2291 void __user *buffer,
2292 size_t *lenp, loff_t *ppos,
2293 int (*conv)(unsigned long *lvalp,
2294 unsigned int *valp,
2295 int write, void *data),
2296 void *data)
2297{
2298 unsigned long lval;
2299 int err = 0;
2300 size_t left;
2301 bool neg;
2302 char *kbuf = NULL, *p;
2303
2304 left = *lenp;
2305
2306 if (proc_first_pos_non_zero_ignore(ppos, table))
2307 goto bail_early;
2308
2309 if (left > PAGE_SIZE - 1)
2310 left = PAGE_SIZE - 1;
2311
2312 p = kbuf = memdup_user_nul(buffer, left);
2313 if (IS_ERR(kbuf))
2314 return -EINVAL;
2315
2316 left -= proc_skip_spaces(&p);
2317 if (!left) {
2318 err = -EINVAL;
2319 goto out_free;
2320 }
2321
2322 err = proc_get_long(&p, &left, &lval, &neg,
2323 proc_wspace_sep,
2324 sizeof(proc_wspace_sep), NULL);
2325 if (err || neg) {
2326 err = -EINVAL;
2327 goto out_free;
2328 }
2329
2330 if (conv(&lval, tbl_data, 1, data)) {
2331 err = -EINVAL;
2332 goto out_free;
2333 }
2334
2335 if (!err && left)
2336 left -= proc_skip_spaces(&p);
2337
2338out_free:
2339 kfree(kbuf);
2340 if (err)
2341 return -EINVAL;
2342
2343 return 0;
2344
2345 /* This is in keeping with old __do_proc_dointvec() */
2346bail_early:
2347 *ppos += *lenp;
2348 return err;
2349}
2350
2351static int do_proc_douintvec_r(unsigned int *tbl_data, void __user *buffer,
2352 size_t *lenp, loff_t *ppos,
2353 int (*conv)(unsigned long *lvalp,
2354 unsigned int *valp,
2355 int write, void *data),
2356 void *data)
2357{
2358 unsigned long lval;
2359 int err = 0;
2360 size_t left;
2361
2362 left = *lenp;
2363
2364 if (conv(&lval, tbl_data, 0, data)) {
2365 err = -EINVAL;
2366 goto out;
2367 }
2368
2369 err = proc_put_long(&buffer, &left, lval, false);
2370 if (err || !left)
2371 goto out;
2372
2373 err = proc_put_char(&buffer, &left, '\n');
2374
2375out:
2376 *lenp -= left;
2377 *ppos += *lenp;
2378
2379 return err;
2380}
2381
2382static int __do_proc_douintvec(void *tbl_data, struct ctl_table *table,
2383 int write, void __user *buffer,
2384 size_t *lenp, loff_t *ppos,
2385 int (*conv)(unsigned long *lvalp,
2386 unsigned int *valp,
2387 int write, void *data),
2388 void *data)
2389{
2390 unsigned int *i, vleft;
2391
2392 if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
2393 *lenp = 0;
2394 return 0;
2395 }
2396
2397 i = (unsigned int *) tbl_data;
2398 vleft = table->maxlen / sizeof(*i);
2399
2400 /*
2401 * Arrays are not supported, keep this simple. *Do not* add
2402 * support for them.
2403 */
2404 if (vleft != 1) {
2405 *lenp = 0;
2406 return -EINVAL;
2407 }
2408
2409 if (!conv)
2410 conv = do_proc_douintvec_conv;
2411
2412 if (write)
2413 return do_proc_douintvec_w(i, table, buffer, lenp, ppos,
2414 conv, data);
2415 return do_proc_douintvec_r(i, buffer, lenp, ppos, conv, data);
2416}
2417
2418static int do_proc_douintvec(struct ctl_table *table, int write,
2419 void __user *buffer, size_t *lenp, loff_t *ppos,
2420 int (*conv)(unsigned long *lvalp,
2421 unsigned int *valp,
2422 int write, void *data),
2423 void *data)
2424{
2425 return __do_proc_douintvec(table->data, table, write,
2426 buffer, lenp, ppos, conv, data);
2427}
2428
2290/** 2429/**
2291 * proc_dointvec - read a vector of integers 2430 * proc_dointvec - read a vector of integers
2292 * @table: the sysctl table 2431 * @table: the sysctl table
@@ -2322,8 +2461,8 @@ int proc_dointvec(struct ctl_table *table, int write,
2322int proc_douintvec(struct ctl_table *table, int write, 2461int proc_douintvec(struct ctl_table *table, int write,
2323 void __user *buffer, size_t *lenp, loff_t *ppos) 2462 void __user *buffer, size_t *lenp, loff_t *ppos)
2324{ 2463{
2325 return do_proc_dointvec(table, write, buffer, lenp, ppos, 2464 return do_proc_douintvec(table, write, buffer, lenp, ppos,
2326 do_proc_douintvec_conv, NULL); 2465 do_proc_douintvec_conv, NULL);
2327} 2466}
2328 2467
2329/* 2468/*