Skip to content

Commit 2a5bfe4

Browse files
committed
ftrace: Zero out ftrace hashes when a module is removed
When a ftrace filter has a module function, and that module is removed, the filter still has its address as being enabled. This can cause interesting side effects. Nothing dangerous, but unwanted functions can be traced because of it. # cd /sys/kernel/tracing # echo ':mod:snd_seq' > set_ftrace_filter # cat set_ftrace_filter snd_use_lock_sync_helper [snd_seq] check_event_type_and_length [snd_seq] snd_seq_ioctl_pversion [snd_seq] snd_seq_ioctl_client_id [snd_seq] snd_seq_ioctl_get_queue_tempo [snd_seq] update_timestamp_of_queue [snd_seq] snd_seq_ioctl_get_queue_status [snd_seq] snd_seq_set_queue_tempo [snd_seq] snd_seq_ioctl_set_queue_tempo [snd_seq] snd_seq_ioctl_get_queue_timer [snd_seq] seq_free_client1 [snd_seq] [..] # rmmod snd_seq # cat set_ftrace_filter # modprobe kvm # cat set_ftrace_filter kvm_set_cr4 [kvm] kvm_emulate_hypercall [kvm] kvm_set_dr [kvm] This is because removing the snd_seq module after it was being filtered, left the address of the snd_seq functions in the hash. When the kvm module was loaded, some of its functions were loaded at the same address as the snd_seq module. This would enable them to be filtered and traced. Now we don't want to clear the hash completely. That would cause removing a module where only its functions are filtered, to cause the tracing to enable all functions, as an empty filter means to trace all functions. Instead, just set the hash ip address to zero. Then it will never match any function. Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 065e63f commit 2a5bfe4

File tree

1 file changed

+55
-3
lines changed

1 file changed

+55
-3
lines changed

kernel/trace/ftrace.c

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5690,10 +5690,51 @@ static int referenced_filters(struct dyn_ftrace *rec)
56905690
return cnt;
56915691
}
56925692

5693+
static void
5694+
clear_mod_from_hash(struct ftrace_page *pg, struct ftrace_hash *hash)
5695+
{
5696+
struct ftrace_func_entry *entry;
5697+
struct dyn_ftrace *rec;
5698+
int i;
5699+
5700+
if (ftrace_hash_empty(hash))
5701+
return;
5702+
5703+
for (i = 0; i < pg->index; i++) {
5704+
rec = &pg->records[i];
5705+
entry = __ftrace_lookup_ip(hash, rec->ip);
5706+
/*
5707+
* Do not allow this rec to match again.
5708+
* Yeah, it may waste some memory, but will be removed
5709+
* if/when the hash is modified again.
5710+
*/
5711+
if (entry)
5712+
entry->ip = 0;
5713+
}
5714+
}
5715+
5716+
/* Clear any records from hashs */
5717+
static void clear_mod_from_hashes(struct ftrace_page *pg)
5718+
{
5719+
struct trace_array *tr;
5720+
5721+
mutex_lock(&trace_types_lock);
5722+
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
5723+
if (!tr->ops || !tr->ops->func_hash)
5724+
continue;
5725+
mutex_lock(&tr->ops->func_hash->regex_lock);
5726+
clear_mod_from_hash(pg, tr->ops->func_hash->filter_hash);
5727+
clear_mod_from_hash(pg, tr->ops->func_hash->notrace_hash);
5728+
mutex_unlock(&tr->ops->func_hash->regex_lock);
5729+
}
5730+
mutex_unlock(&trace_types_lock);
5731+
}
5732+
56935733
void ftrace_release_mod(struct module *mod)
56945734
{
56955735
struct dyn_ftrace *rec;
56965736
struct ftrace_page **last_pg;
5737+
struct ftrace_page *tmp_page = NULL;
56975738
struct ftrace_page *pg;
56985739
int order;
56995740

@@ -5723,14 +5764,25 @@ void ftrace_release_mod(struct module *mod)
57235764

57245765
ftrace_update_tot_cnt -= pg->index;
57255766
*last_pg = pg->next;
5726-
order = get_count_order(pg->size / ENTRIES_PER_PAGE);
5727-
free_pages((unsigned long)pg->records, order);
5728-
kfree(pg);
5767+
5768+
pg->next = tmp_page;
5769+
tmp_page = pg;
57295770
} else
57305771
last_pg = &pg->next;
57315772
}
57325773
out_unlock:
57335774
mutex_unlock(&ftrace_lock);
5775+
5776+
for (pg = tmp_page; pg; pg = tmp_page) {
5777+
5778+
/* Needs to be called outside of ftrace_lock */
5779+
clear_mod_from_hashes(pg);
5780+
5781+
order = get_count_order(pg->size / ENTRIES_PER_PAGE);
5782+
free_pages((unsigned long)pg->records, order);
5783+
tmp_page = pg->next;
5784+
kfree(pg);
5785+
}
57345786
}
57355787

57365788
void ftrace_module_enable(struct module *mod)

0 commit comments

Comments
 (0)