[PATCH 07/11] moduleparam: Route DEFINE_KERNEL_PARAM_OPS get pointer via _Generic
Kees Cook
kees at kernel.org
Thu May 21 06:33:20 PDT 2026
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 <kees at kernel.org>
---
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
More information about the linux-um
mailing list