In ftrace_process_locs(), a series pages are prepared and linked in
start_pg, then fentry records are skipped or added, then unused pages
are freed.

However, assume that all records are skipped, currently the start_pg
will still be in list of ftrace_pages_start but without any record.
Then in ftrace_free_mem() index record by (pg->index - 1) will be out
of bound.

To fix this issue, properly handle with unused start_pg and add
WARN_ON_ONCE() where the records need to be indexed.

Fixes: 26efd79c4624 ("ftrace: Fix possible warning on checking all pages used 
in ftrace_process_locs()")
Signed-off-by: Zheng Yejian <zhengyeji...@huawei.com>
---
 kernel/trace/ftrace.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 0e8628e4d296..c46c35ac9b42 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -6575,10 +6575,22 @@ static int ftrace_process_locs(struct module *mod,
                rec->ip = addr;
        }
 
-       if (pg->next) {
+       if (pg->index == 0) {
+               /* No record is added on the current page, so it's unused */
+               pg_unuse = pg;
+       } else if (pg->next) {
+               /* Current page has records, so it's next page is unused */
                pg_unuse = pg->next;
                pg->next = NULL;
        }
+       /*
+        * Even the start_pg hasn't been used, that means, no record has
+        * been added, so restore state of ftrace_pages and just go out.
+        */
+       if (pg_unuse == start_pg) {
+               ftrace_pages->next = NULL;
+               goto out;
+       }
 
        /* Assign the last page to ftrace_pages */
        ftrace_pages = pg;
@@ -6794,6 +6806,8 @@ void ftrace_release_mod(struct module *mod)
         */
        last_pg = &ftrace_pages_start;
        for (pg = ftrace_pages_start; pg; pg = *last_pg) {
+               /* The page should have at lease one record */
+               WARN_ON_ONCE(!pg->index);
                rec = &pg->records[0];
                if (within_module(rec->ip, mod)) {
                        /*
@@ -7176,6 +7190,8 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, 
void *end_ptr)
                mod_map = allocate_ftrace_mod_map(mod, start, end);
 
        for (pg = ftrace_pages_start; pg; last_pg = &pg->next, pg = *last_pg) {
+               /* The page should have at lease one record */
+               WARN_ON_ONCE(!pg->index);
                if (end < pg->records[0].ip ||
                    start >= (pg->records[pg->index - 1].ip + MCOUNT_INSN_SIZE))
                        continue;
-- 
2.25.1

Reply via email to