When every mcount_loc entry of a module is skipped,
ftrace_process_locs() leaves the module's first page group linked
with no records. ftrace_release_mod() matches a module's groups by
records[0].ip, which is 0 here, so the group is never freed. While
it stays linked, ftrace_free_mem() reads pg->records[pg->index - 1]
with pg->index == 0 on every later module load, as lookup_rec() did
before commit ee92fa443358f ("ftrace: Fix invalid address access in
lookup_rec() when index is 0").

Unlink and free the new page groups when none of them got a record.

Reported-by: [email protected]
Closes: https://lore.kernel.org/all/[email protected]/
Assisted-by: LLM
Signed-off-by: Jose Fernandez (Anthropic) <[email protected]>
---
 kernel/trace/ftrace.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f9d80c7bd9f16..2cc2d41353c10 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -7689,6 +7689,20 @@ static int ftrace_process_locs(struct module *mod,
                rec->ip = addr;
        }
 
+       /*
+        * ftrace_release_mod() finds a module's page groups by their first
+        * record. If every entry was skipped there is none, so unlink the
+        * new page groups and free them now.
+        */
+       if (mod && !start_pg->index) {
+               ftrace_pages->next = NULL;
+               mutex_unlock(&ftrace_lock);
+               /* Need to synchronize with ftrace_location_range() */
+               synchronize_rcu();
+               ftrace_free_pages(start_pg);
+               return 0;
+       }
+
        if (pg->next) {
                pg_unuse = pg->next;
                pg->next = NULL;

-- 
2.52.0


Reply via email to