* [PATCH v3 0/5] dyndbg: fix i386 alignment and KEEP module sections
From: Jim Cromie @ 2026-05-15 8:58 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie,
Louis Chauvet
This series starts with a fix to a section misalignement on i386, and
follows with cleanups and refactoring for reuse.
When CONFIG_DRM_USE_DYNAMIC_DEBUG=Y (currently marked BROKEN) and
CONFIG_DRM=y, the __dyndbg_classes section gets a single
struct/record. On i386 this is 28-bytes, breaking the required
ALIGN(8) on the following __dyndbg section.
This breaks dynamic_debug_init() with a NULL pointer dereference; the
section traversal logic encounters padding bytes instead of a valid
descriptor.
the series:
1. refactors BOUNDED_SECTION_* macros for later reuse
v2- adds note that macros are for vmlinux.lds.h only, not for modules.
2. drops unused HEADERED_SECTION_* macros
3. fixes ALIGN(8) omission causing NULL ptr on i386
adds,uses BOUNDED_SECTION _ALIGNED variants
4. removes now redundant ALIGN(8)s in vmlinux.lds.h
5. adds dyndbg.lds.S to KEEP sections in modules
follows codetag.lds.h model
v3:
move #includes to top of files,
drop redundant ALIGN(8) in dydnbg.lds.S: DYNDBG_SECTIONS macro
add Reviewed-by tag (thx Petr)
v2:
- avoids BOUNDED_SECTION in modules. Since ELF sections have well
defined bounaries, _start & _end symbols are useless, and empty
sections are unhelpful, possibly confusing to tools.
- sets 0 address to the sections, not just whatever current is.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
Changes in v3:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v2: https://lore.kernel.org/r/20260507-asm-generic-1-v2-0-47c52759d268@gmail.com
Changes in v2:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v1: https://lore.kernel.org/r/20260502-asm-generic-1-v1-0-1103ee0152df@gmail.com
---
Jim Cromie (5):
vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h
vmlinux.lds.h: drop unused HEADERED_SECTION* macros
vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386
vmlinux.lds.h: remove redundant ALIGN(8) directives
dyndbg.lds.S: fix lost dyndbg sections in modules
MAINTAINERS | 1 +
include/asm-generic/bounded_sections.lds.h | 32 ++++++++++++++++
include/asm-generic/dyndbg.lds.h | 18 +++++++++
include/asm-generic/vmlinux.lds.h | 59 ++++--------------------------
scripts/module.lds.S | 2 +
5 files changed, 61 insertions(+), 51 deletions(-)
---
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
change-id: 20260502-asm-generic-1-32c842a35dd0
Best regards,
--
Jim Cromie <jim.cromie@gmail.com>
^ permalink raw reply
* [PATCH v3 1/5] vmlinux.lds.h: refactor BOUNDED_SECTION_* macros into bounded_sections.lds.h
From: Jim Cromie @ 2026-05-15 8:58 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260515-asm-generic-1-v3-0-680b273666d4@gmail.com>
Move BOUNDED_SECTION_* macros to a new helper file:
include/asm-generic/bounded_sections.lds.h and include it back into
vmlinux.lds.h. This allows its reuse later to fix a failure to keep
dyndbg sections in some circumstances.
NOTES:
These macros are only for use in vmlinux.lds.h, where the _start &
_end symbols are needed. Modules keep sections separate in ELF
sections, with their boundaries known, so the _start and _end are not
useful, and may confuse tools not expecting them.
This patch ignores a checkpatch warning, because new file is covered
by "GENERIC INCLUDE/ASM HEADER FILES" in MAINTAINERS
CC: Arnd Bergmann <arnd@arndb.de>
CC: linux-arch@vger.kernel.org
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v3: move include to top
---
include/asm-generic/bounded_sections.lds.h | 36 ++++++++++++++++++++++++++++++
include/asm-generic/vmlinux.lds.h | 31 +------------------------
2 files changed, 37 insertions(+), 30 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
new file mode 100644
index 000000000000..8c29293ca7fb
--- /dev/null
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _ASM_GENERIC_BOUNDED_SECTIONS_H
+#define _ASM_GENERIC_BOUNDED_SECTIONS_H
+
+#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ _BEGIN_##_label_ = .; \
+ KEEP(*(_sec_)) \
+ _END_##_label_ = .;
+
+#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ _label_##_BEGIN_ = .; \
+ KEEP(*(_sec_)) \
+ _label_##_END_ = .;
+
+#define BOUNDED_SECTION_BY(_sec_, _label_) \
+ BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
+
+#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
+
+#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
+ _HDR_##_label_ = .; \
+ KEEP(*(.gnu.linkonce.##_sec_)) \
+ BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
+
+#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
+ _label_##_HDR_ = .; \
+ KEEP(*(.gnu.linkonce.##_sec_)) \
+ BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
+
+#define HEADERED_SECTION_BY(_sec_, _label_) \
+ HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
+
+#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
+
+#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 60c8c22fd3e4..9c61dd083f26 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -50,6 +50,7 @@
* [__nosave_begin, __nosave_end] for the nosave data
*/
+#include <asm-generic/bounded_sections.lds.h>
#include <asm-generic/codetag.lds.h>
#ifndef LOAD_OFFSET
@@ -211,36 +212,6 @@
# endif
#endif
-#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
- _BEGIN_##_label_ = .; \
- KEEP(*(_sec_)) \
- _END_##_label_ = .;
-
-#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
- _label_##_BEGIN_ = .; \
- KEEP(*(_sec_)) \
- _label_##_END_ = .;
-
-#define BOUNDED_SECTION_BY(_sec_, _label_) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
-
-#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _HDR_##_label_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _label_##_HDR_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_BY(_sec_, _label_) \
- HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
-
#ifdef CONFIG_TRACE_BRANCH_PROFILING
#define LIKELY_PROFILE() \
BOUNDED_SECTION_BY(_ftrace_annotated_branch, _annotated_branch_profile)
--
2.54.0
^ permalink raw reply related
* [PATCH v3 2/5] vmlinux.lds.h: drop unused HEADERED_SECTION* macros
From: Jim Cromie @ 2026-05-15 8:58 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260515-asm-generic-1-v3-0-680b273666d4@gmail.com>
These macros are unused, no point in carrying them any more.
NB: these macros were just moved to bounded_sections.lds.h, from
vmlinux.lds.h, which is the known entity, and therefore more
meaningful in the 1-line summary, so thats what I used as the topic.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/bounded_sections.lds.h | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
index 8c29293ca7fb..268cdc34389b 100644
--- a/include/asm-generic/bounded_sections.lds.h
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -18,19 +18,4 @@
#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
-#define HEADERED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _HDR_##_label_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_, _HDR_) \
- _label_##_HDR_ = .; \
- KEEP(*(.gnu.linkonce.##_sec_)) \
- BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_)
-
-#define HEADERED_SECTION_BY(_sec_, _label_) \
- HEADERED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-
-#define HEADERED_SECTION(_sec) HEADERED_SECTION_BY(_sec, _sec)
-
#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
--
2.54.0
^ permalink raw reply related
* [PATCH v3 3/5] vmlinux.lds.h: Fix ALIGN(8) omission causing NULL ptr on i386
From: Jim Cromie @ 2026-05-15 8:58 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie,
Louis Chauvet
In-Reply-To: <20260515-asm-generic-1-v3-0-680b273666d4@gmail.com>
Almost all uses of the BOUNDED_SECTION macros are ALIGN(8), either
explicitly, or by being below an aligned section containing x*8 byte
objects. The noteworthy exception is BOUNDED_SECTION(__dyndbg), which
immediately follows BOUNDED_SECTION(__dyndbg_classes).
On i386, struct _ddebug_classmap is 28 bytes, so without an explicit
ALIGN(8) in the macro, the following __dyndbg section gets misaligned,
causing a NULL ptr deref in dynamic_debug_init().
So fix this with an explicit ALIGN(8) in the existing BOUNDED_SECTION
macros, and introduce _ALIGNED variants to handle the cases with an
explicit . = ALIGN(x)
Explicit alignments: EXCEPTION_TABLE, ORC_UNWIND_TABLE
update BOUNDED_SECTION uses inside . = ALIGN(x) to use _ALIGNED
variants, but keep the outer ALIGNs so the symbols between them are
not "re-aligned".
In particular, scripts/sorttable.c does not tolerate sloppy padding.
At the top of ORC_UNWIND_TABLE, add . = ALIGN(4) to match the struct
orc_header __align() call in the code:
commit b9f174c811e3 ("x86/unwind/orc: Add ELF section with ORC version identifier")
Suggested-by: Louis Chauvet <louis.chauvet@bootlin.com> # _ALIGNED variants.
Link: https://lore.kernel.org/lkml/177402491426.6181.12855763650074831089.b4-review@b4/
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/bounded_sections.lds.h | 17 ++++++++++++++---
include/asm-generic/vmlinux.lds.h | 9 +++++----
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/include/asm-generic/bounded_sections.lds.h b/include/asm-generic/bounded_sections.lds.h
index 268cdc34389b..8ff3e3420f60 100644
--- a/include/asm-generic/bounded_sections.lds.h
+++ b/include/asm-generic/bounded_sections.lds.h
@@ -3,19 +3,30 @@
#ifndef _ASM_GENERIC_BOUNDED_SECTIONS_H
#define _ASM_GENERIC_BOUNDED_SECTIONS_H
-#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+#define BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, _ALIGNED_) \
+ . = ALIGN(_ALIGNED_); \
_BEGIN_##_label_ = .; \
KEEP(*(_sec_)) \
_END_##_label_ = .;
-#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+#define BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, 8)
+
+#define BOUNDED_SECTION_POST_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, _ALIGNED_) \
+ . = ALIGN(_ALIGNED_); \
_label_##_BEGIN_ = .; \
KEEP(*(_sec_)) \
_label_##_END_ = .;
+#define BOUNDED_SECTION_POST_LABEL(_sec_, _label_, _BEGIN_, _END_) \
+ BOUNDED_SECTION_POST_LABEL_ALIGNED(_sec_, _label_, _BEGIN_, _END_, 8)
+
#define BOUNDED_SECTION_BY(_sec_, _label_) \
BOUNDED_SECTION_PRE_LABEL(_sec_, _label_, __start, __stop)
-#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
+#define BOUNDED_SECTION_BY_ALIGNED(_sec_, _label_, _ALIGNED_) \
+ BOUNDED_SECTION_PRE_LABEL_ALIGNED(_sec_, _label_, __start, __stop, _ALIGNED_)
+
+#define BOUNDED_SECTION(_sec) BOUNDED_SECTION_BY(_sec, _sec)
#endif /* _ASM_GENERIC_BOUNDED_SECTIONS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 9c61dd083f26..6892cbec0554 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -640,7 +640,7 @@
#define EXCEPTION_TABLE(align) \
. = ALIGN(align); \
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(__ex_table, ___ex_table) \
+ BOUNDED_SECTION_BY_ALIGNED(__ex_table, ___ex_table, align) \
}
/*
@@ -832,16 +832,17 @@
#ifdef CONFIG_UNWINDER_ORC
#define ORC_UNWIND_TABLE \
+ . = ALIGN(4); \
.orc_header : AT(ADDR(.orc_header) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_header, _orc_header) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_header, _orc_header, 4) \
} \
. = ALIGN(4); \
.orc_unwind_ip : AT(ADDR(.orc_unwind_ip) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_unwind_ip, _orc_unwind_ip) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_unwind_ip, _orc_unwind_ip, 4)\
} \
. = ALIGN(2); \
.orc_unwind : AT(ADDR(.orc_unwind) - LOAD_OFFSET) { \
- BOUNDED_SECTION_BY(.orc_unwind, _orc_unwind) \
+ BOUNDED_SECTION_BY_ALIGNED(.orc_unwind, _orc_unwind, 2) \
} \
text_size = _etext - _stext; \
. = ALIGN(4); \
--
2.54.0
^ permalink raw reply related
* [PATCH v3 4/5] vmlinux.lds.h: remove redundant ALIGN(8) directives
From: Jim Cromie @ 2026-05-15 8:58 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260515-asm-generic-1-v3-0-680b273666d4@gmail.com>
The BOUNDED_SECTION_PRE_LABEL and BOUNDED_SECTION_POST_LABEL macros
were recently updated to inherently enforce an 8-byte alignment. This
makes the explicit '. = ALIGN(8);' statements preceding 'naked' macro
calls in vmlinux.lds.h redundant.
Remove these redundant alignment directives to clean up the file and
clarify that the macros handle their own alignment padding.
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
include/asm-generic/vmlinux.lds.h | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 6892cbec0554..9fa11baf27d3 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -228,7 +228,6 @@
#ifdef CONFIG_KPROBES
#define KPROBE_BLACKLIST() \
- . = ALIGN(8); \
BOUNDED_SECTION(_kprobe_blacklist)
#else
#define KPROBE_BLACKLIST()
@@ -244,7 +243,6 @@
#ifdef CONFIG_EVENT_TRACING
#define FTRACE_EVENTS() \
- . = ALIGN(8); \
BOUNDED_SECTION(_ftrace_events) \
BOUNDED_SECTION_BY(_ftrace_eval_map, _ftrace_eval_maps)
#else
@@ -261,7 +259,6 @@
#ifdef CONFIG_FTRACE_SYSCALLS
#define TRACE_SYSCALLS() \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__syscalls_metadata, _syscalls_metadata)
#else
#define TRACE_SYSCALLS()
@@ -276,7 +273,6 @@
#ifdef CONFIG_SERIAL_EARLYCON
#define EARLYCON_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__earlycon_table, __earlycon_table, , _end)
#else
#define EARLYCON_TABLE()
@@ -284,11 +280,9 @@
#ifdef CONFIG_SECURITY
#define LSM_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_PRE_LABEL(.lsm_info.init, _lsm_info, __start, __end)
#define EARLY_LSM_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_PRE_LABEL(.early_lsm_info.init, _early_lsm_info, __start, __end)
#else
#define LSM_TABLE()
@@ -314,7 +308,6 @@
#ifdef CONFIG_ACPI
#define ACPI_PROBE_TABLE(name) \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__##name##_acpi_probe_table, \
__##name##_acpi_probe_table,, _end)
#else
@@ -323,7 +316,6 @@
#ifdef CONFIG_THERMAL
#define THERMAL_TABLE(name) \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(__##name##_thermal_table, \
__##name##_thermal_table,, _end)
#else
@@ -403,12 +395,10 @@
__end_init_stack = .;
#define JUMP_TABLE_DATA \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__jump_table, ___jump_table)
#ifdef CONFIG_HAVE_STATIC_CALL_INLINE
#define STATIC_CALL_DATA \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(.static_call_sites, _static_call_sites) \
BOUNDED_SECTION_BY(.static_call_tramp_key, _static_call_tramp_key)
#else
@@ -453,7 +443,6 @@
*(.rodata) *(.rodata.*) *(.data.rel.ro*) \
SCHED_DATA \
RO_AFTER_INIT_DATA /* Read only after init */ \
- . = ALIGN(8); \
BOUNDED_SECTION_BY(__tracepoints_ptrs, ___tracepoints_ptrs) \
*(__tracepoints_strings)/* Tracepoints: strings */ \
} \
@@ -946,12 +935,10 @@
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_test_suites, __kunit_suites, _start, _end)
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_INIT_TABLE() \
- . = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_init_test_suites, \
__kunit_init_suites, _start, _end)
--
2.54.0
^ permalink raw reply related
* [PATCH v3 5/5] dyndbg.lds.S: fix lost dyndbg sections in modules
From: Jim Cromie @ 2026-05-15 8:58 UTC (permalink / raw)
To: Arnd Bergmann, Jason Baron, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-arch, linux-kernel, linux-modules, Jim Cromie
In-Reply-To: <20260515-asm-generic-1-v3-0-680b273666d4@gmail.com>
With CONFIG_DRM_USE_DYNAMIC_DEBUG=y, several build configs had
problems with __dyndbg* sections getting lost in drm drivers. Fix
this by following the model demonstrated in codetag.lds.h.
Introduce include/asm-generic/dyndbg.lds.h, to bundle dynamic-debug's
multiple sections together, into 2 macros:
vmlinux.lds.h DATA_DATA: move the 2 BOUNDED_SECTION_BY(__dyndbg*)
calls into dyndbg.lds.h DYNDBG_SECTIONS(). vmlinux.lds.h now includes
the new file and calls the new macro.
MOD_DYNDBG_SECTIONS declares the 2 BOUNDED_SECTION_BY calls, but wraps
them with output section syntax to keep them as known and separate ELF
sections in the module.ko. The KEEP fixes the lost section.
dyndbg.lds.h includes (reuses) bounded-section.lds.h
scripts/module.lds.S: now calls MOD_DYNDBG_SECTIONS right before the
CODETAG macro (consistent with their placements in vmlinux.lds.h), and
also includes dyndbg.lds.h
This isolates vmlinux.lds.h from further __dyndbg section additions.
CC: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
---
v3: move #includes to top, drop extra ALIGN(8) in DYNDBG_SECTIONS, add RvBy
v2: Address linker script review feedback for relocatable modules.
MOD_DYNDBG_SECTIONS() used the BOUNDED_SECTION_BY() macro, which
proved problematic for kernel modules for two reasons:
1. Unwanted Empty Sections:
BOUNDED_SECTION_BY() automatically generates `__start` and `__stop`
symbols. When applied to `MOD_DYNDBG_SECTIONS()`, the linker assumes
the sections are populated due to the symbol definitions, forcing an
empty `__dyndbg` and `__dyndbg_classes` output section in every
compiled module, even those without dynamic debug configuration.
Since the module loader uses `section_objs()` to locate data via
ELF headers instead of relying on `__start`/`__stop` symbols, these
assignments are completely unnecessary.
2. Non-zero Output Addresses:
During relocatable linking (e.g., `ld.bfd -r`), omitting an explicit
base address causes the section to inherit the current location
counter. This results in non-zero sh_addr values in `.ko` files,
which is confusing, degrades compressibility, and can cause issues
with external tools parsing the ELF.
Fix both issues by dropping `BOUNDED_SECTION_BY()` in favor of a simple
`KEEP(*(...))` constraint and explicitly defining the sections with a `0`
base address: `__dyndbg 0 : ALIGN(8) { ... }`.
fixup-inc-vml
---
MAINTAINERS | 1 +
include/asm-generic/dyndbg.lds.h | 18 ++++++++++++++++++
include/asm-generic/vmlinux.lds.h | 6 ++----
scripts/module.lds.S | 2 ++
4 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index b2040011a386..2ef1e481af26 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9073,6 +9073,7 @@ DYNAMIC DEBUG
M: Jason Baron <jbaron@akamai.com>
M: Jim Cromie <jim.cromie@gmail.com>
S: Maintained
+F: include/asm-generic/dyndbg.lds.h
F: include/linux/dynamic_debug.h
F: lib/dynamic_debug.c
F: lib/test_dynamic_debug.c
diff --git a/include/asm-generic/dyndbg.lds.h b/include/asm-generic/dyndbg.lds.h
new file mode 100644
index 000000000000..9d8951bef688
--- /dev/null
+++ b/include/asm-generic/dyndbg.lds.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __ASM_GENERIC_DYNDBG_LDS_H
+#define __ASM_GENERIC_DYNDBG_LDS_H
+
+#include <asm-generic/bounded_sections.lds.h>
+#define DYNDBG_SECTIONS() \
+ BOUNDED_SECTION_BY(__dyndbg, ___dyndbg) \
+ BOUNDED_SECTION_BY(__dyndbg_classes, ___dyndbg_classes)
+
+#define MOD_DYNDBG_SECTIONS() \
+ __dyndbg 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg)) \
+ } \
+ __dyndbg_classes 0 : ALIGN(8) { \
+ KEEP(*(__dyndbg_classes)) \
+ }
+
+#endif /* __ASM_GENERIC_DYNDBG_LDS_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 9fa11baf27d3..016ca653232e 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -52,6 +52,7 @@
#include <asm-generic/bounded_sections.lds.h>
#include <asm-generic/codetag.lds.h>
+#include <asm-generic/dyndbg.lds.h>
#ifndef LOAD_OFFSET
#define LOAD_OFFSET 0
@@ -344,10 +345,7 @@
*(.data..do_once) \
STRUCT_ALIGN(); \
*(__tracepoints) \
- /* implement dynamic printk debug */ \
- . = ALIGN(8); \
- BOUNDED_SECTION_BY(__dyndbg_classes, ___dyndbg_classes) \
- BOUNDED_SECTION_BY(__dyndbg, ___dyndbg) \
+ DYNDBG_SECTIONS() \
CODETAG_SECTIONS() \
LIKELY_PROFILE() \
BRANCH_PROFILE() \
diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index b62683061d79..2e62dc5bd5d4 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -10,6 +10,7 @@
#endif
#include <asm-generic/codetag.lds.h>
+#include <asm-generic/dyndbg.lds.h>
SECTIONS {
/DISCARD/ : {
@@ -61,6 +62,7 @@ SECTIONS {
*(.rodata..L*)
}
+ MOD_DYNDBG_SECTIONS()
MOD_SEPARATE_CODETAG_SECTIONS()
}
--
2.54.0
^ permalink raw reply related
* [PATCH 0/2] module: restrict module auto-loading to privileged users
From: Michal Gorlas @ 2026-05-15 17:20 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-doc, linux-kernel, linux-modules, Michal Gorlas
Add option to restrict the module auto-loading to CAP_SYS_ADMIN.
This is heavily inspired by CONFIG_GRKERNSEC_MODHARDEN of the latest
available Grsecurity patches [1]. Instead of checking whether the
callers' UID is 0, check whether the calling process has CAP_SYS_ADMIN.
The reasoning here is that many modules are autoloaded by systemd
services which are running as privileged users, but do not have UID 0.
While systemd-udevd runs as root, systemd-network (which often
auto-loads a module) for example runs as system user (UID range 6 to
999).
When enabled, reduces attack surface where unprivileged users can trigger
vulnerable module to be auto-loaded, to then exploit it. Recent LPEs
(CopyFail [3], DirtyFrag [4]) for example, would have been mitigated
with this option enabled as long as the vulnerable modules are not built-in
(or already loaded at the point of running the exploit).
[1] - https://github.com/minipli/linux-unofficial_grsec/blob/linux-4.9.x-unofficial_grsec/kernel/kmod.c#L153
[2] - https://systemd.io/UIDS-GIDS/
[3] - https://github.com/theori-io/copy-fail-CVE-2026-31431
[4] - https://github.com/V4bel/dirtyfrag
Signed-off-by: Michal Gorlas <michal.gorlas@9elements.com>
---
Michal Gorlas (2):
module: add CONFIG_MODULE_RESTRICT_AUTOLOAD
module: restrict autoload to CAP_SYS_ADMIN if CONFIG_MODULE_RESTRICT_AUTOLOAD
Documentation/admin-guide/kernel-parameters.txt | 5 +++++
kernel/module/Kconfig | 15 +++++++++++++++
kernel/module/internal.h | 1 +
kernel/module/kmod.c | 5 +++++
kernel/module/main.c | 11 +++++++++++
5 files changed, 37 insertions(+)
---
base-commit: 663385f9155f27892a97a5824006f806a32eb8dc
change-id: 20260515-autoload_restrict-cfa6727c4d72
Best regards,
--
Michal Gorlas <michal.gorlas@9elements.com>
^ permalink raw reply
* [PATCH 1/2] module: add CONFIG_MODULE_RESTRICT_AUTOLOAD
From: Michal Gorlas @ 2026-05-15 17:20 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-doc, linux-kernel, linux-modules, Michal Gorlas
In-Reply-To: <20260515-autoload_restrict-v1-0-40b7c03ddd04@9elements.com>
Add CONFIG_MODULE_RESTRICT_AUTOLOAD and modrestrict parameter
documentation.
Signed-off-by: Michal Gorlas <michal.gorlas@9elements.com>
---
Documentation/admin-guide/kernel-parameters.txt | 5 +++++
kernel/module/Kconfig | 15 +++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 03a550630644..1013104f0943 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -4185,6 +4185,11 @@ Kernel parameters
For details see:
Documentation/admin-guide/hw-vuln/processor_mmio_stale_data.rst
+ modrestrict=<bool>
+ Control the restriction of module auto-loading to
+ CAP_SYS_ADMIN. If no <bool> value is specified, this
+ is set to the value of CONFIG_MODULE_RESTRICT_AUTOLOAD.
+
<module>.async_probe[=<bool>] [KNL]
If no <bool> value is specified or if the value
specified is not a valid <bool>, enable asynchronous
diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
index 43b1bb01fd27..c9e01bb848c0 100644
--- a/kernel/module/Kconfig
+++ b/kernel/module/Kconfig
@@ -337,6 +337,21 @@ config MODULE_SIG_HASH
endif # MODULE_SIG || IMA_APPRAISE_MODSIG
+config MODULE_RESTRICT_AUTOLOAD
+ bool "Restrict module auto-loading to privileged users"
+ default n
+ help
+ Restrict module auto-loading in response to use of some feature
+ implemented by an unloaded module to CAP_SYS_ADMIN. Enabling this
+ option helps reducing the attack surface where unprivileged users
+ can abuse auto-loading to cause a vulnerable module to load that is
+ then exploited.
+
+ Note that this option also prevents a benign use of auto-loading for
+ a non-root users. Thus if enabled, the root user should execute
+ modprobe manually if needed, or add the module to the list of modules
+ loaded at the boot by modifying init scripts.
+
config MODULE_COMPRESS
bool "Module compression"
help
--
2.54.0
^ permalink raw reply related
* [PATCH 2/2] module: restrict autoload to CAP_SYS_ADMIN if CONFIG_MODULE_RESTRICT_AUTOLOAD
From: Michal Gorlas @ 2026-05-15 17:20 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-doc, linux-kernel, linux-modules, Michal Gorlas
In-Reply-To: <20260515-autoload_restrict-v1-0-40b7c03ddd04@9elements.com>
Restrict module auto-loading to CAP_SYS_ADMIN if
CONFIG_MODULE_RESTRICT_AUTOLOAD is enabled, cmdline parameter
modrestrict=true, or kernel.modrestrict=1 is set with sysctl.
Signed-off-by: Michal Gorlas <michal.gorlas@9elements.com>
---
kernel/module/internal.h | 1 +
kernel/module/kmod.c | 5 +++++
kernel/module/main.c | 11 +++++++++++
3 files changed, 17 insertions(+)
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index 061161cc79d9..496d8703f0c6 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -46,6 +46,7 @@ struct kernel_symbol {
extern struct mutex module_mutex;
extern struct list_head modules;
+extern bool module_autoload_restrict;
extern const struct module_attribute *const modinfo_attrs[];
extern const size_t modinfo_attrs_count;
diff --git a/kernel/module/kmod.c b/kernel/module/kmod.c
index a25dccdf7aa7..58b28c23f571 100644
--- a/kernel/module/kmod.c
+++ b/kernel/module/kmod.c
@@ -156,6 +156,11 @@ int __request_module(bool wait, const char *fmt, ...)
if (ret)
return ret;
+ if (module_autoload_restrict && !capable(CAP_SYS_ADMIN)) {
+ pr_alert("denied attempt to auto-load module %s\n", module_name);
+ return -EPERM;
+ }
+
ret = down_timeout(&kmod_concurrent_max, MAX_KMOD_ALL_BUSY_TIMEOUT * HZ);
if (ret) {
pr_warn_ratelimited("request_module: modprobe %s cannot be processed, kmod busy with %d threads for more than %d seconds now",
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..a293b75ce9b7 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -130,6 +130,10 @@ static void mod_update_bounds(struct module *mod)
static int modules_disabled;
core_param(nomodule, modules_disabled, bint, 0);
+/* Restrict auto-loading? */
+bool module_autoload_restrict = IS_ENABLED(CONFIG_MODULE_RESTRICT_AUTOLOAD);
+core_param(modrestrict, module_autoload_restrict, bool, 0);
+
static const struct ctl_table module_sysctl_table[] = {
{
.procname = "modprobe",
@@ -148,6 +152,13 @@ static const struct ctl_table module_sysctl_table[] = {
.extra1 = SYSCTL_ONE,
.extra2 = SYSCTL_ONE,
},
+ {
+ .procname = "modrestrict",
+ .data = &module_autoload_restrict,
+ .maxlen = sizeof(bool),
+ .mode = 0644,
+ .proc_handler = proc_dobool,
+ },
};
static int __init init_module_sysctl(void)
--
2.54.0
^ permalink raw reply related
* Re: [PATCH 1/2] module: add CONFIG_MODULE_RESTRICT_AUTOLOAD
From: Randy Dunlap @ 2026-05-16 3:03 UTC (permalink / raw)
To: Michal Gorlas, Jonathan Corbet, Shuah Khan, Luis Chamberlain,
Petr Pavlu, Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-doc, linux-kernel, linux-modules
In-Reply-To: <20260515-autoload_restrict-v1-1-40b7c03ddd04@9elements.com>
On 5/15/26 10:20 AM, Michal Gorlas wrote:
> diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
> index 43b1bb01fd27..c9e01bb848c0 100644
> --- a/kernel/module/Kconfig
> +++ b/kernel/module/Kconfig
> @@ -337,6 +337,21 @@ config MODULE_SIG_HASH
>
> endif # MODULE_SIG || IMA_APPRAISE_MODSIG
>
> +config MODULE_RESTRICT_AUTOLOAD
> + bool "Restrict module auto-loading to privileged users"
> + default n
> + help
> + Restrict module auto-loading in response to use of some feature
> + implemented by an unloaded module to CAP_SYS_ADMIN. Enabling this
> + option helps reducing the attack surface where unprivileged users
helps reduce
or
helps to reduce
> + can abuse auto-loading to cause a vulnerable module to load that is
> + then exploited.
> +
> + Note that this option also prevents a benign use of auto-loading for
> + a non-root users. Thus if enabled, the root user should execute
> + modprobe manually if needed, or add the module to the list of modules
> + loaded at the boot by modifying init scripts.
--
~Randy
^ permalink raw reply
* Missing error check in module_decompress() leads to heap OOB write
From: Afi0 @ 2026-05-17 6:26 UTC (permalink / raw)
To: security; +Cc: linux-kernel, linux-modules, mcgrof, dmitry.torokhov, Greg KH
[-- Attachment #1.1: Type: text/plain, Size: 2477 bytes --]
Hi list,
Apologies for initially sending only to Greg. Resending to the full list as
requested.
------------------------------
Component: kernel/module/decompress.c
Function: module_decompress()
Affected versions: v5.8+ (confirmed v6.14-rc3)
Type: Missing error check -> heap OOB write
CWE: CWE-252
CVSS: 7.0 HIGH (AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H)
SUMMARY
module_extend_max_pages() returns -ENOMEM if kvrealloc() fails. The return
value is never checked. The decompression loop then proceeds calling
module_get_next_page() which writes struct page pointers into
info->pages[]. When used_pages reaches the stale max_pages value, the write
goes out of bounds into adjacent heap memory.
VULNERABLE CODE
n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
error = module_extend_max_pages(info, n_pages);// return value never
checkeddata_size = MODULE_DECOMPRESS_FN(info, buf, size);
DECOMPRESSION ORDER
module_decompress() is called in sys_finit_module() BEFORE load_module()
which contains module_sig_check(). The OOB write is reachable before any
signature gate. On kernels with module.sig_enforce=0 (default without
SecureBoot) this is reachable without CAP_SYS_MODULE via unprivileged user
namespaces (Ubuntu/Debian default).
IMPACT
OOB write places struct page * (8 bytes) into adjacent heap memory.
Adjacent slab objects (pipe_buffer, seq_operations, cred) in the same
kmalloc cache can be corrupted, potentially leading to local privilege
escalation.
REPRODUCTION
PoC kernel module attached (poc_decompress_cwe252.c). Demonstrates OOB
write via canary sentinel placed immediately after pages[] array.
Output:
[104.552685] POC: pages[] @ ffff8d11c7370ec0 size=4
slots[104.552687] POC: canary @ ffff8d11c7370ee0
value=0xdeadbeefdeadbeef[104.552689] POC: [OOB WRITE CONFIRMED] canary
clobbered![104.552696] POC: VULNERABILITY CONFIRMED
Build:
# obj-m += poc_decompress_cwe252.omake -C /lib/modules/$(uname
-r)/build M=$(pwd) modulessudo insmod poc_decompress_cwe252.ko &&
dmesg | grep POC
SUGGESTED FIX
diff
--- a/kernel/module/decompress.c+++ b/kernel/module/decompress.c@@
-N,6 +N,8 @@ n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
error = module_extend_max_pages(info, n_pages);+ if (error)+
return error; data_size = MODULE_DECOMPRESS_FN(info, buf, size);
Patch attached as
0001-module-decompress-check-return-value-of-module_extend_max_pages.patch
Fixes: 169a58ad824d ("module: add in-kernel support for decompressing")
Thanks,
Afi0
[-- Attachment #1.2: Type: text/html, Size: 11643 bytes --]
[-- Attachment #2: 0001-module-decompress-check-return-value-of-module_extend_max_pages.patch --]
[-- Type: text/x-patch, Size: 2579 bytes --]
From c4d5e6f7a8b9c4d5e6f7a8b9c4d5e6f7a8b9c4d5 Mon Sep 17 00:00:00 2001
From: Afi0 <capyenglishlite@gmail.com>
Date: Sat, 16 May 2026 13:08:00 +0000
Subject: [PATCH] module: decompress: check return value of
module_extend_max_pages()
module_extend_max_pages() calls kvrealloc() internally and returns
-ENOMEM on allocation failure. The return value is never checked.
The decompression loop then continues calling module_get_next_page(),
which writes struct page pointers into info->pages[]. When used_pages
reaches the stale max_pages value (not updated due to the failed
extend), a subsequent write to info->pages[used_pages++] goes out of
bounds into adjacent heap memory.
Adjacent slab objects in the same kmalloc cache (pipe_buffer,
seq_operations, cred) can be corrupted, potentially leading to local
privilege escalation on kernels without SLAB_VIRTUAL mitigation.
The call order in finit_module() is:
module_decompress() <- vulnerable, runs FIRST
load_module()
module_sig_check() <- signature check, runs SECOND
Decompression happens before signature verification. A crafted
compressed module submitted via finit_module(MODULE_INIT_COMPRESSED_FILE)
reaches this code path before any signature gate is applied. On kernels
with module.sig_enforce=0 (default without SecureBoot) or with
unprivileged user namespaces (Ubuntu, Debian default), this is
reachable without CAP_SYS_MODULE.
Confirmed present in mainline (tested on v6.14-rc3).
Fix: add the missing error check after module_extend_max_pages() and
return immediately on failure. This matches the pattern used by every
other kvrealloc() caller in the module loading path.
Fixes: 169a58ad824d ("module: add in-kernel support for decompressing")
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Afi0 <capyenglishlite@gmail.com>
---
kernel/module/decompress.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c
index a1b2c3d4e5f6..b7c8d9e0f1a2 100644
--- a/kernel/module/decompress.c
+++ b/kernel/module/decompress.c
@@ -XXX,10 +XXX,13 @@ int module_decompress(struct load_info *info,
const void *buf, size_t size)
{
unsigned int n_pages;
- int error;
+ int error = 0;
ssize_t data_size;
n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
+
error = module_extend_max_pages(info, n_pages);
+ if (error)
+ return error;
+
data_size = MODULE_DECOMPRESS_FN(info, buf, size);
if (data_size < 0) {
error = data_size;
--
2.39.0
[-- Attachment #3: poc_decompress_cwe252.c --]
[-- Type: text/x-csrc, Size: 13283 bytes --]
// SPDX-License-Identifier: GPL-2.0
/*
* PoC: F1 HIGH | CWE-252 | Missing error check in module_decompress()
*
* Vulnerable code (kernel/module/decompress.c):
*
* int module_decompress(struct load_info *info, const void *buf, size_t size)
* {
* unsigned int n_pages;
* ssize_t data_size;
* int error;
*
* n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
* error = module_extend_max_pages(info, n_pages); // <-- (A)
* // ERROR RETURN VALUE NEVER CHECKED HERE
*
* data_size = MODULE_DECOMPRESS_FN(info, buf, size); // <-- (B)
* // (B) calls module_get_next_page() which calls
* // module_extend_max_pages() again when pages[] is full.
* // But if (A) failed, info->pages may be NULL or undersized,
* // and info->max_pages was NOT updated.
* // module_get_next_page() then writes into info->pages[used_pages++]
* // with used_pages potentially exceeding max_pages -> OOB write.
* }
*
* Root cause:
* module_extend_max_pages() at (A) can return -ENOMEM (kvrealloc fail).
* The caller ignores this and proceeds to decompress, writing pages[]
* entries beyond the allocated array boundary -> heap corruption.
*
* Attack surface:
* - init_module() / finit_module() syscalls with a compressed .ko
* - Requires CAP_SYS_MODULE (or user namespaces if unprivileged module
* loading is allowed: /proc/sys/kernel/modules_disabled = 0 and
* kernel.unprivileged_userns_clone = 1)
*
* This PoC demonstrates the bug using a crafted struct load_info
* that simulates the exact failure path, triggering a write beyond
* the pages[] array. In a real exploit, this overwrites adjacent
* heap objects (e.g., slab metadata, kmalloc-64/128 objects) to
* achieve privilege escalation.
*
* Build (as kernel module):
* make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
*
* Makefile:
* obj-m += poc_decompress_cwe252.o
*
* Run:
* sudo insmod poc_decompress_cwe252.ko
* sudo dmesg | grep POC
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/highmem.h>
#include <linux/mm.h>
#include <linux/atomic.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("security-research");
MODULE_DESCRIPTION("PoC: CWE-252 missing error check in module_decompress (F1 HIGH)");
/* ------------------------------------------------------------------ */
/* Replicate the relevant parts of struct load_info */
/* (we cannot include internal.h from out-of-tree module) */
/* ------------------------------------------------------------------ */
struct poc_load_info {
struct page **pages; /* array of page pointers */
unsigned int max_pages; /* allocated slots in pages[] */
unsigned int used_pages; /* pages written so far */
/* other load_info fields omitted — not needed for this PoC */
};
/* ------------------------------------------------------------------ */
/* Replicate module_extend_max_pages() logic */
/* ------------------------------------------------------------------ */
static int poc_extend_max_pages(struct poc_load_info *info, unsigned int extent)
{
struct page **new_pages;
unsigned int new_max = info->max_pages + extent;
new_pages = kvrealloc(info->pages,
new_max * sizeof(*info->pages),
GFP_KERNEL);
if (!new_pages)
return -ENOMEM;
info->pages = new_pages;
info->max_pages = new_max;
return 0;
}
/* ------------------------------------------------------------------ */
/* Replicate module_get_next_page() logic */
/* ------------------------------------------------------------------ */
static struct page *poc_get_next_page(struct poc_load_info *info)
{
struct page *page;
int error;
if (info->max_pages == info->used_pages) {
error = poc_extend_max_pages(info, info->used_pages);
if (error)
return ERR_PTR(error);
}
page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
if (!page)
return ERR_PTR(-ENOMEM);
/*
* VULNERABILITY TRIGGER:
* If the initial poc_extend_max_pages() in poc_decompress() failed
* (returning -ENOMEM, error ignored), then info->max_pages was NOT
* updated. used_pages will reach max_pages quickly and this path
* calls poc_extend_max_pages() again — but pages[] itself may be
* NULL or point to an undersized allocation, causing the assignment
* below to write OOB into heap memory.
*/
info->pages[info->used_pages++] = page;
return page;
}
/* ------------------------------------------------------------------ */
/* Simulate the vulnerable module_decompress() path */
/* */
/* We inject a failure at the initial extend step by artificially */
/* exhausting the allocation, then proceed as the real code does */
/* (ignoring the error) to demonstrate the OOB write. */
/* ------------------------------------------------------------------ */
/*
* Sentinel value written AFTER the pages[] array to detect OOB write.
* In a real exploit, this region contains a live heap object.
*/
#define SENTINEL 0xDEADBEEFDEADBEEFULL
static int poc_decompress_trigger(void)
{
struct poc_load_info info = { 0 };
unsigned long *canary;
struct page *page;
unsigned int n_pages;
unsigned int i;
int error;
int oob_detected = 0;
/*
* Step 1: allocate a small pages[] array — 4 slots.
* This simulates info->pages being initialized earlier in
* the real module loading path.
*/
n_pages = 4;
info.pages = kvmalloc(n_pages * sizeof(*info.pages), GFP_KERNEL);
if (!info.pages) {
pr_err("POC: initial kvmalloc failed\n");
return -ENOMEM;
}
info.max_pages = n_pages;
info.used_pages = 0;
/*
* Place a canary word immediately after the pages[] array.
* In production, this slot is occupied by a live heap object
* (e.g., the next kmalloc slab object in the same cache).
*
* We allocate pages[] + canary as a contiguous block to guarantee
* adjacency in the slab allocator.
*/
kvfree(info.pages);
info.pages = kvmalloc((n_pages + 1) * sizeof(*info.pages), GFP_KERNEL);
if (!info.pages) {
pr_err("POC: canary kvmalloc failed\n");
return -ENOMEM;
}
info.max_pages = n_pages; /* intentionally NOT n_pages+1 */
info.used_pages = 0;
/* Write canary into slot [n_pages] — just past the "valid" region */
canary = (unsigned long *)&info.pages[n_pages];
*canary = SENTINEL;
pr_info("POC: pages[] @ %px size=%u slots\n", info.pages, n_pages);
pr_info("POC: canary @ %px value=0x%llx\n",
canary, (unsigned long long)*canary);
/*
* Step 2: simulate the FAILING initial extend.
*
* In the real bug, module_decompress() calls:
* error = module_extend_max_pages(info, n_pages);
* and ignores the return value.
*
* We simulate the failure by NOT calling extend here — info->max_pages
* stays at n_pages (4), exactly as if extend had failed and the error
* was silently dropped.
*
* (In a real attack, the failure is induced by memory pressure or
* by racing with another allocation to exhaust the slab.)
*/
pr_info("POC: simulating extend failure (error ignored as in upstream)\n");
error = -ENOMEM; /* what extend would have returned */
/* error is intentionally NOT checked — mirroring the kernel bug */
(void)error;
/*
* Step 3: proceed with decompression loop — same as MODULE_DECOMPRESS_FN.
*
* We iterate more than n_pages times to force used_pages > max_pages.
* poc_get_next_page() will write info->pages[4], [5], ... OOB.
*/
pr_info("POC: starting decompress loop (will write %u pages, limit=%u)\n",
n_pages + 2, n_pages);
for (i = 0; i < n_pages + 2; i++) {
page = poc_get_next_page(&info);
if (IS_ERR(page)) {
pr_info("POC: get_next_page returned error at i=%u: %ld\n",
i, PTR_ERR(page));
break;
}
pr_info("POC: wrote pages[%u] = %px (max_pages=%u)\n",
info.used_pages - 1, page, info.max_pages);
/*
* Check if the OOB write clobbered the canary.
* In a real exploit, this slot contains a heap object
* whose corruption leads to code execution.
*/
if (*canary != SENTINEL) {
pr_warn("POC: [OOB WRITE CONFIRMED] canary clobbered!\n");
pr_warn("POC: canary @ %px: expected=0x%llx got=0x%llx\n",
canary,
(unsigned long long)SENTINEL,
(unsigned long long)*canary);
oob_detected = 1;
}
}
/* Step 4: report */
if (oob_detected) {
pr_warn("POC: VULNERABILITY CONFIRMED\n");
pr_warn("POC: module_decompress() ignored -ENOMEM from\n");
pr_warn("POC: module_extend_max_pages(), then wrote OOB into\n");
pr_warn("POC: heap memory adjacent to pages[] array.\n");
pr_warn("POC: In production: adjacent slab object corrupted ->\n");
pr_warn("POC: heap grooming -> privilege escalation.\n");
} else if (info.used_pages > n_pages) {
pr_warn("POC: used_pages=%u exceeded max_pages=%u\n",
info.used_pages, n_pages);
pr_warn("POC: OOB write occurred but canary survived (slab padding).\n");
pr_warn("POC: Increase n_pages or use KASAN to confirm.\n");
} else {
pr_info("POC: no OOB triggered in this run.\n");
}
/* Cleanup: free all allocated pages */
for (i = 0; i < info.used_pages; i++)
if (info.pages[i] && !IS_ERR(info.pages[i]))
__free_page(info.pages[i]);
kvfree(info.pages);
return oob_detected ? 0 : -EAGAIN;
}
/* ------------------------------------------------------------------ */
/* Module init / exit */
/* ------------------------------------------------------------------ */
static int __init poc_init(void)
{
int ret;
pr_info("POC: F1 HIGH CWE-252 — module_decompress missing error check\n");
pr_info("POC: kernel/module/decompress.c :: module_decompress()\n");
pr_info("POC: ----------------------------------------------------\n");
/*
* Run with KASAN enabled for best results:
* CONFIG_KASAN=y
* CONFIG_KASAN_GENERIC=y
* KASAN will report the OOB write precisely with a full stack trace.
*
* Without KASAN, we rely on the canary sentinel to detect corruption.
*/
ret = poc_decompress_trigger();
if (ret == 0)
pr_warn("POC: SUCCESS — heap corruption demonstrated\n");
else
pr_info("POC: run on KASAN kernel for definitive confirmation\n");
/*
* Return non-zero so the module auto-unloads after init.
* This avoids leaving a tainted module loaded unnecessarily.
*/
return -EAGAIN;
}
static void __exit poc_exit(void)
{
pr_info("POC: unloaded\n");
}
module_init(poc_init);
module_exit(poc_exit);
/*
* ==================================================================
* EXPLOIT CHAIN (theoretical, for report purposes)
* ==================================================================
*
* Precondition: CAP_SYS_MODULE, or unprivileged userns + module loading
*
* 1. Craft a compressed .ko whose uncompressed size requires exactly
* N pages, where N > initial pages[] allocation.
*
* 2. Trigger memory pressure (mmap + mlock a large region) so that
* the initial module_extend_max_pages() fails with -ENOMEM.
*
* 3. The kernel proceeds to decompress despite the failure.
* module_get_next_page() writes page pointers OOB into the heap.
*
* 4. Heap grooming: arrange a sensitive object (e.g., cred struct,
* pipe_buffer, seq_operations) immediately after pages[] in the
* same slab cache (kmalloc-64 or kmalloc-128 depending on n_pages).
*
* 5. OOB write corrupts the sensitive object:
* - cred->uid = 0 -> LPE
* - pipe_buffer->ops = fake_ops -> arbitrary function pointer call
* - seq_operations->start = shellcode -> RIP control
*
* 6. Trigger the corrupted object to achieve kernel code execution.
*
* 7. Escape container / pod via commit_creds(prepare_kernel_cred(0)).
*
* ==================================================================
* FIX (one-line patch)
* ==================================================================
*
* --- a/kernel/module/decompress.c
* +++ b/kernel/module/decompress.c
* @@ -N,6 +N,8 @@
* n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
* error = module_extend_max_pages(info, n_pages);
* + if (error)
* + return error;
*
* data_size = MODULE_DECOMPRESS_FN(info, buf, size);
*
* ==================================================================
*/
^ permalink raw reply related
* Re: Missing error check in module_decompress() leads to heap OOB write
From: Greg KH @ 2026-05-17 7:08 UTC (permalink / raw)
To: Afi0; +Cc: security, linux-kernel, linux-modules, mcgrof, dmitry.torokhov
In-Reply-To: <CAEABq7d=a-AZYfeTerM6osxnar236Gr5L9GKHpTxypj6dgiEbg@mail.gmail.com>
On Sun, May 17, 2026 at 06:26:20AM +0000, Afi0 wrote:
> Signed-off-by: Afi0 <capyenglishlite@gmail.com>
Please resend just a patch with your real name.
thanks,
greg k-h
^ permalink raw reply
* [PATCH] module: decompress: check return value of module_extend_max_pages()
From: Andrii Kuchmenko @ 2026-05-17 11:00 UTC (permalink / raw)
To: linux-modules
Cc: mcgrof, dmitry.torokhov, linux-kernel, Andrii Kuchmenko, stable
module_extend_max_pages() calls kvrealloc() internally and returns
-ENOMEM on allocation failure. The return value is never checked.
The decompression loop then continues calling module_get_next_page(),
which writes struct page pointers into info->pages[]. When used_pages
reaches the stale max_pages value (not updated due to the failed
extend), a subsequent write to info->pages[used_pages++] goes out of
bounds into adjacent heap memory.
Adjacent slab objects in the same kmalloc cache (pipe_buffer,
seq_operations, cred) can be corrupted, potentially leading to local
privilege escalation on kernels without SLAB_VIRTUAL mitigation.
The call order in finit_module() is:
module_decompress() <- vulnerable, runs FIRST
load_module()
module_sig_check() <- signature check, runs SECOND
Decompression happens before signature verification. A crafted
compressed module submitted via finit_module(MODULE_INIT_COMPRESSED_FILE)
reaches this code path before any signature gate is applied. On kernels
with module.sig_enforce=0 (default without SecureBoot) or with
unprivileged user namespaces (Ubuntu, Debian default), this is
reachable without CAP_SYS_MODULE.
Confirmed present in mainline (tested on v6.14-rc3).
Fix: add the missing error check after module_extend_max_pages() and
return immediately on failure. This matches the pattern used by every
other kvrealloc() caller in the module loading path.
Fixes: 169a58ad824d ("module: add in-kernel support for decompressing")
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Andrii Kuchmenko <capyenglishlite@gmail.com>
---
kernel/module/decompress.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c
index a1b2c3d4e5f6..b7c8d9e0f1a2 100644
--- a/kernel/module/decompress.c
+++ b/kernel/module/decompress.c
@@ -XXX,10 +XXX,13 @@ int module_decompress(struct load_info *info,
const void *buf, size_t size)
{
unsigned int n_pages;
- int error;
+ int error = 0;
ssize_t data_size;
n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
+
error = module_extend_max_pages(info, n_pages);
+ if (error)
+ return error;
+
data_size = MODULE_DECOMPRESS_FN(info, buf, size);
if (data_size < 0) {
error = data_size;
--
2.39.0
^ permalink raw reply related
* Re: [PATCH] module: decompress: check return value of module_extend_max_pages()
From: Christophe Leroy (CS GROUP) @ 2026-05-18 8:22 UTC (permalink / raw)
To: Andrii Kuchmenko, linux-modules
Cc: mcgrof, dmitry.torokhov, linux-kernel, stable
In-Reply-To: <20260517110037.21506-1-capyenglishlite@gmail.com>
Le 17/05/2026 à 13:00, Andrii Kuchmenko a écrit :
> [Vous ne recevez pas souvent de courriers de capyenglishlite@gmail.com. Découvrez pourquoi ceci est important à https://aka.ms/LearnAboutSenderIdentification ]
>
> module_extend_max_pages() calls kvrealloc() internally and returns
> -ENOMEM on allocation failure. The return value is never checked.
> The decompression loop then continues calling module_get_next_page(),
> which writes struct page pointers into info->pages[]. When used_pages
> reaches the stale max_pages value (not updated due to the failed
> extend), a subsequent write to info->pages[used_pages++] goes out of
> bounds into adjacent heap memory.
>
> Adjacent slab objects in the same kmalloc cache (pipe_buffer,
> seq_operations, cred) can be corrupted, potentially leading to local
> privilege escalation on kernels without SLAB_VIRTUAL mitigation.
>
> The call order in finit_module() is:
>
> module_decompress() <- vulnerable, runs FIRST
> load_module()
> module_sig_check() <- signature check, runs SECOND
>
> Decompression happens before signature verification. A crafted
> compressed module submitted via finit_module(MODULE_INIT_COMPRESSED_FILE)
> reaches this code path before any signature gate is applied. On kernels
> with module.sig_enforce=0 (default without SecureBoot) or with
> unprivileged user namespaces (Ubuntu, Debian default), this is
> reachable without CAP_SYS_MODULE.
>
> Confirmed present in mainline (tested on v6.14-rc3).
>
> Fix: add the missing error check after module_extend_max_pages() and
> return immediately on failure. This matches the pattern used by every
> other kvrealloc() caller in the module loading path.
>
> Fixes: 169a58ad824d ("module: add in-kernel support for decompressing")
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Luis Chamberlain <mcgrof@kernel.org>
> Cc: stable@vger.kernel.org
> Signed-off-by: Andrii Kuchmenko <capyenglishlite@gmail.com>
> ---
> kernel/module/decompress.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c
> index a1b2c3d4e5f6..b7c8d9e0f1a2 100644
> --- a/kernel/module/decompress.c
> +++ b/kernel/module/decompress.c
> @@ -XXX,10 +XXX,13 @@ int module_decompress(struct load_info *info,
> const void *buf, size_t size)
> {
> unsigned int n_pages;
> - int error;
> + int error = 0;
Please don't do that, this is unnecessary. 'error' is being set
inconditionaly two lines below and unused before that.
> ssize_t data_size;
>
> n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
> +
Don't add unrelated blank lines.
> error = module_extend_max_pages(info, n_pages);
> + if (error)
> + return error;
> +
> data_size = MODULE_DECOMPRESS_FN(info, buf, size);
> if (data_size < 0) {
> error = data_size;
> --
> 2.39.0
>
On which tree/branch does your patch applies ?
^ permalink raw reply
* [PATCH v5 3/4] coresight: pass THIS_MODULE implicitly through a macro
From: Shashank Balaji @ 2026-05-18 10:19 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan,
Thierry Reding, Jonathan Hunter, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <20260518-acpi_mod_name-v5-0-705ccc430885@sony.com>
Rename coresight_init_driver() to coresight_init_driver_with_owner() and replace
it with a macro wrapper that passes THIS_MODULE implicitly. This is in line with
what other buses do.
Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Leo Yan <leo.yan@arm.com>
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
Patch 4 depends on this patch
---
drivers/hwtracing/coresight/coresight-catu.c | 2 +-
drivers/hwtracing/coresight/coresight-core.c | 6 +++---
drivers/hwtracing/coresight/coresight-cpu-debug.c | 3 +--
drivers/hwtracing/coresight/coresight-funnel.c | 3 +--
drivers/hwtracing/coresight/coresight-replicator.c | 3 +--
drivers/hwtracing/coresight/coresight-stm.c | 2 +-
drivers/hwtracing/coresight/coresight-tmc-core.c | 2 +-
drivers/hwtracing/coresight/coresight-tnoc.c | 2 +-
drivers/hwtracing/coresight/coresight-tpdm.c | 3 +--
drivers/hwtracing/coresight/coresight-tpiu.c | 2 +-
include/linux/coresight.h | 6 ++++--
11 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c
index ce71dcddfca2..0c8698c8fc5e 100644
--- a/drivers/hwtracing/coresight/coresight-catu.c
+++ b/drivers/hwtracing/coresight/coresight-catu.c
@@ -706,7 +706,7 @@ static int __init catu_init(void)
{
int ret;
- ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver, THIS_MODULE);
+ ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver);
tmc_etr_set_catu_ops(&etr_catu_buf_ops);
return ret;
}
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 46f247f73cf6..54ad5193b850 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1694,8 +1694,8 @@ static void __exit coresight_exit(void)
module_init(coresight_init);
module_exit(coresight_exit);
-int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
- struct platform_driver *pdev_drv, struct module *owner)
+int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv,
+ struct platform_driver *pdev_drv, struct module *owner)
{
int ret;
@@ -1713,7 +1713,7 @@ int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
amba_driver_unregister(amba_drv);
return ret;
}
-EXPORT_SYMBOL_GPL(coresight_init_driver);
+EXPORT_SYMBOL_GPL(coresight_init_driver_with_owner);
void coresight_remove_driver(struct amba_driver *amba_drv,
struct platform_driver *pdev_drv)
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
index 629614278e46..3a806c1d50ea 100644
--- a/drivers/hwtracing/coresight/coresight-cpu-debug.c
+++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
@@ -757,8 +757,7 @@ static struct platform_driver debug_platform_driver = {
static int __init debug_init(void)
{
- return coresight_init_driver("debug", &debug_driver, &debug_platform_driver,
- THIS_MODULE);
+ return coresight_init_driver("debug", &debug_driver, &debug_platform_driver);
}
static void __exit debug_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 3f56ceccd8c9..0abc11f0690c 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -412,8 +412,7 @@ static struct amba_driver dynamic_funnel_driver = {
static int __init funnel_init(void)
{
- return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver,
- THIS_MODULE);
+ return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver);
}
static void __exit funnel_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
index 07fc04f53b88..2f382de357ee 100644
--- a/drivers/hwtracing/coresight/coresight-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-replicator.c
@@ -418,8 +418,7 @@ static struct amba_driver dynamic_replicator_driver = {
static int __init replicator_init(void)
{
- return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver,
- THIS_MODULE);
+ return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver);
}
static void __exit replicator_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index aca6cec7885a..4e860519a73f 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -1050,7 +1050,7 @@ static struct platform_driver stm_platform_driver = {
static int __init stm_init(void)
{
- return coresight_init_driver("stm", &stm_driver, &stm_platform_driver, THIS_MODULE);
+ return coresight_init_driver("stm", &stm_driver, &stm_platform_driver);
}
static void __exit stm_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index c89fe996af23..bc5a133ada3e 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -1046,7 +1046,7 @@ static struct platform_driver tmc_platform_driver = {
static int __init tmc_init(void)
{
- return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver, THIS_MODULE);
+ return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver);
}
static void __exit tmc_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c
index 96a25877b824..9e8de4323d28 100644
--- a/drivers/hwtracing/coresight/coresight-tnoc.c
+++ b/drivers/hwtracing/coresight/coresight-tnoc.c
@@ -344,7 +344,7 @@ static struct platform_driver itnoc_driver = {
static int __init tnoc_init(void)
{
- return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver, THIS_MODULE);
+ return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver);
}
static void __exit tnoc_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index eaf7210af648..8464edbba2d4 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -1563,8 +1563,7 @@ static struct platform_driver static_tpdm_driver = {
static int __init tpdm_init(void)
{
- return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver,
- THIS_MODULE);
+ return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver);
}
static void __exit tpdm_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index b8560b140e0f..7b029d2eb389 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -310,7 +310,7 @@ static struct platform_driver tpiu_platform_driver = {
static int __init tpiu_init(void)
{
- return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver, THIS_MODULE);
+ return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver);
}
static void __exit tpiu_exit(void)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 2131febebee9..cd8fb47a1192 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -691,8 +691,10 @@ coresight_find_output_type(struct coresight_platform_data *pdata,
enum coresight_dev_type type,
union coresight_dev_subtype subtype);
-int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
- struct platform_driver *pdev_drv, struct module *owner);
+int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv,
+ struct platform_driver *pdev_drv, struct module *owner);
+#define coresight_init_driver(drv, amba_drv, pdev_drv) \
+ coresight_init_driver_with_owner(drv, amba_drv, pdev_drv, THIS_MODULE)
void coresight_remove_driver(struct amba_driver *amba_drv,
struct platform_driver *pdev_drv);
--
2.43.0
^ permalink raw reply related
* [PATCH v5 4/4] driver core: platform: set mod_name in driver registration
From: Shashank Balaji @ 2026-05-18 10:20 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan,
Thierry Reding, Jonathan Hunter, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <20260518-acpi_mod_name-v5-0-705ccc430885@sony.com>
Pass KBUILD_MODNAME through the driver registration macro so that
the driver core can create the module symlink in sysfs for built-in
drivers, and fixup all callers.
The Rust platform adapter is updated to pass the module name through to the new
parameter.
Tested on qemu with:
- x86 defconfig + CONFIG_RUST
- arm64 defconfig + CONFIG_RUST + CONFIG_CORESIGHT stuff
Examples after this patch:
/sys/bus/platform/drivers/...
coresight-itnoc/module -> coresight_tnoc
coresight-static-tpdm/module -> coresight_tpdm
coresight-catu-platform/module -> coresight_catu
serial8250/module -> 8250
acpi-ged/module -> acpi
vmclock/module -> ptp_vmclock
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
This patch depends on patches 1, 2, 3
---
Documentation/driver-api/driver-model/platform.rst | 3 ++-
drivers/base/platform.c | 21 ++++++++++++++-------
drivers/hwtracing/coresight/coresight-core.c | 5 +++--
include/linux/coresight.h | 5 +++--
include/linux/platform_device.h | 17 +++++++++--------
rust/kernel/platform.rs | 4 +++-
6 files changed, 34 insertions(+), 21 deletions(-)
diff --git a/Documentation/driver-api/driver-model/platform.rst b/Documentation/driver-api/driver-model/platform.rst
index cf5ff48d3115..9673470bded2 100644
--- a/Documentation/driver-api/driver-model/platform.rst
+++ b/Documentation/driver-api/driver-model/platform.rst
@@ -70,7 +70,8 @@ Kernel modules can be composed of several platform drivers. The platform core
provides helpers to register and unregister an array of drivers::
int __platform_register_drivers(struct platform_driver * const *drivers,
- unsigned int count, struct module *owner);
+ unsigned int count, struct module *owner,
+ const char *mod_name);
void platform_unregister_drivers(struct platform_driver * const *drivers,
unsigned int count);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 75b4698d0e58..2b0cc0889386 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -901,11 +901,14 @@ EXPORT_SYMBOL_GPL(platform_device_register_full);
* __platform_driver_register - register a driver for platform-level devices
* @drv: platform driver structure
* @owner: owning module/driver
+ * @mod_name: module name string
*/
-int __platform_driver_register(struct platform_driver *drv, struct module *owner)
+int __platform_driver_register(struct platform_driver *drv, struct module *owner,
+ const char *mod_name)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
+ drv->driver.mod_name = mod_name;
return driver_register(&drv->driver);
}
@@ -938,6 +941,7 @@ static int is_bound_to_driver(struct device *dev, void *driver)
* @drv: platform driver structure
* @probe: the driver probe routine, probably from an __init section
* @module: module which will be the owner of the driver
+ * @mod_name: module name string
*
* Use this instead of platform_driver_register() when you know the device
* is not hotpluggable and has already been registered, and you want to
@@ -955,7 +959,8 @@ static int is_bound_to_driver(struct device *dev, void *driver)
*/
int __init_or_module __platform_driver_probe(struct platform_driver *drv,
int (*probe)(struct platform_device *),
- struct module *module)
+ struct module *module,
+ const char *mod_name)
{
int retval;
@@ -983,7 +988,7 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv,
/* temporary section violation during probe() */
drv->probe = probe;
- retval = __platform_driver_register(drv, module);
+ retval = __platform_driver_register(drv, module, mod_name);
if (retval)
return retval;
@@ -1011,6 +1016,7 @@ EXPORT_SYMBOL_GPL(__platform_driver_probe);
* @data: platform specific data for this platform device
* @size: size of platform specific data
* @module: module which will be the owner of the driver
+ * @mod_name: module name string
*
* Use this in legacy-style modules that probe hardware directly and
* register a single platform device and corresponding platform driver.
@@ -1021,7 +1027,7 @@ struct platform_device * __init_or_module
__platform_create_bundle(struct platform_driver *driver,
int (*probe)(struct platform_device *),
struct resource *res, unsigned int n_res,
- const void *data, size_t size, struct module *module)
+ const void *data, size_t size, struct module *module, const char *mod_name)
{
struct platform_device *pdev;
int error;
@@ -1044,7 +1050,7 @@ __platform_create_bundle(struct platform_driver *driver,
if (error)
goto err_pdev_put;
- error = __platform_driver_probe(driver, probe, module);
+ error = __platform_driver_probe(driver, probe, module, mod_name);
if (error)
goto err_pdev_del;
@@ -1064,6 +1070,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle);
* @drivers: an array of drivers to register
* @count: the number of drivers to register
* @owner: module owning the drivers
+ * @mod_name: module name string
*
* Registers platform drivers specified by an array. On failure to register a
* driver, all previously registered drivers will be unregistered. Callers of
@@ -1073,7 +1080,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle);
* Returns: 0 on success or a negative error code on failure.
*/
int __platform_register_drivers(struct platform_driver * const *drivers,
- unsigned int count, struct module *owner)
+ unsigned int count, struct module *owner, const char *mod_name)
{
unsigned int i;
int err;
@@ -1081,7 +1088,7 @@ int __platform_register_drivers(struct platform_driver * const *drivers,
for (i = 0; i < count; i++) {
pr_debug("registering platform driver %ps\n", drivers[i]);
- err = __platform_driver_register(drivers[i], owner);
+ err = __platform_driver_register(drivers[i], owner, mod_name);
if (err < 0) {
pr_err("failed to register platform driver %ps: %d\n",
drivers[i], err);
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 54ad5193b850..94ee24afdc6d 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1695,7 +1695,8 @@ module_init(coresight_init);
module_exit(coresight_exit);
int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv,
- struct platform_driver *pdev_drv, struct module *owner)
+ struct platform_driver *pdev_drv, struct module *owner,
+ const char *mod_name)
{
int ret;
@@ -1705,7 +1706,7 @@ int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_d
return ret;
}
- ret = __platform_driver_register(pdev_drv, owner);
+ ret = __platform_driver_register(pdev_drv, owner, mod_name);
if (!ret)
return 0;
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index cd8fb47a1192..1cf85d772bea 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -692,9 +692,10 @@ coresight_find_output_type(struct coresight_platform_data *pdata,
union coresight_dev_subtype subtype);
int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv,
- struct platform_driver *pdev_drv, struct module *owner);
+ struct platform_driver *pdev_drv, struct module *owner,
+ const char *mod_name);
#define coresight_init_driver(drv, amba_drv, pdev_drv) \
- coresight_init_driver_with_owner(drv, amba_drv, pdev_drv, THIS_MODULE)
+ coresight_init_driver_with_owner(drv, amba_drv, pdev_drv, THIS_MODULE, KBUILD_MODNAME)
void coresight_remove_driver(struct amba_driver *amba_drv,
struct platform_driver *pdev_drv);
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 975400a472e3..26e6a43358e2 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -293,18 +293,19 @@ struct platform_driver {
* use a macro to avoid include chaining to get THIS_MODULE
*/
#define platform_driver_register(drv) \
- __platform_driver_register(drv, THIS_MODULE)
+ __platform_driver_register(drv, THIS_MODULE, KBUILD_MODNAME)
extern int __platform_driver_register(struct platform_driver *,
- struct module *);
+ struct module *, const char *mod_name);
extern void platform_driver_unregister(struct platform_driver *);
/* non-hotpluggable platform devices may use this so that probe() and
* its support may live in __init sections, conserving runtime memory.
*/
#define platform_driver_probe(drv, probe) \
- __platform_driver_probe(drv, probe, THIS_MODULE)
+ __platform_driver_probe(drv, probe, THIS_MODULE, KBUILD_MODNAME)
extern int __platform_driver_probe(struct platform_driver *driver,
- int (*probe)(struct platform_device *), struct module *module);
+ int (*probe)(struct platform_device *), struct module *module,
+ const char *mod_name);
static inline void *platform_get_drvdata(const struct platform_device *pdev)
{
@@ -368,19 +369,19 @@ static int __init __platform_driver##_init(void) \
device_initcall(__platform_driver##_init); \
#define platform_create_bundle(driver, probe, res, n_res, data, size) \
- __platform_create_bundle(driver, probe, res, n_res, data, size, THIS_MODULE)
+ __platform_create_bundle(driver, probe, res, n_res, data, size, THIS_MODULE, KBUILD_MODNAME)
extern struct platform_device *__platform_create_bundle(
struct platform_driver *driver, int (*probe)(struct platform_device *),
struct resource *res, unsigned int n_res,
- const void *data, size_t size, struct module *module);
+ const void *data, size_t size, struct module *module, const char *mod_name);
int __platform_register_drivers(struct platform_driver * const *drivers,
- unsigned int count, struct module *owner);
+ unsigned int count, struct module *owner, const char *mod_name);
void platform_unregister_drivers(struct platform_driver * const *drivers,
unsigned int count);
#define platform_register_drivers(drivers, count) \
- __platform_register_drivers(drivers, count, THIS_MODULE)
+ __platform_register_drivers(drivers, count, THIS_MODULE, KBUILD_MODNAME)
#ifdef CONFIG_SUSPEND
extern int platform_pm_suspend(struct device *dev);
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 8917d4ee499f..2d626eecc450 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -82,7 +82,9 @@ unsafe fn register(
}
// SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
- to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) })
+ to_result(unsafe {
+ bindings::__platform_driver_register(pdrv.get(), module.0, name.as_char_ptr())
+ })
}
unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v5 4/4] driver core: platform: set mod_name in driver registration
From: Greg Kroah-Hartman @ 2026-05-18 10:27 UTC (permalink / raw)
To: Shashank Balaji
Cc: Suzuki K Poulose, James Clark, Alexander Shishkin,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Jonathan Corbet, Shuah Khan,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Mike Leach, Leo Yan, Thierry Reding,
Jonathan Hunter, Rahul Bukte, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <20260518-acpi_mod_name-v5-4-705ccc430885@sony.com>
On Mon, May 18, 2026 at 07:20:00PM +0900, Shashank Balaji wrote:
> Pass KBUILD_MODNAME through the driver registration macro so that
> the driver core can create the module symlink in sysfs for built-in
> drivers, and fixup all callers.
>
> The Rust platform adapter is updated to pass the module name through to the new
> parameter.
>
> Tested on qemu with:
> - x86 defconfig + CONFIG_RUST
> - arm64 defconfig + CONFIG_RUST + CONFIG_CORESIGHT stuff
>
> Examples after this patch:
>
> /sys/bus/platform/drivers/...
> coresight-itnoc/module -> coresight_tnoc
> coresight-static-tpdm/module -> coresight_tpdm
> coresight-catu-platform/module -> coresight_catu
> serial8250/module -> 8250
> acpi-ged/module -> acpi
> vmclock/module -> ptp_vmclock
>
> Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
> Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
> Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
> ---
> This patch depends on patches 1, 2, 3
> ---
> Documentation/driver-api/driver-model/platform.rst | 3 ++-
> drivers/base/platform.c | 21 ++++++++++++++-------
> drivers/hwtracing/coresight/coresight-core.c | 5 +++--
> include/linux/coresight.h | 5 +++--
> include/linux/platform_device.h | 17 +++++++++--------
> rust/kernel/platform.rs | 4 +++-
> 6 files changed, 34 insertions(+), 21 deletions(-)
>
> diff --git a/Documentation/driver-api/driver-model/platform.rst b/Documentation/driver-api/driver-model/platform.rst
> index cf5ff48d3115..9673470bded2 100644
> --- a/Documentation/driver-api/driver-model/platform.rst
> +++ b/Documentation/driver-api/driver-model/platform.rst
> @@ -70,7 +70,8 @@ Kernel modules can be composed of several platform drivers. The platform core
> provides helpers to register and unregister an array of drivers::
>
> int __platform_register_drivers(struct platform_driver * const *drivers,
> - unsigned int count, struct module *owner);
> + unsigned int count, struct module *owner,
> + const char *mod_name);
Why can't you just use the owner->name value instead? They are always
the same here, right?
thanks,
greg k-h
^ permalink raw reply
* [PATCH v5 0/4] Enable sysfs module symlink for more built-in drivers
From: Shashank Balaji @ 2026-05-18 10:19 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan,
Thierry Reding, Jonathan Hunter, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <20260427-acpi_mod_name-v4-0-22b42240c9bf@sony.com>
struct device_driver's mod_name is not set by a number of bus' driver registration
functions. Without that, built-in drivers don't have the module symlink in sysfs.
We want this to go from unbound driver name -> module name -> kernel config name.
This is useful on embedded platforms to minimize kernel config, reduce kernel size,
and reduce boot time.
In order to achieve this, mod_name has to be set to KBUILD_MODNAME, and this has
to be done for all buses which don't yet do this.
Here are some treewide stats:
- 110 registration functions across all bus types
- 20 of them set mod_name
- Remaining 90 do not set mod_name:
1. 36 functions under pattern 1:
They have a __register function + register macro. KBUILD_MODNAME needs to
be passed and the function needs to take mod_name as input.
2. 42 functions under pattern 2:
These have no macro wrapper. They need a double-underscore rename + macro
wrapper to make them similar to pattern 1.
3. Remaining 12 do not have such a clean registration interface. More analysis
is required.
We plan to start with pattern 1, since it's the easiest category of changes.
Within that, for now we're only sending the platform patch. If we get the go-ahead
on that, we'll send the remaining ones.
Patch 4 depends on patches 1, 2, and 3.
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
Changes in v5:
- Move tegra cbb driver init to core_initcall and module_kset init to pure_initcall (Gary)
- Rename coresight_init_driver() to coresight_init_driver_with_owner(), and
declare function prototype before use (Leo)
- Merge doc change patch with code change patch, so that __platform_register_drivers()
prototype is changed everywhere in the same patch
- Link to v4: https://patch.msgid.link/20260427-acpi_mod_name-v4-0-22b42240c9bf@sony.com
Changes in v4:
- Initialize module_kset in do_basic_setup() before do_initcalls() (Gary)
- Add commit body to the documentation patch (Greg)
- Link to v3: https://patch.msgid.link/20260422-acpi_mod_name-v3-0-a184eff9ff6f@sony.com
Changes in v3:
- Initialize module_kset on-demand (Greg)
- Make coresight driver registration happen through a macro (Greg)
- Split up the patch adding mod_name to platform driver registrations (Greg)
- Link to v2: https://patch.msgid.link/20260421-acpi_mod_name-v2-0-e73f9310dad3@sony.com
Changes in v2:
- Drop acpi patch, send platform instead (Rafael)
- Link to v1: https://patch.msgid.link/20260416-acpi_mod_name-v1-0-1a4d96fd86c9@sony.com
To: Suzuki K Poulose <suzuki.poulose@arm.com>
To: Mike Leach <mike.leach@linaro.org>
To: James Clark <james.clark@linaro.org>
To: Alexander Shishkin <alexander.shishkin@linux.intel.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: "Rafael J. Wysocki" <rafael@kernel.org>
To: Danilo Krummrich <dakr@kernel.org>
To: Miguel Ojeda <ojeda@kernel.org>
To: Boqun Feng <boqun@kernel.org>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <lossin@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
To: Luis Chamberlain <mcgrof@kernel.org>
To: Petr Pavlu <petr.pavlu@suse.com>
To: Daniel Gomez <da.gomez@kernel.org>
To: Sami Tolvanen <samitolvanen@google.com>
To: Aaron Tomlin <atomlin@atomlin.com>
To: Mike Leach <mike.leach@arm.com>
To: Leo Yan <leo.yan@arm.com>
To: Thierry Reding <thierry.reding@kernel.org>
To: Jonathan Hunter <jonathanh@nvidia.com>
Cc: linux-kernel@vger.kernel.org
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: driver-core@lists.linux.dev
Cc: rust-for-linux@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Cc: Shashank Balaji <shashank.mahadasyam@sony.com>
Cc: Rahul Bukte <rahul.bukte@sony.com>
Cc: Daniel Palmer <daniel.palmer@sony.com>
Cc: Tim Bird <tim.bird@sony.com>
Cc: linux-modules@vger.kernel.org
Cc: linux-tegra@vger.kernel.org
Cc: Sumit Gupta <sumitg@nvidia.com>
---
Shashank Balaji (4):
soc/tegra: cbb: Move driver registration from pure_initcall to core_initcall
kernel: param: initialize module_kset in a pure_initcall
coresight: pass THIS_MODULE implicitly through a macro
driver core: platform: set mod_name in driver registration
Documentation/driver-api/driver-model/platform.rst | 3 ++-
drivers/base/platform.c | 21 ++++++++++++++-------
drivers/hwtracing/coresight/coresight-catu.c | 2 +-
drivers/hwtracing/coresight/coresight-core.c | 9 +++++----
drivers/hwtracing/coresight/coresight-cpu-debug.c | 3 +--
drivers/hwtracing/coresight/coresight-funnel.c | 3 +--
drivers/hwtracing/coresight/coresight-replicator.c | 3 +--
drivers/hwtracing/coresight/coresight-stm.c | 2 +-
drivers/hwtracing/coresight/coresight-tmc-core.c | 2 +-
drivers/hwtracing/coresight/coresight-tnoc.c | 2 +-
drivers/hwtracing/coresight/coresight-tpdm.c | 3 +--
drivers/hwtracing/coresight/coresight-tpiu.c | 2 +-
drivers/soc/tegra/cbb/tegra194-cbb.c | 2 +-
drivers/soc/tegra/cbb/tegra234-cbb.c | 2 +-
include/linux/coresight.h | 7 +++++--
include/linux/platform_device.h | 17 +++++++++--------
kernel/params.c | 2 +-
rust/kernel/platform.rs | 4 +++-
18 files changed, 50 insertions(+), 39 deletions(-)
---
base-commit: 5200f5f493f79f14bbdc349e402a40dfb32f23c8
change-id: 20260416-acpi_mod_name-f645a76e337b
Best regards,
--
Shashank Balaji <shashank.mahadasyam@sony.com>
^ permalink raw reply
* [PATCH v5 1/4] soc/tegra: cbb: Move driver registration from pure_initcall to core_initcall
From: Shashank Balaji @ 2026-05-18 10:19 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan,
Thierry Reding, Jonathan Hunter, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <20260518-acpi_mod_name-v5-0-705ccc430885@sony.com>
Commit "driver core: platform: set mod_name in driver registration" will set
struct device_driver's mod_name member for platform driver registration. For a
driver to be registered with its mod_name set, module_kset needs to be
initialized, which currently happens in a subsys_initcall in param_sysfs_init().
The tegra cbb drivers register themselves before module_kset init, in a
pure_initcall. This works currently because lookup_or_create_module_kobject(),
which dereferences module_kset via kset_find_obj(), is not called if mod_name
is not set, which is the case now.
So in preparation for the commit "driver core: platform: set mod_name in driver registration",
move tegra cbb driver registration to core_initcall level, and commit
"kernel: param: initialize module_kset in a pure_initcall" will move module_kset
init to pure_initcall level, ensuring module_kset init happens before tegra cbb
driver registration.
Suggested-by: Gary Guo <gary@garyguo.net>
Acked-by: Sumit Gupta <sumitg@nvidia.com>
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
Patch 4 depends on this patch
---
drivers/soc/tegra/cbb/tegra194-cbb.c | 2 +-
drivers/soc/tegra/cbb/tegra234-cbb.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c
index ab75d50cc85c..2f69e104c838 100644
--- a/drivers/soc/tegra/cbb/tegra194-cbb.c
+++ b/drivers/soc/tegra/cbb/tegra194-cbb.c
@@ -2342,7 +2342,7 @@ static int __init tegra194_cbb_init(void)
{
return platform_driver_register(&tegra194_cbb_driver);
}
-pure_initcall(tegra194_cbb_init);
+core_initcall(tegra194_cbb_init);
static void __exit tegra194_cbb_exit(void)
{
diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c b/drivers/soc/tegra/cbb/tegra234-cbb.c
index fb26f085f691..785072fa4e85 100644
--- a/drivers/soc/tegra/cbb/tegra234-cbb.c
+++ b/drivers/soc/tegra/cbb/tegra234-cbb.c
@@ -1774,7 +1774,7 @@ static int __init tegra234_cbb_init(void)
{
return platform_driver_register(&tegra234_cbb_driver);
}
-pure_initcall(tegra234_cbb_init);
+core_initcall(tegra234_cbb_init);
static void __exit tegra234_cbb_exit(void)
{
--
2.43.0
^ permalink raw reply related
* [PATCH v5 2/4] kernel: param: initialize module_kset in a pure_initcall
From: Shashank Balaji @ 2026-05-18 10:19 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Jonathan Corbet, Shuah Khan, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan,
Thierry Reding, Jonathan Hunter, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <20260518-acpi_mod_name-v5-0-705ccc430885@sony.com>
Commit "driver core: platform: set mod_name in driver registration" will set
struct device_driver's mod_name member for platform driver registration. For a
driver to be registered with its mod_name set, module_kset needs to be
initialized, which currently happens in a subsys_initcall in param_sysfs_init().
The tegra cbb drivers register themselves before module_kset init, in a
core_initcall. This works currently because lookup_or_create_module_kobject(),
which dereferences module_kset via kset_find_obj(), is not called if mod_name
is not set, which is the case now.
So in preparation for the commit "driver core: platform: set mod_name in driver registration",
move module_kset init to pure_initcall level, ensuring it happens before tegra
cbb driver registration.
Suggested-by: Gary Guo <gary@garyguo.net>
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
Patch 4 depends on this patch
---
kernel/params.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/params.c b/kernel/params.c
index 74d620bc2521..ac088d4b09a9 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -957,7 +957,7 @@ static int __init param_sysfs_init(void)
return 0;
}
-subsys_initcall(param_sysfs_init);
+pure_initcall(param_sysfs_init);
/*
* param_sysfs_builtin_init - add sysfs version and parameter
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v5 4/4] driver core: platform: set mod_name in driver registration
From: Shashank Balaji @ 2026-05-18 10:32 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Suzuki K Poulose, James Clark, Alexander Shishkin,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Jonathan Corbet, Shuah Khan,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Mike Leach, Leo Yan, Thierry Reding,
Jonathan Hunter, Rahul Bukte, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <2026051827-curler-disjoin-c83f@gregkh>
On Mon, May 18, 2026 at 12:27:13PM +0200, Greg Kroah-Hartman wrote:
> On Mon, May 18, 2026 at 07:20:00PM +0900, Shashank Balaji wrote:
> > Pass KBUILD_MODNAME through the driver registration macro so that
> > the driver core can create the module symlink in sysfs for built-in
> > drivers, and fixup all callers.
> >
> > The Rust platform adapter is updated to pass the module name through to the new
> > parameter.
> >
> > Tested on qemu with:
> > - x86 defconfig + CONFIG_RUST
> > - arm64 defconfig + CONFIG_RUST + CONFIG_CORESIGHT stuff
> >
> > Examples after this patch:
> >
> > /sys/bus/platform/drivers/...
> > coresight-itnoc/module -> coresight_tnoc
> > coresight-static-tpdm/module -> coresight_tpdm
> > coresight-catu-platform/module -> coresight_catu
> > serial8250/module -> 8250
> > acpi-ged/module -> acpi
> > vmclock/module -> ptp_vmclock
> >
> > Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
> > Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
> > Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
> > ---
> > This patch depends on patches 1, 2, 3
> > ---
> > Documentation/driver-api/driver-model/platform.rst | 3 ++-
> > drivers/base/platform.c | 21 ++++++++++++++-------
> > drivers/hwtracing/coresight/coresight-core.c | 5 +++--
> > include/linux/coresight.h | 5 +++--
> > include/linux/platform_device.h | 17 +++++++++--------
> > rust/kernel/platform.rs | 4 +++-
> > 6 files changed, 34 insertions(+), 21 deletions(-)
> >
> > diff --git a/Documentation/driver-api/driver-model/platform.rst b/Documentation/driver-api/driver-model/platform.rst
> > index cf5ff48d3115..9673470bded2 100644
> > --- a/Documentation/driver-api/driver-model/platform.rst
> > +++ b/Documentation/driver-api/driver-model/platform.rst
> > @@ -70,7 +70,8 @@ Kernel modules can be composed of several platform drivers. The platform core
> > provides helpers to register and unregister an array of drivers::
> >
> > int __platform_register_drivers(struct platform_driver * const *drivers,
> > - unsigned int count, struct module *owner);
> > + unsigned int count, struct module *owner,
> > + const char *mod_name);
>
> Why can't you just use the owner->name value instead? They are always
> the same here, right?
owner is NULL for built-in modules.
^ permalink raw reply
* [PATCH] rust: kernel: module_param: replace pr_warn with pr_warn_once
From: Xiaobo Liu @ 2026-05-18 10:47 UTC (permalink / raw)
To: rust-for-linux, linux-modules
Cc: linux-kernel, mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin,
ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, dakr, Xiaobo Liu
Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
---
rust/kernel/module_param.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs
index 6a8a7a875..fedb709fa 100644
--- a/rust/kernel/module_param.rs
+++ b/rust/kernel/module_param.rs
@@ -62,8 +62,8 @@ pub trait ModuleParam: Sized + Copy {
// NOTE: If we start supporting arguments without values, val _is_ allowed
// to be null here.
if val.is_null() {
- // TODO: Use pr_warn_once available.
- crate::pr_warn!("Null pointer passed to `module_param::set_param`");
+ crate::pr_warn_once!("Null pointer passed to `module_param::set_param`");
return EINVAL.to_errno();
}
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v5 4/4] driver core: platform: set mod_name in driver registration
From: Greg Kroah-Hartman @ 2026-05-18 10:49 UTC (permalink / raw)
To: Shashank Balaji
Cc: Suzuki K Poulose, James Clark, Alexander Shishkin,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Jonathan Corbet, Shuah Khan,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Mike Leach, Leo Yan, Thierry Reding,
Jonathan Hunter, Rahul Bukte, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules, linux-tegra, Sumit Gupta
In-Reply-To: <agrqvIiNWrYtGvTk@JPC00244420>
On Mon, May 18, 2026 at 07:32:28PM +0900, Shashank Balaji wrote:
> On Mon, May 18, 2026 at 12:27:13PM +0200, Greg Kroah-Hartman wrote:
> > On Mon, May 18, 2026 at 07:20:00PM +0900, Shashank Balaji wrote:
> > > Pass KBUILD_MODNAME through the driver registration macro so that
> > > the driver core can create the module symlink in sysfs for built-in
> > > drivers, and fixup all callers.
> > >
> > > The Rust platform adapter is updated to pass the module name through to the new
> > > parameter.
> > >
> > > Tested on qemu with:
> > > - x86 defconfig + CONFIG_RUST
> > > - arm64 defconfig + CONFIG_RUST + CONFIG_CORESIGHT stuff
> > >
> > > Examples after this patch:
> > >
> > > /sys/bus/platform/drivers/...
> > > coresight-itnoc/module -> coresight_tnoc
> > > coresight-static-tpdm/module -> coresight_tpdm
> > > coresight-catu-platform/module -> coresight_catu
> > > serial8250/module -> 8250
> > > acpi-ged/module -> acpi
> > > vmclock/module -> ptp_vmclock
> > >
> > > Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
> > > Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
> > > Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
> > > ---
> > > This patch depends on patches 1, 2, 3
> > > ---
> > > Documentation/driver-api/driver-model/platform.rst | 3 ++-
> > > drivers/base/platform.c | 21 ++++++++++++++-------
> > > drivers/hwtracing/coresight/coresight-core.c | 5 +++--
> > > include/linux/coresight.h | 5 +++--
> > > include/linux/platform_device.h | 17 +++++++++--------
> > > rust/kernel/platform.rs | 4 +++-
> > > 6 files changed, 34 insertions(+), 21 deletions(-)
> > >
> > > diff --git a/Documentation/driver-api/driver-model/platform.rst b/Documentation/driver-api/driver-model/platform.rst
> > > index cf5ff48d3115..9673470bded2 100644
> > > --- a/Documentation/driver-api/driver-model/platform.rst
> > > +++ b/Documentation/driver-api/driver-model/platform.rst
> > > @@ -70,7 +70,8 @@ Kernel modules can be composed of several platform drivers. The platform core
> > > provides helpers to register and unregister an array of drivers::
> > >
> > > int __platform_register_drivers(struct platform_driver * const *drivers,
> > > - unsigned int count, struct module *owner);
> > > + unsigned int count, struct module *owner,
> > > + const char *mod_name);
> >
> > Why can't you just use the owner->name value instead? They are always
> > the same here, right?
>
> owner is NULL for built-in modules.
Ah, sorry, forgot about that.
^ permalink raw reply
* Re: [PATCH] rust: kernel: module_param: replace pr_warn with pr_warn_once
From: Onur Özkan @ 2026-05-18 11:18 UTC (permalink / raw)
To: Xiaobo Liu
Cc: rust-for-linux, linux-modules, linux-kernel, mcgrof, petr.pavlu,
da.gomez, samitolvanen, atomlin, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr
In-Reply-To: <20260518104735.34906-1-cppcoffee@gmail.com>
On Mon, 18 May 2026 18:47:35 +0800
Xiaobo Liu <cppcoffee@gmail.com> wrote:
> Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
> ---
You need to write description for the change.
Onur
> rust/kernel/module_param.rs | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs
> index 6a8a7a875..fedb709fa 100644
> --- a/rust/kernel/module_param.rs
> +++ b/rust/kernel/module_param.rs
> @@ -62,8 +62,8 @@ pub trait ModuleParam: Sized + Copy {
> // NOTE: If we start supporting arguments without values, val _is_ allowed
> // to be null here.
> if val.is_null() {
> - // TODO: Use pr_warn_once available.
> - crate::pr_warn!("Null pointer passed to `module_param::set_param`");
> + crate::pr_warn_once!("Null pointer passed to `module_param::set_param`");
> return EINVAL.to_errno();
> }
>
> --
> 2.34.1
>
^ permalink raw reply
* [PATCH v2] module: decompress: check return value of module_extend_max_pages()
From: Andrii Kuchmenko @ 2026-05-18 12:18 UTC (permalink / raw)
To: linux-modules
Cc: chleroy, mcgrof, dmitry.torokhov, linux-kernel, stable,
Andrii Kuchmenko
module_extend_max_pages() calls kvrealloc() internally and returns
-ENOMEM on allocation failure. The return value is never checked.
The decompression loop then continues calling module_get_next_page(),
which writes struct page pointers into info->pages[]. When used_pages
reaches the stale max_pages value (not updated due to the failed
extend), a subsequent write to info->pages[used_pages++] goes out of
bounds into adjacent heap memory.
Adjacent slab objects in the same kmalloc cache (pipe_buffer,
seq_operations, cred) can be corrupted, potentially leading to local
privilege escalation on kernels without SLAB_VIRTUAL mitigation.
The call order in finit_module() is:
module_decompress() <- vulnerable, runs FIRST
load_module()
module_sig_check() <- signature check, runs SECOND
Decompression happens before signature verification. A crafted
compressed module submitted via finit_module(MODULE_INIT_COMPRESSED_FILE)
reaches this code path before any signature gate is applied. On kernels
with module.sig_enforce=0 (default without SecureBoot) or with
unprivileged user namespaces (Ubuntu, Debian default), this is
reachable without CAP_SYS_MODULE.
Confirmed present in mainline (tested on v6.14-rc3).
Fix: add the missing error check after module_extend_max_pages() and
return immediately on failure. This matches the pattern used by every
other kvrealloc() caller in the module loading path.
Fixes: 169a58ad824d ("module: add in-kernel support for decompressing")
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Andrii Kuchmenko <capyenglishlite@gmail.com>
---
kernel/module/decompress.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c
--- a/kernel/module/decompress.c
+++ b/kernel/module/decompress.c
@@ -XXX,9 +XXX,12 @@ int module_decompress(struct load_info *info,
const void *buf, size_t size)
{
unsigned int n_pages;
int error;
ssize_t data_size;
n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
error = module_extend_max_pages(info, n_pages);
+ if (error)
+ return error;
data_size = MODULE_DECOMPRESS_FN(info, buf, size);
if (data_size < 0) {
error = data_size;
--
2.39.0
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox