mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing
@ 2026-10-05 10:40 lzhan011
  2026-10-05 10:40 ` [PATCH 1/9] kconfig: fix NULL pointer dereference for defaults taken from choice members lzhan011
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

This series fixes bugs in kbuild host programs found by building them
with AddressSanitizer and UndefinedBehaviorSanitizer and fuzzing their
inputs. Most patches include a reproducer.

Patches 1 and 2 fix bugs that also trigger without sanitizers:

 - kconfig: a string/int/hex symbol whose default is a choice member
   gets a NULL value and crashes conf (since v6.11).
 - genksyms: a declaration such as "int a, f(int);" makes genksyms loop
   forever while allocating memory (since v6.14; earlier versions
   silently recorded wrong types for such declarations).

Patches 3-9 fix out-of-bounds reads (fixdep, modpost) and undefined
behaviour reported by UBSan (kallsyms, modpost, sorttable,
tools/include byteshift helpers), several of which trigger on every
normal x86_64 build when the host tools are built with UBSan.

The series is based on next-20261002. With it applied, a defconfig +
MODULES + MODVERSIONS x86_64 build succeeds; for kconfig, the generated
.config files for the def/allyes/allno/allmod/rand configs on seven
architectures are unchanged, and for genksyms, the symtypes/CRCs of 149
preprocessed kernel sources are unchanged.

lzhan011 (9):
  kconfig: fix NULL pointer dereference for defaults taken from choice
    members
  genksyms: fix infinite loop on declarations with parameter lists
  fixdep: fix out-of-bounds read on a comment ending with a backslash
  kallsyms: do not call qsort() with a NULL table
  modpost: fix stack out-of-bounds read for unterminated PNP ids
  modpost: fix handling of short reads in read_text_file()
  modpost: fix pointer arithmetic on NULL in parse_source_files()
  sorttable: avoid pointer arithmetic overflow when locating sort_needed
  tools/include: fix signed shift overflow in 32-bit unaligned accessors

 scripts/basic/fixdep.c             |  2 +-
 scripts/genksyms/parse.y           | 23 +++++++++++++++++++----
 scripts/kallsyms.c                 |  6 ++++--
 scripts/kconfig/symbol.c           |  4 ++--
 scripts/mod/file2alias.c           | 10 ++++++----
 scripts/mod/modpost.c              |  6 +++++-
 scripts/mod/sumversion.c           |  3 ++-
 scripts/sorttable.c                |  2 +-
 tools/include/tools/be_byteshift.h |  2 +-
 tools/include/tools/le_byteshift.h |  2 +-
 10 files changed, 42 insertions(+), 18 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/9] kconfig: fix NULL pointer dereference for defaults taken from choice members
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-08 16:04   ` Julian Braha
  2026-10-05 10:40 ` [PATCH 2/9] genksyms: fix infinite loop on declarations with parameter lists lzhan011
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

Since commit f79dc03fe68c ("kconfig: refactor choice value calculation"),
sym_calc_choice() marks the members of a choice SYMBOL_VALID but only
sets their curr.tri; curr.val is left NULL. If a string, int or hex
symbol takes its default from such a choice member, sym_calc_value() and
sym_get_string_default() copy ds->curr.val, so the symbol ends up with a
NULL string value. Depending on symbol order this crashes in conf_read()
(strcmp), sym_get_string_default() (str[0]) or ends up writing
CONFIG_X=(null).

Reproducer:

  choice
          prompt "choice"
  config C1
          bool "c1"
  config C2
          bool "c2"
  endchoice

  config FOO
          bool
          default C2

  config BAR
          string
          default C1

  $ touch .config
  $ KCONFIG_CONFIG=.config scripts/kconfig/conf --olddefconfig Kconfig
  Segmentation fault

Use sym_get_string_value() instead of reading curr.val directly. It
handles all symbol types and returns "y"/"m"/"n" for tristate and bool
symbols.

The resulting .config and savedefconfig output for defconfig,
allyesconfig, allnoconfig, allmodconfig and randconfig on x86_64, arm64,
riscv, powerpc, s390, arm and mips is unchanged.

Found by fuzzing Kconfig input with ASan/UBSan.

Fixes: f79dc03fe68c ("kconfig: refactor choice value calculation")
Assisted-by: Claude:claude-opus-5-5 ASan UBSan libFuzzer
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/kconfig/symbol.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index dcb4b45e6..de88b13f5 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -546,7 +546,7 @@ void sym_calc_value(struct symbol *sym)
 			if (ds) {
 				sym->flags |= SYMBOL_WRITE;
 				sym_calc_value(ds);
-				newval.val = ds->curr.val;
+				newval.val = (char *)sym_get_string_value(ds);
 			}
 		}
 		break;
@@ -887,7 +887,7 @@ const char *sym_get_string_default(struct symbol *sym)
 			ds = prop_get_symbol(prop);
 			if (ds != NULL) {
 				sym_calc_value(ds);
-				str = (const char *)ds->curr.val;
+				str = sym_get_string_value(ds);
 			}
 		}
 	}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 2/9] genksyms: fix infinite loop on declarations with parameter lists
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
  2026-10-05 10:40 ` [PATCH 1/9] kconfig: fix NULL pointer dereference for defaults taken from choice members lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-05 10:40 ` [PATCH 3/9] fixdep: fix out-of-bounds read on a comment ending with a backslash lzhan011
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

decl_specifier_seq updates the global decl_spec, which is used by the ','
action of init_declarator_list to give each further declarator the
specifiers of the declaration. However, decl_specifier_seq is also used
for parameter declarations, so for a declaration such as

  int a, f(int);

decl_spec is clobbered by the specifiers of the parameter "int" by the
time the ',' action runs. The action then links the parameter's own token
node back into its chain, creating a cycle, and copy_list_range() loops
forever while allocating memory:

  $ printf 'int a, f(int);\n' | scripts/genksyms/genksyms
  (hangs)

Before commit 45c9c4101d3d ("genksyms: fix memory leak when the same
symbol is added from source") the same corruption did not hang but
silently recorded a wrong type for subsequent declarators (e.g. for
"int f(long), a;" the type of "a" was recorded as "int f ( long a").

Only set decl_spec in decl_specifier_seq_opt, which is used at the
declaration level, and let decl_specifier_seq just return the last
specifier. Struct and union member declarations use a new
member_decl_specifier_seq_opt that does not touch decl_spec, so a
struct body nested in a parameter list cannot clobber it either.

The generated symtypes and CRCs for 149 preprocessed kernel source files
(1128 exported symbols) are unchanged, and no new bison conflicts are
introduced.

Found by fuzzing genksyms with ASan/UBSan.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by: Claude:claude-opus-5-5 ASan UBSan
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/genksyms/parse.y | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y
index cabcd146f..7cc0336a7 100644
--- a/scripts/genksyms/parse.y
+++ b/scripts/genksyms/parse.y
@@ -201,13 +201,28 @@ init_declarator:
 /* Hang on to the specifiers so that we can reuse them.  */
 decl_specifier_seq_opt:
 	/* empty */				{ decl_spec = NULL; }
+	| decl_specifier_seq			{ decl_spec = *$1; }
+	;
+
+/*
+ * Unlike decl_specifier_seq_opt, this does not touch decl_spec, so that
+ * a struct/union body nested in a parameter list does not clobber the
+ * specifiers of the enclosing declaration.
+ */
+member_decl_specifier_seq_opt:
+	/* empty */				{ $$ = NULL; }
 	| decl_specifier_seq
 	;
 
+/*
+ * Do not set decl_spec here; decl_specifier_seq is also used for parameter
+ * declarations, which would clobber the specifiers of the enclosing
+ * declaration, e.g. "int a, f(long);".
+ */
 decl_specifier_seq:
-	attribute_opt decl_specifier		{ decl_spec = *$2; }
-	| decl_specifier_seq decl_specifier	{ decl_spec = *$2; }
-	| decl_specifier_seq ATTRIBUTE_PHRASE	{ decl_spec = *$2; }
+	attribute_opt decl_specifier		{ $$ = $2; }
+	| decl_specifier_seq decl_specifier	{ $$ = $2; }
+	| decl_specifier_seq ATTRIBUTE_PHRASE	{ $$ = $2; }
 	;
 
 decl_specifier:
@@ -456,7 +471,7 @@ member_specification:
 	;
 
 member_declaration:
-	decl_specifier_seq_opt member_declarator_list_opt ';'
+	member_decl_specifier_seq_opt member_declarator_list_opt ';'
 		{ $$ = $3; dont_want_type_specifier = false; }
 	| error ';'
 		{ $$ = $2; dont_want_type_specifier = false; }
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 3/9] fixdep: fix out-of-bounds read on a comment ending with a backslash
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
  2026-10-05 10:40 ` [PATCH 1/9] kconfig: fix NULL pointer dereference for defaults taken from choice members lzhan011
  2026-10-05 10:40 ` [PATCH 2/9] genksyms: fix infinite loop on declarations with parameter lists lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-05 10:40 ` [PATCH 4/9] kallsyms: do not call qsort() with a NULL table lzhan011
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

