aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
authorSteven Rostedt (VMware) <rostedt@goodmis.org>2017-08-31 17:36:51 -0400
committerSteven Rostedt (VMware) <rostedt@goodmis.org>2017-08-31 19:55:12 -0400
commit2a5bfe47624bfc835aa0632a0505ba55576c98db (patch)
treee6e3e3667a071359ddfe97bcba942a0449e77db8 /kernel/trace
parent065e63f951432068ba89a844fcbff68ea16ee186 (diff)
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) <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/ftrace.c58
1 files changed, 55 insertions, 3 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 96cea88fa00f..165b149ccb1a 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5690,10 +5690,51 @@ static int referenced_filters(struct dyn_ftrace *rec)
5690 return cnt; 5690 return cnt;
5691} 5691}
5692 5692
5693static void
5694clear_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 */
5717static 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
5693void ftrace_release_mod(struct module *mod) 5733void ftrace_release_mod(struct module *mod)
5694{ 5734{
5695 struct dyn_ftrace *rec; 5735 struct dyn_ftrace *rec;
5696 struct ftrace_page **last_pg; 5736 struct ftrace_page **last_pg;
5737 struct ftrace_page *tmp_page = NULL;
5697 struct ftrace_page *pg; 5738 struct ftrace_page *pg;
5698 int order; 5739 int order;
5699 5740
@@ -5723,14 +5764,25 @@ void ftrace_release_mod(struct module *mod)
5723 5764
5724 ftrace_update_tot_cnt -= pg->index; 5765 ftrace_update_tot_cnt -= pg->index;
5725 *last_pg = pg->next; 5766 *last_pg = pg->next;
5726 order = get_count_order(pg->size / ENTRIES_PER_PAGE); 5767
5727 free_pages((unsigned long)pg->records, order); 5768 pg->next = tmp_page;
5728 kfree(pg); 5769 tmp_page = pg;
5729 } else 5770 } else
5730 last_pg = &pg->next; 5771 last_pg = &pg->next;
5731 } 5772 }
5732 out_unlock: 5773 out_unlock:
5733 mutex_unlock(&ftrace_lock); 5774 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 }
5734} 5786}
5735 5787
5736void ftrace_module_enable(struct module *mod) 5788void ftrace_module_enable(struct module *mod)