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_INT_CONV_CUSTOM

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>

+40 -66
+40 -66
kernel/sysctl.c
··· 402 402 return 0; \ 403 403 } 404 404 405 + /** 406 + * To range check on a converted value, use a temp k_ptr 407 + * When checking range, value should be within (tbl->extra1, tbl->extra2) 408 + */ 409 + #define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ 410 + k_ptr_range_check) \ 411 + int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ 412 + int dir, const struct ctl_table *tbl) \ 413 + { \ 414 + if (SYSCTL_KERN_TO_USER(dir)) \ 415 + return kern_to_user(negp, u_ptr, k_ptr); \ 416 + \ 417 + if (k_ptr_range_check) { \ 418 + int tmp_k, ret; \ 419 + if (!tbl) \ 420 + return -EINVAL; \ 421 + ret = user_to_kern(negp, u_ptr, &tmp_k); \ 422 + if (ret) \ 423 + return ret; \ 424 + if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) || \ 425 + (tbl->extra2 && *(int *)tbl->extra2 < tmp_k)) \ 426 + return -EINVAL; \ 427 + WRITE_ONCE(*k_ptr, tmp_k); \ 428 + } else \ 429 + return user_to_kern(negp, u_ptr, k_ptr); \ 430 + return 0; \ 431 + } 432 + 405 433 #define SYSCTL_CONV_IDENTITY(val) val 406 434 #define SYSCTL_CONV_MULT_HZ(val) ((val) * HZ) 407 435 #define SYSCTL_CONV_DIV_HZ(val) ((val) / HZ) ··· 446 418 static SYSCTL_USER_TO_KERN_INT_CONV(_ms, msecs_to_jiffies) 447 419 static SYSCTL_KERN_TO_USER_INT_CONV(_ms, jiffies_to_msecs) 448 420 449 - #define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user) \ 450 - int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ 451 - int dir, const struct ctl_table *table) \ 452 - { \ 453 - if (SYSCTL_USER_TO_KERN(dir)) \ 454 - return user_to_kern(negp, u_ptr, k_ptr); \ 455 - return kern_to_user(negp, u_ptr, k_ptr); \ 456 - } 457 - 458 421 static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv, 459 - sysctl_kern_to_user_int_conv) 422 + sysctl_kern_to_user_int_conv, false) 460 423 static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_user_to_kern_int_conv_hz, 461 - sysctl_kern_to_user_int_conv_hz) 424 + sysctl_kern_to_user_int_conv_hz, false) 462 425 static SYSCTL_INT_CONV_CUSTOM(_userhz_jiffies, 463 426 sysctl_user_to_kern_int_conv_userhz, 464 - sysctl_kern_to_user_int_conv_userhz) 427 + sysctl_kern_to_user_int_conv_userhz, false) 465 428 static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_user_to_kern_int_conv_ms, 466 - sysctl_kern_to_user_int_conv_ms) 429 + sysctl_kern_to_user_int_conv_ms, false) 430 + 431 + static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv, 432 + sysctl_kern_to_user_int_conv, true) 433 + static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies_minmax, 434 + sysctl_user_to_kern_int_conv_ms, 435 + sysctl_kern_to_user_int_conv_ms, true) 467 436 468 437 static int do_proc_douintvec_conv(unsigned long *u_ptr, 469 438 unsigned int *k_ptr, int dir, ··· 746 721 do_proc_douintvec_conv); 747 722 } 748 723 749 - static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *u_ptr, 750 - int *k_ptr, int dir, 751 - const struct ctl_table *table) 752 - { 753 - int tmp, ret, *min, *max; 754 - /* 755 - * If writing to a kernel variable, first do so via a temporary 756 - * local int so we can bounds-check it before touching *k_ptr. 757 - */ 758 - int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr; 759 - 760 - ret = do_proc_int_conv(negp, u_ptr, ip, dir, table); 761 - if (ret) 762 - return ret; 763 - 764 - if (SYSCTL_USER_TO_KERN(dir)) { 765 - min = (int *) table->extra1; 766 - max = (int *) table->extra2; 767 - if ((min && *min > tmp) || (max && *max < tmp)) 768 - return -EINVAL; 769 - WRITE_ONCE(*k_ptr, tmp); 770 - } 771 - 772 - return 0; 773 - } 774 - 775 724 /** 776 725 * proc_dointvec_minmax - read a vector of integers with min/max values 777 726 * @table: the sysctl table ··· 767 768 void *buffer, size_t *lenp, loff_t *ppos) 768 769 { 769 770 return do_proc_dointvec(table, dir, buffer, lenp, ppos, 770 - do_proc_dointvec_minmax_conv); 771 + do_proc_int_conv_minmax); 771 772 } 772 773 773 774 static int do_proc_douintvec_minmax_conv(unsigned long *u_ptr, ··· 993 994 lenp, ppos, HZ, 1000l); 994 995 } 995 996 996 - static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *u_ptr, 997 - int *k_ptr, int dir, 998 - const struct ctl_table *table) 999 - { 1000 - int tmp, ret, *min, *max; 1001 - /* 1002 - * If writing to a kernel var, first do so via a temporary local 1003 - * int so we can bounds-check it before touching *k_ptr. 1004 - */ 1005 - int *ip = SYSCTL_USER_TO_KERN(dir) ? &tmp : k_ptr; 1006 - 1007 - ret = do_proc_int_conv_ms_jiffies(negp, u_ptr, ip, dir, table); 1008 - if (ret) 1009 - return ret; 1010 - 1011 - if (SYSCTL_USER_TO_KERN(dir)) { 1012 - min = (int *) table->extra1; 1013 - max = (int *) table->extra2; 1014 - if ((min && *min > tmp) || (max && *max < tmp)) 1015 - return -EINVAL; 1016 - *k_ptr = tmp; 1017 - } 1018 - return 0; 1019 - } 1020 - 1021 997 /** 1022 998 * proc_dointvec_jiffies - read a vector of integers as seconds 1023 999 * @table: the sysctl table ··· 1019 1045 void *buffer, size_t *lenp, loff_t *ppos) 1020 1046 { 1021 1047 return do_proc_dointvec(table, dir, buffer, lenp, ppos, 1022 - do_proc_dointvec_ms_jiffies_minmax_conv); 1048 + do_proc_int_conv_ms_jiffies_minmax); 1023 1049 } 1024 1050 1025 1051 /**