When skipping a comment, parse_dep_file() treats a backslash as escaping
the next character and skips it unconditionally. If the backslash is the
last character of the file, this steps over the terminating NUL and the
loop keeps reading beyond the end of the buffer:

  $ printf 'foo.o: foo.c\n# x\\' > foo.d
  $ touch foo.c
  $ scripts/basic/fixdep foo.d foo.o cc
  AddressSanitizer: heap-buffer-overflow ... in parse_dep_file

Only skip the character after the backslash if it is not the terminating
NUL.

Found by fuzzing fixdep with ASan/UBSan.

Fixes: bc6df812a152 ("fixdep: parse Makefile more correctly to handle comments etc.")
Assisted-by: Claude:claude-opus-5-5 ASan UBSan
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/basic/fixdep.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 54063d980..9b57fe555 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -280,7 +280,7 @@ static void parse_dep_file(char *p, const char *target)
 				 * escaped newlines continue the comment across
 				 * multiple lines.
 				 */
-				if (*p == '\\')
+				if (*p == '\\' && *(p + 1) != '\0')
 					p++;
 				p++;
 			}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 4/9] kallsyms: do not call qsort() with a NULL table
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
                   ` (2 preceding siblings ...)
  2026-10-05 10:40 ` [PATCH 3/9] fixdep: fix out-of-bounds read on a comment ending with a backslash lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-05 10:40 ` [PATCH 5/9] modpost: fix stack out-of-bounds read for unterminated PNP ids lzhan011
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

link-vmlinux.sh runs kallsyms on an empty input for the first link, in
which case table is NULL and table_cnt is 0. Passing NULL to qsort() is
undefined behaviour even for zero elements, and UBSan reports:

  scripts/kallsyms.c: runtime error: null pointer passed as argument 1,
  which is declared to never be null

Skip the sort when there are no symbols.

Fixes: c442db3f49f2 ("kbuild: remove PROVIDE() for kallsyms symbols")
Assisted-by: Claude:claude-opus-5-5 UBSan
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/kallsyms.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index bd9e9ce20..9300e10ad 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -390,7 +390,8 @@ static int compare_names(const void *a, const void *b)
 
 static void sort_symbols_by_name(void)
 {
-	qsort(table, table_cnt, sizeof(table[0]), compare_names);
+	if (table_cnt)
+		qsort(table, table_cnt, sizeof(table[0]), compare_names);
 }
 
 static void write_src(FILE *out_bin_file, const char *out_bin_name)
@@ -822,7 +823,8 @@ static int compare_symbols(const void *a, const void *b)
 
 static void sort_symbols(void)
 {
-	qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
+	if (table_cnt)
+		qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
 }
 
 int main(int argc, char **argv)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 5/9] modpost: fix stack out-of-bounds read for unterminated PNP ids
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
                   ` (3 preceding siblings ...)
  2026-10-05 10:40 ` [PATCH 4/9] kallsyms: do not call qsort() with a NULL table lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-05 10:40 ` [PATCH 6/9] modpost: fix handling of short reads in read_text_file() lzhan011
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

