Add the RHEL 211.21.1..211.22.1 backports (1288-1351) from centos-stream-10 and upstream stable, on top of 211.20.1. Bump pkgrelease and specrelease to 211.22.1. (The redhat/ automotive rebuild-changelog tooling change is omitted: it patches redhat/scripts not present in this build base and does not affect the kernel.)
317 lines
9.8 KiB
Diff
317 lines
9.8 KiB
Diff
From 8cde64d5091653560e13e7cd34a385258372db58 Mon Sep 17 00:00:00 2001
|
|
From: Jerome Marchand <jmarchan@redhat.com>
|
|
Date: Mon, 1 Jun 2026 15:57:38 +0200
|
|
Subject: [PATCH] scripts/sorttable: Zero out weak functions in mcount_loc
|
|
table
|
|
|
|
JIRA: https://redhat.atlassian.net/browse/RHEL-180193
|
|
|
|
commit ef378c3b8233855497a414b9d67bf22592c928a4
|
|
Author: Steven Rostedt <rostedt@goodmis.org>
|
|
Date: Tue Feb 18 14:59:22 2025 -0500
|
|
|
|
scripts/sorttable: Zero out weak functions in mcount_loc table
|
|
|
|
When a function is annotated as "weak" and is overridden, the code is not
|
|
removed. If it is traced, the fentry/mcount location in the weak function
|
|
will be referenced by the "__mcount_loc" section. This will then be added
|
|
to the available_filter_functions list. Since only the address of the
|
|
functions are listed, to find the name to show, a search of kallsyms is
|
|
used.
|
|
|
|
Since kallsyms will return the function by simply finding the function
|
|
that the address is after but before the next function, an address of a
|
|
weak function will show up as the function before it. This is because
|
|
kallsyms does not save names of weak functions. This has caused issues in
|
|
the past, as now the traced weak function will be listed in
|
|
available_filter_functions with the name of the function before it.
|
|
|
|
At best, this will cause the previous function's name to be listed twice.
|
|
At worse, if the previous function was marked notrace, it will now show up
|
|
as a function that can be traced. Note that it only shows up that it can
|
|
be traced but will not be if enabled, which causes confusion.
|
|
|
|
https://lore.kernel.org/all/20220412094923.0abe90955e5db486b7bca279@kernel.org/
|
|
|
|
The commit b39181f7c6907 ("ftrace: Add FTRACE_MCOUNT_MAX_OFFSET to avoid
|
|
adding weak function") was a workaround to this by checking the function
|
|
address before printing its name. If the address was too far from the
|
|
function given by the name then instead of printing the name it would
|
|
print: __ftrace_invalid_address___<invalid-offset>
|
|
|
|
The real issue is that these invalid addresses are listed in the ftrace
|
|
table look up which available_filter_functions is derived from. A place
|
|
holder must be listed in that file because set_ftrace_filter may take a
|
|
series of indexes into that file instead of names to be able to do O(1)
|
|
lookups to enable filtering (many tools use this method).
|
|
|
|
Even if kallsyms saved the size of the function, it does not remove the
|
|
need of having these place holders. The real solution is to not add a weak
|
|
function into the ftrace table in the first place.
|
|
|
|
To solve this, the sorttable.c code that sorts the mcount regions during
|
|
the build is modified to take a "nm -S vmlinux" input, sort it, and any
|
|
function listed in the mcount_loc section that is not within a boundary of
|
|
the function list given by nm is considered a weak function and is zeroed
|
|
out.
|
|
|
|
Note, this does not mean they will remain zero when booting as KASLR
|
|
will still shift those addresses. To handle this, the entries in the
|
|
mcount_loc section will be ignored if they are zero or match the
|
|
kaslr_offset() value.
|
|
|
|
Before:
|
|
|
|
~# grep __ftrace_invalid_address___ /sys/kernel/tracing/available_filter_functions | wc -l
|
|
551
|
|
|
|
After:
|
|
|
|
~# grep __ftrace_invalid_address___ /sys/kernel/tracing/available_filter_functions | wc -l
|
|
0
|
|
|
|
Cc: bpf <bpf@vger.kernel.org>
|
|
Cc: Masami Hiramatsu <mhiramat@kernel.org>
|
|
Cc: Mark Rutland <mark.rutland@arm.com>
|
|
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
|
|
Cc: Andrew Morton <akpm@linux-foundation.org>
|
|
Cc: Peter Zijlstra <peterz@infradead.org>
|
|
Cc: Linus Torvalds <torvalds@linux-foundation.org>
|
|
Cc: Masahiro Yamada <masahiroy@kernel.org>
|
|
Cc: Nathan Chancellor <nathan@kernel.org>
|
|
Cc: Nicolas Schier <nicolas@fjasle.eu>
|
|
Cc: Zheng Yejian <zhengyejian1@huawei.com>
|
|
Cc: Martin Kelly <martin.kelly@crowdstrike.com>
|
|
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
|
|
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
Cc: Heiko Carstens <hca@linux.ibm.com>
|
|
Cc: Catalin Marinas <catalin.marinas@arm.com>
|
|
Cc: Will Deacon <will@kernel.org>
|
|
Cc: Vasily Gorbik <gor@linux.ibm.com>
|
|
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
|
|
Link: https://lore.kernel.org/20250218200022.883095980@goodmis.org
|
|
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
|
|
|
|
Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
|
|
|
|
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
|
|
index 57b8fad660cf..792c57d1f0b9 100644
|
|
--- a/kernel/trace/ftrace.c
|
|
+++ b/kernel/trace/ftrace.c
|
|
@@ -7052,6 +7052,7 @@ static int ftrace_process_locs(struct module *mod,
|
|
unsigned long count;
|
|
unsigned long *p;
|
|
unsigned long addr;
|
|
+ unsigned long kaslr;
|
|
unsigned long flags = 0; /* Shut up gcc */
|
|
int ret = -ENOMEM;
|
|
|
|
@@ -7100,6 +7101,9 @@ static int ftrace_process_locs(struct module *mod,
|
|
ftrace_pages->next = start_pg;
|
|
}
|
|
|
|
+ /* For zeroed locations that were shifted for core kernel */
|
|
+ kaslr = !mod ? kaslr_offset() : 0;
|
|
+
|
|
p = start;
|
|
pg = start_pg;
|
|
while (p < end) {
|
|
@@ -7111,7 +7115,7 @@ static int ftrace_process_locs(struct module *mod,
|
|
* object files to satisfy alignments.
|
|
* Skip any NULL pointers.
|
|
*/
|
|
- if (!addr) {
|
|
+ if (!addr || addr == kaslr) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
|
|
index cdf16029fc58..8619ae612997 100755
|
|
--- a/scripts/link-vmlinux.sh
|
|
+++ b/scripts/link-vmlinux.sh
|
|
@@ -174,12 +174,14 @@ mksysmap()
|
|
|
|
sorttable()
|
|
{
|
|
- ${objtree}/scripts/sorttable ${1}
|
|
+ ${NM} -S ${1} > .tmp_vmlinux.nm-sort
|
|
+ ${objtree}/scripts/sorttable -s .tmp_vmlinux.nm-sort ${1}
|
|
}
|
|
|
|
cleanup()
|
|
{
|
|
rm -f .btf.*
|
|
+ rm -f .tmp_vmlinux.nm-sort
|
|
rm -f System.map
|
|
rm -f vmlinux
|
|
rm -f vmlinux.map
|
|
diff --git a/scripts/sorttable.c b/scripts/sorttable.c
|
|
index ec02a2852efb..23c7e0e6c024 100644
|
|
--- a/scripts/sorttable.c
|
|
+++ b/scripts/sorttable.c
|
|
@@ -580,6 +580,98 @@ static void rela_write_addend(Elf_Rela *rela, uint64_t val)
|
|
e.rela_write_addend(rela, val);
|
|
}
|
|
|
|
+struct func_info {
|
|
+ uint64_t addr;
|
|
+ uint64_t size;
|
|
+};
|
|
+
|
|
+/* List of functions created by: nm -S vmlinux */
|
|
+static struct func_info *function_list;
|
|
+static int function_list_size;
|
|
+
|
|
+/* Allocate functions in 1k blocks */
|
|
+#define FUNC_BLK_SIZE 1024
|
|
+#define FUNC_BLK_MASK (FUNC_BLK_SIZE - 1)
|
|
+
|
|
+static int add_field(uint64_t addr, uint64_t size)
|
|
+{
|
|
+ struct func_info *fi;
|
|
+ int fsize = function_list_size;
|
|
+
|
|
+ if (!(fsize & FUNC_BLK_MASK)) {
|
|
+ fsize += FUNC_BLK_SIZE;
|
|
+ fi = realloc(function_list, fsize * sizeof(struct func_info));
|
|
+ if (!fi)
|
|
+ return -1;
|
|
+ function_list = fi;
|
|
+ }
|
|
+ fi = &function_list[function_list_size++];
|
|
+ fi->addr = addr;
|
|
+ fi->size = size;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Only return match if the address lies inside the function size */
|
|
+static int cmp_func_addr(const void *K, const void *A)
|
|
+{
|
|
+ uint64_t key = *(const uint64_t *)K;
|
|
+ const struct func_info *a = A;
|
|
+
|
|
+ if (key < a->addr)
|
|
+ return -1;
|
|
+ return key >= a->addr + a->size;
|
|
+}
|
|
+
|
|
+/* Find the function in function list that is bounded by the function size */
|
|
+static int find_func(uint64_t key)
|
|
+{
|
|
+ return bsearch(&key, function_list, function_list_size,
|
|
+ sizeof(struct func_info), cmp_func_addr) != NULL;
|
|
+}
|
|
+
|
|
+static int cmp_funcs(const void *A, const void *B)
|
|
+{
|
|
+ const struct func_info *a = A;
|
|
+ const struct func_info *b = B;
|
|
+
|
|
+ if (a->addr < b->addr)
|
|
+ return -1;
|
|
+ return a->addr > b->addr;
|
|
+}
|
|
+
|
|
+static int parse_symbols(const char *fname)
|
|
+{
|
|
+ FILE *fp;
|
|
+ char addr_str[20]; /* Only need 17, but round up to next int size */
|
|
+ char size_str[20];
|
|
+ char type;
|
|
+
|
|
+ fp = fopen(fname, "r");
|
|
+ if (!fp) {
|
|
+ perror(fname);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ while (fscanf(fp, "%16s %16s %c %*s\n", addr_str, size_str, &type) == 3) {
|
|
+ uint64_t addr;
|
|
+ uint64_t size;
|
|
+
|
|
+ /* Only care about functions */
|
|
+ if (type != 't' && type != 'T' && type != 'W')
|
|
+ continue;
|
|
+
|
|
+ addr = strtoull(addr_str, NULL, 16);
|
|
+ size = strtoull(size_str, NULL, 16);
|
|
+ if (add_field(addr, size) < 0)
|
|
+ return -1;
|
|
+ }
|
|
+ fclose(fp);
|
|
+
|
|
+ qsort(function_list, function_list_size, sizeof(struct func_info), cmp_funcs);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static pthread_t mcount_sort_thread;
|
|
static bool sort_reloc;
|
|
|
|
@@ -752,6 +844,21 @@ static void *sort_mcount_loc(void *arg)
|
|
goto out;
|
|
}
|
|
|
|
+ /* zero out any locations not found by function list */
|
|
+ if (function_list_size) {
|
|
+ for (void *ptr = vals; ptr < vals + size; ptr += long_size) {
|
|
+ uint64_t key;
|
|
+
|
|
+ key = long_size == 4 ? r((uint32_t *)ptr) : r8((uint64_t *)ptr);
|
|
+ if (!find_func(key)) {
|
|
+ if (long_size == 4)
|
|
+ *(uint32_t *)ptr = 0;
|
|
+ else
|
|
+ *(uint64_t *)ptr = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
compare_values = long_size == 4 ? compare_values_32 : compare_values_64;
|
|
|
|
qsort(vals, count, long_size, compare_values);
|
|
@@ -801,6 +908,8 @@ static void get_mcount_loc(struct elf_mcount_loc *emloc, Elf_Shdr *symtab_sec,
|
|
return;
|
|
}
|
|
}
|
|
+#else /* MCOUNT_SORT_ENABLED */
|
|
+static inline int parse_symbols(const char *fname) { return 0; }
|
|
#endif
|
|
|
|
static int do_sort(Elf_Ehdr *ehdr,
|
|
@@ -1256,14 +1365,29 @@ int main(int argc, char *argv[])
|
|
int i, n_error = 0; /* gcc-4.3.0 false positive complaint */
|
|
size_t size = 0;
|
|
void *addr = NULL;
|
|
+ int c;
|
|
+
|
|
+ while ((c = getopt(argc, argv, "s:")) >= 0) {
|
|
+ switch (c) {
|
|
+ case 's':
|
|
+ if (parse_symbols(optarg) < 0) {
|
|
+ fprintf(stderr, "Could not parse %s\n", optarg);
|
|
+ return -1;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ fprintf(stderr, "usage: sorttable [-s nm-file] vmlinux...\n");
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
|
|
- if (argc < 2) {
|
|
+ if ((argc - optind) < 1) {
|
|
fprintf(stderr, "usage: sorttable vmlinux...\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Process each file in turn, allowing deep failure. */
|
|
- for (i = 1; i < argc; i++) {
|
|
+ for (i = optind; i < argc; i++) {
|
|
addr = mmap_file(argv[i], &size);
|
|
if (!addr) {
|
|
++n_error;
|
|
--
|
|
2.50.1 (Apple Git-155)
|
|
|