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 <zhengyejian1@xxxxxxxxxx> --- 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