@@ -59,7 +59,7 @@ struct uprobe {
59
59
struct rw_semaphore register_rwsem ;
60
60
struct rw_semaphore consumer_rwsem ;
61
61
struct list_head pending_list ;
62
- struct uprobe_consumer * consumers ;
62
+ struct list_head consumers ;
63
63
struct inode * inode ; /* Also hold a ref to inode */
64
64
struct rcu_head rcu ;
65
65
loff_t offset ;
@@ -783,6 +783,7 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset,
783
783
uprobe -> inode = inode ;
784
784
uprobe -> offset = offset ;
785
785
uprobe -> ref_ctr_offset = ref_ctr_offset ;
786
+ INIT_LIST_HEAD (& uprobe -> consumers );
786
787
init_rwsem (& uprobe -> register_rwsem );
787
788
init_rwsem (& uprobe -> consumer_rwsem );
788
789
RB_CLEAR_NODE (& uprobe -> rb_node );
@@ -808,32 +809,19 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset,
808
809
static void consumer_add (struct uprobe * uprobe , struct uprobe_consumer * uc )
809
810
{
810
811
down_write (& uprobe -> consumer_rwsem );
811
- uc -> next = uprobe -> consumers ;
812
- uprobe -> consumers = uc ;
812
+ list_add_rcu (& uc -> cons_node , & uprobe -> consumers );
813
813
up_write (& uprobe -> consumer_rwsem );
814
814
}
815
815
816
816
/*
817
817
* For uprobe @uprobe, delete the consumer @uc.
818
- * Return true if the @uc is deleted successfully
819
- * or return false.
818
+ * Should never be called with consumer that's not part of @uprobe->consumers.
820
819
*/
821
- static bool consumer_del (struct uprobe * uprobe , struct uprobe_consumer * uc )
820
+ static void consumer_del (struct uprobe * uprobe , struct uprobe_consumer * uc )
822
821
{
823
- struct uprobe_consumer * * con ;
824
- bool ret = false;
825
-
826
822
down_write (& uprobe -> consumer_rwsem );
827
- for (con = & uprobe -> consumers ; * con ; con = & (* con )-> next ) {
828
- if (* con == uc ) {
829
- * con = uc -> next ;
830
- ret = true;
831
- break ;
832
- }
833
- }
823
+ list_del_rcu (& uc -> cons_node );
834
824
up_write (& uprobe -> consumer_rwsem );
835
-
836
- return ret ;
837
825
}
838
826
839
827
static int __copy_insn (struct address_space * mapping , struct file * filp ,
@@ -929,7 +917,8 @@ static bool filter_chain(struct uprobe *uprobe, struct mm_struct *mm)
929
917
bool ret = false;
930
918
931
919
down_read (& uprobe -> consumer_rwsem );
932
- for (uc = uprobe -> consumers ; uc ; uc = uc -> next ) {
920
+ list_for_each_entry_srcu (uc , & uprobe -> consumers , cons_node ,
921
+ srcu_read_lock_held (& uprobes_srcu )) {
933
922
ret = consumer_filter (uc , mm );
934
923
if (ret )
935
924
break ;
@@ -1125,18 +1114,29 @@ void uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
1125
1114
int err ;
1126
1115
1127
1116
down_write (& uprobe -> register_rwsem );
1128
- if (WARN_ON (!consumer_del (uprobe , uc ))) {
1129
- err = - ENOENT ;
1130
- } else {
1131
- err = register_for_each_vma (uprobe , NULL );
1132
- /* TODO : cant unregister? schedule a worker thread */
1133
- if (unlikely (err ))
1134
- uprobe_warn (current , "unregister, leaking uprobe" );
1135
- }
1117
+ consumer_del (uprobe , uc );
1118
+ err = register_for_each_vma (uprobe , NULL );
1136
1119
up_write (& uprobe -> register_rwsem );
1137
1120
1138
- if (!err )
1139
- put_uprobe (uprobe );
1121
+ /* TODO : cant unregister? schedule a worker thread */
1122
+ if (unlikely (err )) {
1123
+ uprobe_warn (current , "unregister, leaking uprobe" );
1124
+ goto out_sync ;
1125
+ }
1126
+
1127
+ put_uprobe (uprobe );
1128
+
1129
+ out_sync :
1130
+ /*
1131
+ * Now that handler_chain() and handle_uretprobe_chain() iterate over
1132
+ * uprobe->consumers list under RCU protection without holding
1133
+ * uprobe->register_rwsem, we need to wait for RCU grace period to
1134
+ * make sure that we can't call into just unregistered
1135
+ * uprobe_consumer's callbacks anymore. If we don't do that, fast and
1136
+ * unlucky enough caller can free consumer's memory and cause
1137
+ * handler_chain() or handle_uretprobe_chain() to do an use-after-free.
1138
+ */
1139
+ synchronize_srcu (& uprobes_srcu );
1140
1140
}
1141
1141
EXPORT_SYMBOL_GPL (uprobe_unregister );
1142
1142
@@ -1214,13 +1214,20 @@ EXPORT_SYMBOL_GPL(uprobe_register);
1214
1214
int uprobe_apply (struct uprobe * uprobe , struct uprobe_consumer * uc , bool add )
1215
1215
{
1216
1216
struct uprobe_consumer * con ;
1217
- int ret = - ENOENT ;
1217
+ int ret = - ENOENT , srcu_idx ;
1218
1218
1219
1219
down_write (& uprobe -> register_rwsem );
1220
- for (con = uprobe -> consumers ; con && con != uc ; con = con -> next )
1221
- ;
1222
- if (con )
1223
- ret = register_for_each_vma (uprobe , add ? uc : NULL );
1220
+
1221
+ srcu_idx = srcu_read_lock (& uprobes_srcu );
1222
+ list_for_each_entry_srcu (con , & uprobe -> consumers , cons_node ,
1223
+ srcu_read_lock_held (& uprobes_srcu )) {
1224
+ if (con == uc ) {
1225
+ ret = register_for_each_vma (uprobe , add ? uc : NULL );
1226
+ break ;
1227
+ }
1228
+ }
1229
+ srcu_read_unlock (& uprobes_srcu , srcu_idx );
1230
+
1224
1231
up_write (& uprobe -> register_rwsem );
1225
1232
1226
1233
return ret ;
@@ -2085,10 +2092,12 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
2085
2092
struct uprobe_consumer * uc ;
2086
2093
int remove = UPROBE_HANDLER_REMOVE ;
2087
2094
bool need_prep = false; /* prepare return uprobe, when needed */
2095
+ bool has_consumers = false;
2088
2096
2089
- down_read (& uprobe -> register_rwsem );
2090
2097
current -> utask -> auprobe = & uprobe -> arch ;
2091
- for (uc = uprobe -> consumers ; uc ; uc = uc -> next ) {
2098
+
2099
+ list_for_each_entry_srcu (uc , & uprobe -> consumers , cons_node ,
2100
+ srcu_read_lock_held (& uprobes_srcu )) {
2092
2101
int rc = 0 ;
2093
2102
2094
2103
if (uc -> handler ) {
@@ -2101,31 +2110,40 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
2101
2110
need_prep = true;
2102
2111
2103
2112
remove &= rc ;
2113
+ has_consumers = true;
2104
2114
}
2105
2115
current -> utask -> auprobe = NULL ;
2106
2116
2107
2117
if (need_prep && !remove )
2108
2118
prepare_uretprobe (uprobe , regs ); /* put bp at return */
2109
2119
2110
- if (remove && uprobe -> consumers ) {
2111
- WARN_ON (!uprobe_is_active (uprobe ));
2112
- unapply_uprobe (uprobe , current -> mm );
2120
+ if (remove && has_consumers ) {
2121
+ down_read (& uprobe -> register_rwsem );
2122
+
2123
+ /* re-check that removal is still required, this time under lock */
2124
+ if (!filter_chain (uprobe , current -> mm )) {
2125
+ WARN_ON (!uprobe_is_active (uprobe ));
2126
+ unapply_uprobe (uprobe , current -> mm );
2127
+ }
2128
+
2129
+ up_read (& uprobe -> register_rwsem );
2113
2130
}
2114
- up_read (& uprobe -> register_rwsem );
2115
2131
}
2116
2132
2117
2133
static void
2118
2134
handle_uretprobe_chain (struct return_instance * ri , struct pt_regs * regs )
2119
2135
{
2120
2136
struct uprobe * uprobe = ri -> uprobe ;
2121
2137
struct uprobe_consumer * uc ;
2138
+ int srcu_idx ;
2122
2139
2123
- down_read (& uprobe -> register_rwsem );
2124
- for (uc = uprobe -> consumers ; uc ; uc = uc -> next ) {
2140
+ srcu_idx = srcu_read_lock (& uprobes_srcu );
2141
+ list_for_each_entry_srcu (uc , & uprobe -> consumers , cons_node ,
2142
+ srcu_read_lock_held (& uprobes_srcu )) {
2125
2143
if (uc -> ret_handler )
2126
2144
uc -> ret_handler (uc , ri -> func , regs );
2127
2145
}
2128
- up_read ( & uprobe -> register_rwsem );
2146
+ srcu_read_unlock ( & uprobes_srcu , srcu_idx );
2129
2147
}
2130
2148
2131
2149
static struct return_instance * find_next_ret_chain (struct return_instance * ri )
0 commit comments