]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
objtool/klp: Fix pointer comparisons for rodata objects
authorJosh Poimboeuf <jpoimboe@kernel.org>
Tue, 28 Apr 2026 21:34:23 +0000 (14:34 -0700)
committerJosh Poimboeuf <jpoimboe@kernel.org>
Tue, 5 May 2026 04:16:02 +0000 (21:16 -0700)
klp-diff treats all rodata as uncorrelated, so any reference to it uses
a duplicated copy rather than using a KLP reloc.

For the contents of the data itself, a duplicated copy is fine.
However, pointer comparisons (e.g., f->f_op == &foo_ops) are broken.

Fix it by correlating non-anonymous rodata objects.

Also, use a new find_symbol_containing_inclusive() helper for matching
the end of a symbol so bounds calculations don't get broken, for the
case where an array or other symbol's ending address is used as part of
a bounds calculation.

While these are really two distinct changes, they need to be done in the
same patch so as to avoid introducing bisection regressions.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
tools/objtool/elf.c
tools/objtool/include/objtool/elf.h
tools/objtool/klp-diff.c

index 87c6e00749c60183565018d367eacf960a12ed12..5a20dab683dd2a162b4387cb4264b476d377d13c 100644 (file)
@@ -207,6 +207,20 @@ struct symbol *find_symbol_containing(const struct section *sec, unsigned long o
        return sym ? sym->alias : NULL;
 }
 
+/*
+ * Also match the symbol end address which can be used for a bounds comparison.
+ */
+struct symbol *find_symbol_containing_inclusive(const struct section *sec,
+                                               unsigned long offset)
+{
+       struct symbol *sym = find_symbol_containing(sec, offset);
+
+       if (!sym && offset)
+               sym = find_symbol_containing(sec, offset - 1);
+
+       return sym;
+}
+
 /*
  * Returns size of hole starting at @offset.
  */
index ab5f7017ec34310be4c3be96f7b85e35d244c85e..8a543cea43b90cf650a5494975d2a1bcf86d7ea6 100644 (file)
@@ -211,6 +211,7 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
 struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
 struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name);
 struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
+struct symbol *find_symbol_containing_inclusive(const struct section *sec, unsigned long offset);
 int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
 struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
 struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
index 78633c9b68eb1c9a630a15d7bc95fcbe32414233..bf37c652188bd7586c91152bf4762d69f9a0c305 100644 (file)
@@ -386,6 +386,7 @@ static bool dont_correlate(struct symbol *sym)
               is_uncorrelated_static_local(sym) ||
               is_local_label(sym) ||
               is_string_sec(sym->sec) ||
+              (is_rodata_sec(sym->sec) && !is_object_sym(sym)) ||
               is_initcall_sym(sym) ||
               is_addressable_sym(sym) ||
               is_special_section(sym->sec) ||
@@ -979,7 +980,7 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
                goto found_sym;
 
        /* No dedicated section; find the symbol manually */
-       sym = find_symbol_containing(sec, arch_adjusted_addend(reloc));
+       sym = find_symbol_containing_inclusive(sec, arch_adjusted_addend(reloc));
        if (!sym) {
                /*
                 * This is presumably an .altinstr_replacement section which is
@@ -988,6 +989,17 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
                if (!sec_size(sec))
                        return 1;
 
+               /*
+                * .rodata is a mixed bag of named objects and anonymous data.
+                *
+                * Convert section symbol references to named object symbols
+                * when possible, to preserve pointer identity for const
+                * structs like file_operations.  Otherwise a section symbol is
+                * fine.
+                */
+               if (is_rodata_sec(sec))
+                       return 0;
+
                /*
                 * This can happen for special section references to weak code
                 * whose symbol has been stripped by the linker.
@@ -1009,7 +1021,6 @@ found_sym:
 static bool is_uncorrelated_section(struct section *sec)
 {
        return is_string_sec(sec) ||
-              strstarts(sec->name, ".rodata") ||
               strstarts(sec->name, ".data..Lubsan") ||         /* GCC */
               strstarts(sec->name, ".data..L__unnamed_");      /* Clang */
 }