do_pnp_device_entry() and do_pnp_card_entry() copy the PNP id into a
local acpi_id[PNP_ID_LEN] buffer to upper-case it, and then print it with
"%s". A PNP id that uses all PNP_ID_LEN characters, e.g. "PNP0C0F1", is
accepted by the compiler without a terminating NUL (the kernel is built
with -Wno-unterminated-string-initialization), so the local copy is not
NUL-terminated and printing it reads past the end of the stack buffer:

  AddressSanitizer: stack-buffer-overflow ... in printf_common
    ... do_pnp_device_entry scripts/mod/file2alias.c

Limit the printed length to the size of the id buffers.

Found by fuzzing modpost with ASan/UBSan.

Fixes: 72638f598ec9 ("PNP: fix broken pnp lowercasing for acpi module aliases")
Assisted-by: Claude:claude-opus-5-5 ASan UBSan
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/mod/file2alias.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 61a831c18..7685fb513 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -573,8 +573,9 @@ static void do_pnp_device_entry(struct module *mod, void *symval)
 	/* fix broken pnp bus lowercasing */
 	for (unsigned int i = 0; i < sizeof(acpi_id); i++)
 		acpi_id[i] = toupper((*id)[i]);
-	module_alias_printf(mod, false, "pnp:d%s*", *id);
-	module_alias_printf(mod, false, "acpi*:%s:*", acpi_id);
+	module_alias_printf(mod, false, "pnp:d%.*s*", (int)sizeof(*id), *id);
+	module_alias_printf(mod, false, "acpi*:%.*s:*",
+			    (int)sizeof(acpi_id), acpi_id);
 }
 
 /* looks like: "pnp:dD" for every device of the card */
