@@ -354,29 +354,117 @@ static void proc_put_char(void **buf, size_t *size, char c)
354354 }
355355}
356356
357- static SYSCTL_USER_TO_KERN_INT_CONV (, SYSCTL_CONV_IDENTITY )
358- static SYSCTL_KERN_TO_USER_INT_CONV (, SYSCTL_CONV_IDENTITY )
359-
360- static SYSCTL_INT_CONV_CUSTOM (, sysctl_user_to_kern_int_conv ,
361- sysctl_kern_to_user_int_conv , false )
362- static SYSCTL_INT_CONV_CUSTOM (_minmax , sysctl_user_to_kern_int_conv ,
363- sysctl_kern_to_user_int_conv , true )
357+ /**
358+ * proc_uint_u2k_conv_uop - Assign user value to a kernel pointer
359+ *
360+ * @u_ptr: pointer to user space variable
361+ * @k_ptr: pointer to kernel variable
362+ * @u_ptr_op: execute this function before assigning to k_ptr
363+ *
364+ * Uses WRITE_ONCE to assign value to k_ptr. Executes u_ptr_op if
365+ * not NULL. Check that the values are less than UINT_MAX to avoid
366+ * having to support wrap around from userspace.
367+ *
368+ * returns 0 on success.
369+ */
370+ int proc_uint_u2k_conv_uop (const ulong * u_ptr , uint * k_ptr ,
371+ ulong (* u_ptr_op )(const ulong ))
372+ {
373+ ulong u = u_ptr_op ? u_ptr_op (* u_ptr ) : * u_ptr ;
364374
375+ if (u > UINT_MAX )
376+ return - EINVAL ;
377+ WRITE_ONCE (* k_ptr , u );
378+ return 0 ;
379+ }
365380
366- static SYSCTL_USER_TO_KERN_UINT_CONV (, SYSCTL_CONV_IDENTITY )
381+ /**
382+ * proc_uint_k2u_conv - Assign kernel value to a user space pointer
383+ *
384+ * @u_ptr: pointer to user space variable
385+ * @k_ptr: pointer to kernel variable
386+ *
387+ * Uses READ_ONCE to assign value to u_ptr.
388+ *
389+ * returns 0 on success.
390+ */
391+ int proc_uint_k2u_conv (ulong * u_ptr , const uint * k_ptr )
392+ {
393+ uint val = READ_ONCE (* k_ptr );
394+ * u_ptr = (ulong )val ;
395+ return 0 ;
396+ }
367397
368- int sysctl_kern_to_user_uint_conv (unsigned long * u_ptr ,
369- const unsigned int * k_ptr )
398+ /**
399+ * proc_uint_conv - Change user or kernel pointer based on direction
400+ *
401+ * @u_ptr: pointer to user variable
402+ * @k_ptr: pointer to kernel variable
403+ * @dir: %TRUE if this is a write to the sysctl file
404+ * @tbl: the sysctl table
405+ * @k_ptr_range_check: Check range for k_ptr when %TRUE
406+ * @user_to_kern: Callback used to assign value from user to kernel var
407+ * @kern_to_user: Callback used to assign value from kernel to user var
408+ *
409+ * When direction is kernel to user, then the u_ptr is modified.
410+ * When direction is user to kernel, then the k_ptr is modified.
411+ *
412+ * Returns 0 on success
413+ */
414+ int proc_uint_conv (ulong * u_ptr , uint * k_ptr , int dir ,
415+ const struct ctl_table * tbl , bool k_ptr_range_check ,
416+ int (* user_to_kern )(const ulong * u_ptr , uint * k_ptr ),
417+ int (* kern_to_user )(ulong * u_ptr , const uint * k_ptr ))
370418{
371- unsigned int val = READ_ONCE (* k_ptr );
372- * u_ptr = (unsigned long )val ;
419+ if (SYSCTL_KERN_TO_USER (dir ))
420+ return kern_to_user (u_ptr , k_ptr );
421+
422+ if (k_ptr_range_check ) {
423+ uint tmp_k ;
424+ int ret ;
425+
426+ if (!tbl )
427+ return - EINVAL ;
428+ ret = user_to_kern (u_ptr , & tmp_k );
429+ if (ret )
430+ return ret ;
431+ if ((tbl -> extra1 &&
432+ * (uint * )tbl -> extra1 > tmp_k ) ||
433+ (tbl -> extra2 &&
434+ * (uint * )tbl -> extra2 < tmp_k ))
435+ return - ERANGE ;
436+ WRITE_ONCE (* k_ptr , tmp_k );
437+ } else
438+ return user_to_kern (u_ptr , k_ptr );
373439 return 0 ;
374440}
375441
376- static SYSCTL_UINT_CONV_CUSTOM (, sysctl_user_to_kern_uint_conv ,
377- sysctl_kern_to_user_uint_conv , false )
378- static SYSCTL_UINT_CONV_CUSTOM (_minmax , sysctl_user_to_kern_uint_conv ,
379- sysctl_kern_to_user_uint_conv , true )
442+ static int proc_uint_u2k_conv (const ulong * u_ptr , uint * k_ptr )
443+ {
444+ return proc_uint_u2k_conv_uop (u_ptr , k_ptr , NULL );
445+ }
446+
447+ static int do_proc_uint_conv (ulong * u_ptr , uint * k_ptr , int dir ,
448+ const struct ctl_table * tbl )
449+ {
450+ return proc_uint_conv (u_ptr , k_ptr , dir , tbl , false,
451+ proc_uint_u2k_conv , proc_uint_k2u_conv );
452+ }
453+
454+ static int do_proc_uint_conv_minmax (ulong * u_ptr , uint * k_ptr , int dir ,
455+ const struct ctl_table * tbl )
456+ {
457+ return proc_uint_conv (u_ptr , k_ptr , dir , tbl , true,
458+ proc_uint_u2k_conv , proc_uint_k2u_conv );
459+ }
460+
461+ static SYSCTL_USER_TO_KERN_INT_CONV (, SYSCTL_CONV_IDENTITY )
462+ static SYSCTL_KERN_TO_USER_INT_CONV (, SYSCTL_CONV_IDENTITY )
463+
464+ static SYSCTL_INT_CONV_CUSTOM (, sysctl_user_to_kern_int_conv ,
465+ sysctl_kern_to_user_int_conv , false )
466+ static SYSCTL_INT_CONV_CUSTOM (_minmax , sysctl_user_to_kern_int_conv ,
467+ sysctl_kern_to_user_int_conv , true )
380468
381469static const char proc_wspace_sep [] = { ' ' , '\t' , '\n' };
382470
@@ -576,7 +664,6 @@ int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer,
576664 return do_proc_douintvec (table , dir , buffer , lenp , ppos , conv );
577665}
578666
579-
580667/**
581668 * proc_dobool - read/write a bool
582669 * @table: the sysctl table
@@ -1079,6 +1166,25 @@ int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer,
10791166 return - ENOSYS ;
10801167}
10811168
1169+ int proc_uint_k2u_conv (ulong * u_ptr , const uint * k_ptr )
1170+ {
1171+ return - ENOSYS ;
1172+ }
1173+
1174+ int proc_uint_u2k_conv_uop (const ulong * u_ptr , uint * k_ptr ,
1175+ ulong (* u_ptr_op )(const ulong ))
1176+ {
1177+ return - ENOSYS ;
1178+ }
1179+
1180+ int proc_uint_conv (ulong * u_ptr , uint * k_ptr , int dir ,
1181+ const struct ctl_table * tbl , bool k_ptr_range_check ,
1182+ int (* user_to_kern )(const ulong * u_ptr , uint * k_ptr ),
1183+ int (* kern_to_user )(ulong * u_ptr , const uint * k_ptr ))
1184+ {
1185+ return - ENOSYS ;
1186+ }
1187+
10821188int proc_dou8vec_minmax (const struct ctl_table * table , int dir ,
10831189 void * buffer , size_t * lenp , loff_t * ppos )
10841190{
0 commit comments