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),                                        \
        }
 
-- 
2.34.1


Reply via email to