@@ -594,8 +595,9 @@ static void do_pnp_card_entry(struct module *mod, void *symval)
 			acpi_id[j] = toupper(id[j]);
 
 		/* add an individual alias for every device entry */
-		module_alias_printf(mod, false, "pnp:d%s*", id);
-		module_alias_printf(mod, false, "acpi*:%s:*", acpi_id);
+		module_alias_printf(mod, false, "pnp:d%.*s*", PNP_ID_LEN, id);
+		module_alias_printf(mod, false, "acpi*:%.*s:*",
+				    PNP_ID_LEN, acpi_id);
 	}
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 6/9] modpost: fix handling of short reads in read_text_file()
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
                   ` (4 preceding siblings ...)
  2026-10-05 10:40 ` [PATCH 5/9] modpost: fix stack out-of-bounds read for unterminated PNP ids lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-05 10:40 ` [PATCH 7/9] modpost: fix pointer arithmetic on NULL in parse_source_files() lzhan011
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

read_text_file() loops until the whole file has been read, but every
read() call writes to the start of the buffer, so after a short read the
data already read is overwritten and the end of the buffer is left
uninitialized. If read() returns 0 before the expected size has been
read, the loop never terminates.

Read into the correct offset of the buffer, and treat an unexpected end
of file as an error.

This was confirmed by limiting read() to 64 bytes per call with an
LD_PRELOAD shim, which makes modpost fail with a parse error on a valid
Module.symvers.

Fixes: ac5100f54329 ("modpost: add read_text_file() and get_line() helpers")
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/mod/modpost.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 75374c64b..1772068dc 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -143,11 +143,15 @@ char *read_text_file(const char *filename)
 	while (nbytes) {
 		ssize_t bytes_read;
 
-		bytes_read = read(fd, buf, nbytes);
+		bytes_read = read(fd, buf + st.st_size - nbytes, nbytes);
 		if (bytes_read < 0) {
 			perror(filename);
 			exit(1);
 		}
+		if (bytes_read == 0) {
+			fprintf(stderr, "%s: unexpected end of file\n", filename);
+			exit(1);
+		}
 
 		nbytes -= bytes_read;
 	}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 7/9] modpost: fix pointer arithmetic on NULL in parse_source_files()
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
                   ` (5 preceding siblings ...)
  2026-10-05 10:40 ` [PATCH 6/9] modpost: fix handling of short reads in read_text_file() lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-05 10:40 ` [PATCH 8/9] sorttable: avoid pointer arithmetic overflow when locating sort_needed lzhan011
  2026-10-05 10:40 ` [PATCH 9/9] tools/include: fix signed shift overflow in 32-bit unaligned accessors lzhan011
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

parse_source_files() checks whether a dependency is in the same directory
as the object file with

  (strstr(line, dir) + strlen(dir) - 1) == strrchr(line, '/')

When the dependency is not below dir, strstr() returns NULL and the
pointer arithmetic on NULL is undefined behaviour. This happens for
every module with dependencies outside its own directory, and UBSan
reports:

  scripts/mod/sumversion.c: runtime error: applying non-zero offset
  to null pointer

Check the result of strstr() before using it.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by: Claude:claude-opus-5-5 UBSan
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/mod/sumversion.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
index 3dd28b4d0..4c59e7cfb 100644
--- a/scripts/mod/sumversion.c
+++ b/scripts/mod/sumversion.c
@@ -364,7 +364,8 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
 		}
 
 		/* Check if this file is in same dir as objfile */
-		if ((strstr(line, dir)+strlen(dir)-1) == strrchr(line, '/')) {
+		p = strstr(line, dir);
+		if (p && p + dirlen - 1 == strrchr(line, '/')) {
 			if (!parse_file(line, md)) {
 				warn("could not open %s: %s\n",
 				     line, strerror(errno));
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 8/9] sorttable: avoid pointer arithmetic overflow when locating sort_needed
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
                   ` (6 preceding siblings ...)
  2026-10-05 10:40 ` [PATCH 7/9] modpost: fix pointer arithmetic on NULL in parse_source_files() lzhan011
@ 2026-10-05 10:40 ` lzhan011
  2026-10-05 10:40 ` [PATCH 9/9] tools/include: fix signed shift overflow in 32-bit unaligned accessors lzhan011
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

The location of the main_extable_sort_needed variable is computed as

  (void *)ehdr + shdr_offset(sec) + sym_value(sym) - shdr_addr(sec)

which first adds the symbol's virtual address (e.g. 0xffffffff8...) to
the pointer and only then subtracts the section address. The
intermediate pointer overflows, which is undefined behaviour and is
reported by UBSan.

Subtract the section address from the symbol value first.

Fixes: a79f248b9b30 ("scripts: Add sortextable to sort the kernel's exception table.")
Assisted-by: Claude:claude-opus-5-5 UBSan
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 scripts/sorttable.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/sorttable.c b/scripts/sorttable.c
index 88e49a5c4..0425e00c4 100644
--- a/scripts/sorttable.c
+++ b/scripts/sorttable.c
@@ -788,7 +788,7 @@ static int do_sort(Elf_Ehdr *ehdr,
 	sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index);
 	sort_needed_loc = (void *)ehdr +
 		shdr_offset(sort_needed_sec) +
-		sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec);
+		(sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec));
 
 	/* extable has been sorted, clear the flag */
 	elf_parser.w(0, sort_needed_loc);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 9/9] tools/include: fix signed shift overflow in 32-bit unaligned accessors
  2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
                   ` (7 preceding siblings ...)
  2026-10-05 10:40 ` [PATCH 8/9] sorttable: avoid pointer arithmetic overflow when locating sort_needed lzhan011
@ 2026-10-05 10:40 ` lzhan011
  8 siblings, 0 replies; 11+ messages in thread
