Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

sysctl: Add optional range checking to SYSCTL_UINT_CONV_CUSTOM

Add k_ptr_range_check parameter to SYSCTL_UINT_CONV_CUSTOM macro to
enable range validation using table->extra1/extra2. Replace
do_proc_douintvec_minmax_conv with do_proc_uint_conv_minmax generated
by the updated macro.

Signed-off-by: Joel Granados <joel.granados@kernel.org>

+32 -37
+32 -37
kernel/sysctl.c
··· 462 462 sysctl_user_to_kern_int_conv_ms, 463 463 sysctl_kern_to_user_int_conv_ms, true) 464 464 465 - #define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user) \ 466 - int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \ 467 - int dir, const struct ctl_table *tbl) \ 468 - { \ 469 - if (SYSCTL_USER_TO_KERN(dir)) \ 470 - return user_to_kern(u_ptr, k_ptr); \ 471 - return kern_to_user(u_ptr, k_ptr); \ 472 - } 473 - 474 465 static int sysctl_user_to_kern_uint_conv(const unsigned long *u_ptr, 475 466 unsigned int *k_ptr) 476 467 { ··· 479 488 return 0; 480 489 } 481 490 491 + #define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ 492 + k_ptr_range_check) \ 493 + int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \ 494 + int dir, const struct ctl_table *tbl) \ 495 + { \ 496 + if (SYSCTL_KERN_TO_USER(dir)) \ 497 + return kern_to_user(u_ptr, k_ptr); \ 498 + \ 499 + if (k_ptr_range_check) { \ 500 + unsigned int tmp_k; \ 501 + int ret; \ 502 + if (!tbl) \ 503 + return -EINVAL; \ 504 + ret = user_to_kern(u_ptr, &tmp_k); \ 505 + if (ret) \ 506 + return ret; \ 507 + if ((tbl->extra1 && \ 508 + *(unsigned int *)tbl->extra1 > tmp_k) || \ 509 + (tbl->extra2 && \ 510 + *(unsigned int *)tbl->extra2 < tmp_k)) \ 511 + return -ERANGE; \ 512 + WRITE_ONCE(*k_ptr, tmp_k); \ 513 + } else \ 514 + return user_to_kern(u_ptr, k_ptr); \ 515 + return 0; \ 516 + } 517 + 482 518 static SYSCTL_UINT_CONV_CUSTOM(, sysctl_user_to_kern_uint_conv, 483 - sysctl_kern_to_user_uint_conv) 519 + sysctl_kern_to_user_uint_conv, false) 520 + static SYSCTL_UINT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_uint_conv, 521 + sysctl_kern_to_user_uint_conv, true) 484 522 485 523 static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; 486 524 ··· 803 783 do_proc_int_conv_minmax); 804 784 } 805 785 806 - static int do_proc_douintvec_minmax_conv(unsigned long *u_ptr, 807 - unsigned int *k_ptr, int dir, 808 - const struct ctl_table *table) 809 - { 810 - int ret; 811 - unsigned int tmp, *min, *max; 812 - /* When writing to the kernel use a temp local uint for bounds-checking */ 813 - unsigned int *up = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr; 814 - 815 - ret = do_proc_uint_conv(u_ptr, up, dir, table); 816 - if (ret) 817 - return ret; 818 - 819 - if (SYSCTL_USER_TO_KERN(dir)) { 820 - min = (unsigned int *) table->extra1; 821 - max = (unsigned int *) table->extra2; 822 - if ((min && *min > tmp) || (max && *max < tmp)) 823 - return -ERANGE; 824 - 825 - WRITE_ONCE(*k_ptr, tmp); 826 - } 827 - 828 - return 0; 829 - } 830 - 831 786 /** 832 787 * proc_douintvec_minmax - read a vector of unsigned ints with min/max values 833 788 * @table: the sysctl table ··· 827 832 void *buffer, size_t *lenp, loff_t *ppos) 828 833 { 829 834 return do_proc_douintvec(table, dir, buffer, lenp, ppos, 830 - do_proc_douintvec_minmax_conv); 835 + do_proc_uint_conv_minmax); 831 836 } 832 837 833 838 /** ··· 871 876 872 877 val = READ_ONCE(*data); 873 878 res = do_proc_douintvec(&tmp, dir, buffer, lenp, ppos, 874 - do_proc_douintvec_minmax_conv); 879 + do_proc_uint_conv_minmax); 875 880 if (res) 876 881 return res; 877 882 if (SYSCTL_USER_TO_KERN(dir))