Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tracing/fprobe: Remove fprobe from hash in failure path

When register_fprobe_ips() fails, it tries to remove a list of
fprobe_hash_node from fprobe_ip_table, but it missed to remove
fprobe itself from fprobe_table. Moreover, when removing
the fprobe_hash_node which is added to rhltable once, it must
use kfree_rcu() after removing from rhltable.

To fix these issues, this reuses unregister_fprobe() internal
code to rollback the half-way registered fprobe.

Link: https://lore.kernel.org/all/177669366417.132053.17874946321744910456.stgit@mhiramat.tok.corp.google.com/

Fixes: 4346ba160409 ("fprobe: Rewrite fprobe on function-graph tracer")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>

+48 -44
+48 -44
kernel/trace/fprobe.c
··· 79 79 }; 80 80 81 81 /* Node insertion and deletion requires the fprobe_mutex */ 82 - static int insert_fprobe_node(struct fprobe_hlist_node *node) 82 + static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprobe *fp) 83 83 { 84 + int ret; 85 + 84 86 lockdep_assert_held(&fprobe_mutex); 85 87 86 - return rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params); 88 + ret = rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params); 89 + /* Set the fprobe pointer if insertion was successful. */ 90 + if (!ret) 91 + WRITE_ONCE(node->fp, fp); 92 + return ret; 87 93 } 88 94 89 95 /* Return true if there are synonims */ 90 96 static bool delete_fprobe_node(struct fprobe_hlist_node *node) 91 97 { 92 - lockdep_assert_held(&fprobe_mutex); 93 98 bool ret; 94 99 95 - /* Avoid double deleting */ 100 + lockdep_assert_held(&fprobe_mutex); 101 + 102 + /* Avoid double deleting and non-inserted nodes */ 96 103 if (READ_ONCE(node->fp) != NULL) { 97 104 WRITE_ONCE(node->fp, NULL); 98 105 rhltable_remove(&fprobe_ip_table, &node->hlist, ··· 763 756 fp->hlist_array = hlist_array; 764 757 hlist_array->fp = fp; 765 758 for (i = 0; i < num; i++) { 766 - hlist_array->array[i].fp = fp; 767 759 addr = ftrace_location(addrs[i]); 768 760 if (!addr) { 769 761 fprobe_fail_cleanup(fp); ··· 826 820 } 827 821 EXPORT_SYMBOL_GPL(register_fprobe); 828 822 823 + static int unregister_fprobe_nolock(struct fprobe *fp); 824 + 829 825 /** 830 826 * register_fprobe_ips() - Register fprobe to ftrace by address. 831 827 * @fp: A fprobe data structure to be registered. ··· 854 846 if (ret) 855 847 return ret; 856 848 857 - hlist_array = fp->hlist_array; 858 849 if (fprobe_is_ftrace(fp)) 859 850 ret = fprobe_ftrace_add_ips(addrs, num); 860 851 else 861 852 ret = fprobe_graph_add_ips(addrs, num); 862 - 863 - if (!ret) { 864 - add_fprobe_hash(fp); 865 - for (i = 0; i < hlist_array->size; i++) { 866 - ret = insert_fprobe_node(&hlist_array->array[i]); 867 - if (ret) 868 - break; 869 - } 870 - /* fallback on insert error */ 871 - if (ret) { 872 - for (i--; i >= 0; i--) 873 - delete_fprobe_node(&hlist_array->array[i]); 874 - } 853 + if (ret) { 854 + fprobe_fail_cleanup(fp); 855 + return ret; 875 856 } 876 857 877 - if (ret) 878 - fprobe_fail_cleanup(fp); 858 + hlist_array = fp->hlist_array; 859 + ret = add_fprobe_hash(fp); 860 + for (i = 0; i < hlist_array->size && !ret; i++) 861 + ret = insert_fprobe_node(&hlist_array->array[i], fp); 862 + 863 + if (ret) { 864 + unregister_fprobe_nolock(fp); 865 + /* In error case, wait for clean up safely. */ 866 + synchronize_rcu(); 867 + } 879 868 880 869 return ret; 881 870 } ··· 916 911 return true; 917 912 } 918 913 919 - /** 920 - * unregister_fprobe() - Unregister fprobe. 921 - * @fp: A fprobe data structure to be unregistered. 922 - * 923 - * Unregister fprobe (and remove ftrace hooks from the function entries). 924 - * 925 - * Return 0 if @fp is unregistered successfully, -errno if not. 926 - */ 927 - int unregister_fprobe(struct fprobe *fp) 914 + static int unregister_fprobe_nolock(struct fprobe *fp) 928 915 { 929 - struct fprobe_hlist *hlist_array; 916 + struct fprobe_hlist *hlist_array = fp->hlist_array; 930 917 unsigned long *addrs = NULL; 931 - int ret = 0, i, count; 918 + int i, count; 932 919 933 - mutex_lock(&fprobe_mutex); 934 - if (!fp || !fprobe_registered(fp)) { 935 - ret = -EINVAL; 936 - goto out; 937 - } 938 - 939 - hlist_array = fp->hlist_array; 940 920 addrs = kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL); 941 921 /* 942 922 * This will remove fprobe_hash_node from the hash table even if ··· 947 957 948 958 kfree_rcu(hlist_array, rcu); 949 959 fp->hlist_array = NULL; 950 - 951 - out: 952 - mutex_unlock(&fprobe_mutex); 953 - 954 960 kfree(addrs); 955 - return ret; 961 + 962 + return 0; 963 + } 964 + 965 + /** 966 + * unregister_fprobe() - Unregister fprobe. 967 + * @fp: A fprobe data structure to be unregistered. 968 + * 969 + * Unregister fprobe (and remove ftrace hooks from the function entries). 970 + * 971 + * Return 0 if @fp is unregistered successfully, -errno if not. 972 + */ 973 + int unregister_fprobe(struct fprobe *fp) 974 + { 975 + guard(mutex)(&fprobe_mutex); 976 + if (!fp || !fprobe_registered(fp)) 977 + return -EINVAL; 978 + 979 + return unregister_fprobe_nolock(fp); 956 980 } 957 981 EXPORT_SYMBOL_GPL(unregister_fprobe); 958 982