From: lzhan011 @ 2026-10-05 10:40 UTC (permalink / raw)
  To: nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

From: lzhan011 <zhangleizhen645@gmail.com>

In __get_unaligned_le32() and __get_unaligned_be32() the most significant
byte is promoted to int before being shifted left by 24. If the byte is
0x80 or higher, the result does not fit in an int, which is undefined
behaviour. This happens for every kernel virtual address, e.g. in
sorttable, and UBSan reports:

  tools/include/tools/le_byteshift.h:14: runtime error: left shift of
  255 by 24 places cannot be represented in type 'int'

Cast the byte to uint32_t before shifting.

Fixes: a07f7672d7cf ("tools/include: Add byteshift headers for endian access")
Assisted-by: Claude:claude-opus-5-5 UBSan
Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>
---
 tools/include/tools/be_byteshift.h | 2 +-
 tools/include/tools/le_byteshift.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/include/tools/be_byteshift.h b/tools/include/tools/be_byteshift.h
index f7d1d1698..2bda8d199 100644
--- a/tools/include/tools/be_byteshift.h
+++ b/tools/include/tools/be_byteshift.h
@@ -11,7 +11,7 @@ static inline uint16_t __get_unaligned_be16(const uint8_t *p)
 
 static inline uint32_t __get_unaligned_be32(const uint8_t *p)
 {
-	return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+	return (uint32_t)p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
 }
 
 static inline uint64_t __get_unaligned_be64(const uint8_t *p)
