Skip to content

Commit 0d90063

Browse files
mhiramatgregkh
authored andcommitted
tracing/fprobe: Remove fprobe from hash in failure path
commit 845947a upstream. 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: 4346ba1 ("fprobe: Rewrite fprobe on function-graph tracer") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 9345ec2 commit 0d90063

1 file changed

Lines changed: 47 additions & 43 deletions

File tree

kernel/trace/fprobe.c

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,27 @@ static const struct rhashtable_params fprobe_rht_params = {
7979
};
8080

8181
/* 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)
8383
{
84+
int ret;
85+
8486
lockdep_assert_held(&fprobe_mutex);
8587

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;
8793
}
8894

8995
/* Return true if there are synonims */
9096
static bool delete_fprobe_node(struct fprobe_hlist_node *node)
9197
{
92-
lockdep_assert_held(&fprobe_mutex);
9398
bool ret;
9499

95-
/* Avoid double deleting */
100+
lockdep_assert_held(&fprobe_mutex);
101+
102+
/* Avoid double deleting and non-inserted nodes */
96103
if (READ_ONCE(node->fp) != NULL) {
97104
WRITE_ONCE(node->fp, NULL);
98105
rhltable_remove(&fprobe_ip_table, &node->hlist,
@@ -755,7 +762,6 @@ static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num)
755762
fp->hlist_array = hlist_array;
756763
hlist_array->fp = fp;
757764
for (i = 0; i < num; i++) {
758-
hlist_array->array[i].fp = fp;
759765
addr = ftrace_location(addrs[i]);
760766
if (!addr) {
761767
fprobe_fail_cleanup(fp);
@@ -819,6 +825,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter
819825
}
820826
EXPORT_SYMBOL_GPL(register_fprobe);
821827

828+
static int unregister_fprobe_nolock(struct fprobe *fp);
829+
822830
/**
823831
* register_fprobe_ips() - Register fprobe to ftrace by address.
824832
* @fp: A fprobe data structure to be registered.
@@ -845,28 +853,25 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
845853
if (ret)
846854
return ret;
847855

848-
hlist_array = fp->hlist_array;
849856
if (fprobe_is_ftrace(fp))
850857
ret = fprobe_ftrace_add_ips(addrs, num);
851858
else
852859
ret = fprobe_graph_add_ips(addrs, num);
853-
854-
if (!ret) {
855-
add_fprobe_hash(fp);
856-
for (i = 0; i < hlist_array->size; i++) {
857-
ret = insert_fprobe_node(&hlist_array->array[i]);
858-
if (ret)
859-
break;
860-
}
861-
/* fallback on insert error */
862-
if (ret) {
863-
for (i--; i >= 0; i--)
864-
delete_fprobe_node(&hlist_array->array[i]);
865-
}
860+
if (ret) {
861+
fprobe_fail_cleanup(fp);
862+
return ret;
866863
}
867864

868-
if (ret)
869-
fprobe_fail_cleanup(fp);
865+
hlist_array = fp->hlist_array;
866+
ret = add_fprobe_hash(fp);
867+
for (i = 0; i < hlist_array->size && !ret; i++)
868+
ret = insert_fprobe_node(&hlist_array->array[i], fp);
869+
870+
if (ret) {
871+
unregister_fprobe_nolock(fp);
872+
/* In error case, wait for clean up safely. */
873+
synchronize_rcu();
874+
}
870875

871876
return ret;
872877
}
@@ -910,27 +915,12 @@ bool fprobe_is_registered(struct fprobe *fp)
910915
return true;
911916
}
912917

913-
/**
914-
* unregister_fprobe() - Unregister fprobe.
915-
* @fp: A fprobe data structure to be unregistered.
916-
*
917-
* Unregister fprobe (and remove ftrace hooks from the function entries).
918-
*
919-
* Return 0 if @fp is unregistered successfully, -errno if not.
920-
*/
921-
int unregister_fprobe(struct fprobe *fp)
918+
static int unregister_fprobe_nolock(struct fprobe *fp)
922919
{
923-
struct fprobe_hlist *hlist_array;
920+
struct fprobe_hlist *hlist_array = fp->hlist_array;
924921
unsigned long *addrs = NULL;
925-
int ret = 0, i, count;
922+
int i, count;
926923

927-
mutex_lock(&fprobe_mutex);
928-
if (!fp || !fprobe_registered(fp)) {
929-
ret = -EINVAL;
930-
goto out;
931-
}
932-
933-
hlist_array = fp->hlist_array;
934924
addrs = kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL);
935925
if (!addrs) {
936926
ret = -ENOMEM; /* TODO: Fallback to one-by-one loop */
@@ -952,12 +942,26 @@ int unregister_fprobe(struct fprobe *fp)
952942

953943
kfree_rcu(hlist_array, rcu);
954944
fp->hlist_array = NULL;
945+
kfree(addrs);
955946

956-
out:
957-
mutex_unlock(&fprobe_mutex);
947+
return 0;
948+
}
958949

959-
kfree(addrs);
960-
return ret;
950+
/**
951+
* unregister_fprobe() - Unregister fprobe.
952+
* @fp: A fprobe data structure to be unregistered.
953+
*
954+
* Unregister fprobe (and remove ftrace hooks from the function entries).
955+
*
956+
* Return 0 if @fp is unregistered successfully, -errno if not.
957+
*/
958+
int unregister_fprobe(struct fprobe *fp)
959+
{
960+
guard(mutex)(&fprobe_mutex);
961+
if (!fp || !fprobe_registered(fp))
962+
return -EINVAL;
963+
964+
return unregister_fprobe_nolock(fp);
961965
}
962966
EXPORT_SYMBOL_GPL(unregister_fprobe);
963967

0 commit comments

Comments
 (0)