On 5/21/26 3:33 PM, Kees Cook wrote:
> Make the DEFINE_KERNEL_PARAM_OPS family route their _get argument to
> either .get (struct seq_buf *) or .get_str (char *) at compile time
> based on the pointer's actual function signature. Two helper macros
> do the routing:
> 
>   _KERNEL_PARAM_OPS_GET     - return the pointer if it has the seq_buf
>                               signature, otherwise NULL of that type
>   _KERNEL_PARAM_OPS_GET_STR - mirror image for the char * signature
> 
> Both use _Generic; only the two valid function-pointer types are
> listed, so any third-party type is a compile error rather than
> silently falling through.
> 
> Now a callback whose body has been migrated from char * to struct
> seq_buf * needs no change at its kernel_param_ops initialization site,
> because the macro picks up the new type automatically and assigns to
> the correct field.
> 
> Signed-off-by: Kees Cook <[email protected]>
> ---
>  include/linux/moduleparam.h | 33 ++++++++++++++++++++++++++-------
>  1 file changed, 26 insertions(+), 7 deletions(-)
> 
> diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
> index c52120f6ac28..795bc7c654ef 100644
> --- a/include/linux/moduleparam.h
> +++ b/include/linux/moduleparam.h
> @@ -85,15 +85,32 @@ struct kernel_param_ops {
>   *
>   *   static DEFINE_KERNEL_PARAM_OPS(my_ops, my_set, my_get);
>   *
> - * Routing the @_set and @_get function pointers through the macro
> - * (rather than naming the struct fields at every call site) lets the
> - * field layout change in one place when callbacks are migrated to a
> - * new signature.
> + * @_get may be either of:
> + *   int (*)(struct seq_buf *, const struct kernel_param *) (seq_buf)
> + *   int (*)(char *, const struct kernel_param *)           (legacy)
> + *
> + * The macro uses _Generic to route the function pointer to the
> + * matching field (.get or .get_str) at compile time, leaving the
> + * other field NULL. Each helper matches the wrong prototype signature
> + * and returns NULL, falling through to the default branch otherwise;
> + * if @_get has neither expected signature the assignment to the
> + * fields gets a normal compile-time type-mismatch error.
>   */
> +#define _KERNEL_PARAM_OPS_GET(_get)                                  \
> +     _Generic((_get),                                                \
> +         int (*)(char *, const struct kernel_param *): NULL,         \
> +         default: (_get))
> +
> +#define _KERNEL_PARAM_OPS_GET_STR(_get)                                      
> \
> +     _Generic((_get),                                                \
> +         int (*)(struct seq_buf *, const struct kernel_param *): NULL, \
> +         default: (_get))
> +
>  #define DEFINE_KERNEL_PARAM_OPS(_name, _set, _get)                   \
>       const struct kernel_param_ops _name = {                         \
>               .set = (_set),                                          \
> -             .get_str = (_get),                                      \
> +             .get = _KERNEL_PARAM_OPS_GET(_get),                     \
> +             .get_str = _KERNEL_PARAM_OPS_GET_STR(_get),             \
>       }
>  
>  /* As DEFINE_KERNEL_PARAM_OPS, with KERNEL_PARAM_OPS_FL_NOARG set. */
> @@ -101,14 +118,16 @@ struct kernel_param_ops {
>       const struct kernel_param_ops _name = {                         \
>               .flags = KERNEL_PARAM_OPS_FL_NOARG,                     \
>               .set = (_set),                                          \
> -             .get_str = (_get),                                      \
> +             .get = _KERNEL_PARAM_OPS_GET(_get),                     \
> +             .get_str = _KERNEL_PARAM_OPS_GET_STR(_get),             \
>       }
>  
>  /* As DEFINE_KERNEL_PARAM_OPS, with an additional .free callback. */
>  #define DEFINE_KERNEL_PARAM_OPS_FREE(_name, _set, _get, _free)               
> \
>       const struct kernel_param_ops _name = {                         \
>               .set = (_set),                                          \
> -             .get_str = (_get),                                      \
> +             .get = _KERNEL_PARAM_OPS_GET(_get),                     \
> +             .get_str = _KERNEL_PARAM_OPS_GET_STR(_get),             \
>               .free = (_free),                                        \
>       }
>  

Reviewed-by: Petr Pavlu <[email protected]>

-- Petr

Reply via email to