diff --git a/tools/include/tools/le_byteshift.h b/tools/include/tools/le_byteshift.h
index dc8565f39..e5c78a48a 100644
--- a/tools/include/tools/le_byteshift.h
+++ b/tools/include/tools/le_byteshift.h
@@ -11,7 +11,7 @@ static inline uint16_t __get_unaligned_le16(const uint8_t *p)
 
 static inline uint32_t __get_unaligned_le32(const uint8_t *p)
 {
-	return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+	return p[0] | p[1] << 8 | p[2] << 16 | (uint32_t)p[3] << 24;
 }
 
 static inline uint64_t __get_unaligned_le64(const uint8_t *p)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/9] kconfig: fix NULL pointer dereference for defaults taken from choice members
  2026-10-05 10:40 ` [PATCH 1/9] kconfig: fix NULL pointer dereference for defaults taken from choice members lzhan011
@ 2026-10-08 16:04   ` Julian Braha
  0 siblings, 0 replies; 11+ messages in thread
From: Julian Braha @ 2026-10-08 16:04 UTC (permalink / raw)
  To: lzhan011, nathan, nsc; +Cc: rostedt, linux-kbuild, linux-kernel

Hi anonymous,

It looks this is your first time contributing to the linux kernel.
Welcome!

First, make sure that you run ./scripts/get_maintainer.pl on your
patches. There should be a few more people to CC on these emails,
like me.

On 10/5/26 11:40, lzhan011 wrote:
> From: lzhan011 <zhangleizhen645@gmail.com>
> 
> Since commit f79dc03fe68c ("kconfig: refactor choice value calculation"),
> sym_calc_choice() marks the members of a choice SYMBOL_VALID but only
> sets their curr.tri; curr.val is left NULL. If a string, int or hex
> symbol takes its default from such a choice member, sym_calc_value() and
> sym_get_string_default() copy ds->curr.val, so the symbol ends up with a
> NULL string value. Depending on symbol order this crashes in conf_read()
> (strcmp), sym_get_string_default() (str[0]) or ends up writing
> CONFIG_X=(null).

