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

tipc: involve reference counter for subscriber

At present subscriber's lock is used to protect the subscription list
of subscriber as well as subscriptions linked into the list. While one
or all subscriptions are deleted through iterating the list, the
subscriber's lock must be held. Meanwhile, as deletion of subscription
may happen in subscription timer's handler, the lock must be grabbed
in the function as well. When subscription's timer is terminated with
del_timer_sync() during above iteration, subscriber's lock has to be
temporarily released, otherwise, deadlock may occur. However, the
temporary release may cause the double free of a subscription as the
subscription is not disconnected from the subscription list.

Now if a reference counter is introduced to subscriber, subscription's
timer can be asynchronously stopped with del_timer(). As a result, the
issue is not only able to be fixed, but also relevant code is pretty
readable and understandable.

Signed-off-by: Ying Xue <ying.xue@windriver.com>
Reviewed-by: Jon Maloy <jon.maloy@ericson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ying Xue and committed by
David S. Miller
00bc00a9 1b764828

+74 -90
+74 -90
net/tipc/subscr.c
··· 40 40 41 41 /** 42 42 * struct tipc_subscriber - TIPC network topology subscriber 43 + * @kref: reference counter to tipc_subscription object 43 44 * @conid: connection identifier to server connecting to subscriber 44 45 * @lock: control access to subscriber 45 46 * @subscrp_list: list of subscription objects for this subscriber 46 47 */ 47 48 struct tipc_subscriber { 49 + struct kref kref; 48 50 int conid; 49 51 spinlock_t lock; 50 52 struct list_head subscrp_list; 51 53 }; 54 + 55 + static void tipc_subscrp_delete(struct tipc_subscription *sub); 56 + static void tipc_subscrb_put(struct tipc_subscriber *subscriber); 52 57 53 58 /** 54 59 * htohl - convert value to endianness used by destination ··· 121 116 { 122 117 struct tipc_subscription *sub = (struct tipc_subscription *)data; 123 118 struct tipc_subscriber *subscriber = sub->subscriber; 124 - struct tipc_net *tn = net_generic(sub->net, tipc_net_id); 125 - 126 - /* The spin lock per subscriber is used to protect its members */ 127 - spin_lock_bh(&subscriber->lock); 128 - 129 - /* Validate timeout (in case subscription is being cancelled) */ 130 - if (sub->timeout == TIPC_WAIT_FOREVER) { 131 - spin_unlock_bh(&subscriber->lock); 132 - return; 133 - } 134 - 135 - /* Unlink subscription from name table */ 136 - tipc_nametbl_unsubscribe(sub); 137 - 138 - /* Unlink subscription from subscriber */ 139 - list_del(&sub->subscrp_list); 140 - 141 - spin_unlock_bh(&subscriber->lock); 142 119 143 120 /* Notify subscriber of timeout */ 144 121 tipc_subscrp_send_event(sub, sub->evt.s.seq.lower, sub->evt.s.seq.upper, 145 122 TIPC_SUBSCR_TIMEOUT, 0, 0); 146 123 147 - /* Now destroy subscription */ 148 - kfree(sub); 149 - atomic_dec(&tn->subscription_count); 124 + spin_lock_bh(&subscriber->lock); 125 + tipc_subscrp_delete(sub); 126 + spin_unlock_bh(&subscriber->lock); 127 + 128 + tipc_subscrb_put(subscriber); 129 + } 130 + 131 + static void tipc_subscrb_kref_release(struct kref *kref) 132 + { 133 + struct tipc_subscriber *subcriber = container_of(kref, 134 + struct tipc_subscriber, kref); 135 + 136 + kfree(subcriber); 137 + } 138 + 139 + static void tipc_subscrb_put(struct tipc_subscriber *subscriber) 140 + { 141 + kref_put(&subscriber->kref, tipc_subscrb_kref_release); 142 + } 143 + 144 + static void tipc_subscrb_get(struct tipc_subscriber *subscriber) 145 + { 146 + kref_get(&subscriber->kref); 147 + } 148 + 149 + static struct tipc_subscriber *tipc_subscrb_create(int conid) 150 + { 151 + struct tipc_subscriber *subscriber; 152 + 153 + subscriber = kzalloc(sizeof(*subscriber), GFP_ATOMIC); 154 + if (!subscriber) { 155 + pr_warn("Subscriber rejected, no memory\n"); 156 + return NULL; 157 + } 158 + kref_init(&subscriber->kref); 159 + INIT_LIST_HEAD(&subscriber->subscrp_list); 160 + subscriber->conid = conid; 161 + spin_lock_init(&subscriber->lock); 162 + 163 + return subscriber; 164 + } 165 + 166 + static void tipc_subscrb_delete(struct tipc_subscriber *subscriber) 167 + { 168 + struct tipc_subscription *sub, *temp; 169 + 170 + spin_lock_bh(&subscriber->lock); 171 + /* Destroy any existing subscriptions for subscriber */ 172 + list_for_each_entry_safe(sub, temp, &subscriber->subscrp_list, 173 + subscrp_list) { 174 + if (del_timer(&sub->timer)) { 175 + tipc_subscrp_delete(sub); 176 + tipc_subscrb_put(subscriber); 177 + } 178 + } 179 + spin_unlock_bh(&subscriber->lock); 180 + 181 + tipc_subscrb_put(subscriber); 150 182 } 151 183 152 184 static void tipc_subscrp_delete(struct tipc_subscription *sub) ··· 196 154 atomic_dec(&tn->subscription_count); 197 155 } 198 156 199 - static struct tipc_subscriber *tipc_subscrb_create(int conid) 200 - { 201 - struct tipc_subscriber *subscriber; 202 - 203 - subscriber = kzalloc(sizeof(*subscriber), GFP_ATOMIC); 204 - if (!subscriber) { 205 - pr_warn("Subscriber rejected, no memory\n"); 206 - return NULL; 207 - } 208 - INIT_LIST_HEAD(&subscriber->subscrp_list); 209 - subscriber->conid = conid; 210 - spin_lock_init(&subscriber->lock); 211 - 212 - return subscriber; 213 - } 214 - 215 - static void tipc_subscrb_delete(struct tipc_subscriber *subscriber) 216 - { 217 - struct tipc_subscription *sub; 218 - struct tipc_subscription *sub_temp; 219 - 220 - spin_lock_bh(&subscriber->lock); 221 - 222 - /* Destroy any existing subscriptions for subscriber */ 223 - list_for_each_entry_safe(sub, sub_temp, &subscriber->subscrp_list, 224 - subscrp_list) { 225 - if (sub->timeout != TIPC_WAIT_FOREVER) { 226 - spin_unlock_bh(&subscriber->lock); 227 - del_timer_sync(&sub->timer); 228 - spin_lock_bh(&subscriber->lock); 229 - } 230 - tipc_subscrp_delete(sub); 231 - } 232 - spin_unlock_bh(&subscriber->lock); 233 - 234 - /* Now destroy subscriber */ 235 - kfree(subscriber); 236 - } 237 - 238 - /** 239 - * tipc_subscrp_cancel - handle subscription cancellation request 240 - * 241 - * Called with subscriber lock held. Routine must temporarily release lock 242 - * to enable the subscription timeout routine to finish without deadlocking; 243 - * the lock is then reclaimed to allow caller to release it upon return. 244 - * 245 - * Note that fields of 's' use subscriber's endianness! 246 - */ 247 157 static void tipc_subscrp_cancel(struct tipc_subscr *s, 248 158 struct tipc_subscriber *subscriber) 249 159 { 250 - struct tipc_subscription *sub; 251 - struct tipc_subscription *sub_temp; 252 - int found = 0; 160 + struct tipc_subscription *sub, *temp; 253 161 254 162 /* Find first matching subscription, exit if not found */ 255 - list_for_each_entry_safe(sub, sub_temp, &subscriber->subscrp_list, 163 + list_for_each_entry_safe(sub, temp, &subscriber->subscrp_list, 256 164 subscrp_list) { 257 165 if (!memcmp(s, &sub->evt.s, sizeof(struct tipc_subscr))) { 258 - found = 1; 166 + if (del_timer(&sub->timer)) { 167 + tipc_subscrp_delete(sub); 168 + tipc_subscrb_put(subscriber); 169 + } 259 170 break; 260 171 } 261 172 } 262 - if (!found) 263 - return; 264 - 265 - /* Cancel subscription timer (if used), then delete subscription */ 266 - if (sub->timeout != TIPC_WAIT_FOREVER) { 267 - sub->timeout = TIPC_WAIT_FOREVER; 268 - spin_unlock_bh(&subscriber->lock); 269 - del_timer_sync(&sub->timer); 270 - spin_lock_bh(&subscriber->lock); 271 - } 272 - tipc_subscrp_delete(sub); 273 173 } 274 174 275 175 static int tipc_subscrp_create(struct net *net, struct tipc_subscr *s, ··· 265 281 sub->swap = swap; 266 282 memcpy(&sub->evt.s, s, sizeof(*s)); 267 283 atomic_inc(&tn->subscription_count); 268 - if (sub->timeout != TIPC_WAIT_FOREVER) { 269 - setup_timer(&sub->timer, tipc_subscrp_timeout, 270 - (unsigned long)sub); 271 - mod_timer(&sub->timer, jiffies + sub->timeout); 272 - } 284 + setup_timer(&sub->timer, tipc_subscrp_timeout, (unsigned long)sub); 285 + if (sub->timeout != TIPC_WAIT_FOREVER) 286 + sub->timeout += jiffies; 287 + if (!mod_timer(&sub->timer, sub->timeout)) 288 + tipc_subscrb_get(subscriber); 273 289 *sub_p = sub; 274 290 return 0; 275 291 }