Thanks for reporting this issue. But after careful consideration, I
think we'd like to resolve this issue a different way. In a v2 of this
series, you can simply drop this patch, and I will handle it.

> 
> Reproducer:
> 
>   choice
>           prompt "choice"
>   config C1
>           bool "c1"
>   config C2
>           bool "c2"
>   endchoice
> 
>   config FOO
>           bool
>           default C2
> 
>   config BAR
>           string
>           default C1
> 
>   $ touch .config
>   $ KCONFIG_CONFIG=.config scripts/kconfig/conf --olddefconfig Kconfig
>   Segmentation fault
> 
> Use sym_get_string_value() instead of reading curr.val directly. It
> handles all symbol types and returns "y"/"m"/"n" for tristate and bool
> symbols.
> 
> The resulting .config and savedefconfig output for defconfig,
> allyesconfig, allnoconfig, allmodconfig and randconfig on x86_64, arm64,
> riscv, powerpc, s390, arm and mips is unchanged.
> 
> Found by fuzzing Kconfig input with ASan/UBSan.
> 
> Fixes: f79dc03fe68c ("kconfig: refactor choice value calculation")
> Assisted-by: Claude:claude-opus-5-5 ASan UBSan libFuzzer
> Signed-off-by: lzhan011 <zhangleizhen645@gmail.com>

Please make sure to use your real name when signing off on your commits,
see also:
https://www.kernel.org/doc/html/latest/process/1.Intro.html

> ---
>  scripts/kconfig/symbol.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
> index dcb4b45e6..de88b13f5 100644
> --- a/scripts/kconfig/symbol.c
> +++ b/scripts/kconfig/symbol.c
> @@ -546,7 +546,7 @@ void sym_calc_value(struct symbol *sym)
>  			if (ds) {
>  				sym->flags |= SYMBOL_WRITE;
>  				sym_calc_value(ds);
> -				newval.val = ds->curr.val;
> +				newval.val = (char *)sym_get_string_value(ds);
>  			}
>  		}
>  		break;
> @@ -887,7 +887,7 @@ const char *sym_get_string_default(struct symbol *sym)
>  			ds = prop_get_symbol(prop);
>  			if (ds != NULL) {
>  				sym_calc_value(ds);
> -				str = (const char *)ds->curr.val;
> +				str = sym_get_string_value(ds);
>  			}
>  		}
>  	}
> -- 2.34.1
> 

- Julian Braha

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-10-08 16:04 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-05 10:40 [PATCH 0/9] kbuild: fix memory safety and UB bugs in host tools found by fuzzing lzhan011
2026-10-05 10:40 ` [PATCH 1/9] kconfig: fix NULL pointer dereference for defaults taken from choice members lzhan011
2026-10-08 16:04   ` Julian Braha
2026-10-05 10:40 ` [PATCH 2/9] genksyms: fix infinite loop on declarations with parameter lists lzhan011
2026-10-05 10:40 ` [PATCH 3/9] fixdep: fix out-of-bounds read on a comment ending with a backslash lzhan011
2026-10-05 10:40 ` [PATCH 4/9] kallsyms: do not call qsort() with a NULL table lzhan011
2026-10-05 10:40 ` [PATCH 5/9] modpost: fix stack out-of-bounds read for unterminated PNP ids lzhan011
2026-10-05 10:40 ` [PATCH 6/9] modpost: fix handling of short reads in read_text_file() lzhan011
2026-10-05 10:40 ` [PATCH 7/9] modpost: fix pointer arithmetic on NULL in parse_source_files() lzhan011
2026-10-05 10:40 ` [PATCH 8/9] sorttable: avoid pointer arithmetic overflow when locating sort_needed lzhan011
2026-10-05 10:40 ` [PATCH 9/9] tools/include: fix signed shift overflow in 32-bit unaligned accessors lzhan011

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®