diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2008-11-09 09:23:57 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-09 14:17:33 -0500 |
commit | 6209344f5a3795d34b7f2c0061f49802283b6bdd (patch) | |
tree | 5c037ddbb8caac17b0c6101c9ab86387df106d41 /net/unix/garbage.c | |
parent | 058e3739f6b0753696db1952378de9e8d2a11735 (diff) |
net: unix: fix inflight counting bug in garbage collector
Previously I assumed that the receive queues of candidates don't
change during the GC. This is only half true, nothing can be received
from the queues (see comment in unix_gc()), but buffers could be added
through the other half of the socket pair, which may still have file
descriptors referring to it.
This can result in inc_inflight_move_tail() erronously increasing the
"inflight" counter for a unix socket for which dec_inflight() wasn't
previously called. This in turn can trigger the "BUG_ON(total_refs <
inflight_refs)" in a later garbage collection run.
Fix this by only manipulating the "inflight" counter for sockets which
are candidates themselves. Duplicating the file references in
unix_attach_fds() is also needed to prevent a socket becoming a
candidate for GC while the skb that contains it is not yet queued.
Reported-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
CC: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'net/unix/garbage.c')
-rw-r--r-- | net/unix/garbage.c | 49 |
1 files changed, 37 insertions, 12 deletions
diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 2a27b84f740b..6d4a9a8de5ef 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c | |||
@@ -186,8 +186,17 @@ static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *), | |||
186 | */ | 186 | */ |
187 | struct sock *sk = unix_get_socket(*fp++); | 187 | struct sock *sk = unix_get_socket(*fp++); |
188 | if (sk) { | 188 | if (sk) { |
189 | hit = true; | 189 | struct unix_sock *u = unix_sk(sk); |
190 | func(unix_sk(sk)); | 190 | |
191 | /* | ||
192 | * Ignore non-candidates, they could | ||
193 | * have been added to the queues after | ||
194 | * starting the garbage collection | ||
195 | */ | ||
196 | if (u->gc_candidate) { | ||
197 | hit = true; | ||
198 | func(u); | ||
199 | } | ||
191 | } | 200 | } |
192 | } | 201 | } |
193 | if (hit && hitlist != NULL) { | 202 | if (hit && hitlist != NULL) { |
@@ -249,11 +258,11 @@ static void inc_inflight_move_tail(struct unix_sock *u) | |||
249 | { | 258 | { |
250 | atomic_long_inc(&u->inflight); | 259 | atomic_long_inc(&u->inflight); |
251 | /* | 260 | /* |
252 | * If this is still a candidate, move it to the end of the | 261 | * If this still might be part of a cycle, move it to the end |
253 | * list, so that it's checked even if it was already passed | 262 | * of the list, so that it's checked even if it was already |
254 | * over | 263 | * passed over |
255 | */ | 264 | */ |
256 | if (u->gc_candidate) | 265 | if (u->gc_maybe_cycle) |
257 | list_move_tail(&u->link, &gc_candidates); | 266 | list_move_tail(&u->link, &gc_candidates); |
258 | } | 267 | } |
259 | 268 | ||
@@ -267,6 +276,7 @@ void unix_gc(void) | |||
267 | struct unix_sock *next; | 276 | struct unix_sock *next; |
268 | struct sk_buff_head hitlist; | 277 | struct sk_buff_head hitlist; |
269 | struct list_head cursor; | 278 | struct list_head cursor; |
279 | LIST_HEAD(not_cycle_list); | ||
270 | 280 | ||
271 | spin_lock(&unix_gc_lock); | 281 | spin_lock(&unix_gc_lock); |
272 | 282 | ||
@@ -282,10 +292,14 @@ void unix_gc(void) | |||
282 | * | 292 | * |
283 | * Holding unix_gc_lock will protect these candidates from | 293 | * Holding unix_gc_lock will protect these candidates from |
284 | * being detached, and hence from gaining an external | 294 | * being detached, and hence from gaining an external |
285 | * reference. This also means, that since there are no | 295 | * reference. Since there are no possible receivers, all |
286 | * possible receivers, the receive queues of these sockets are | 296 | * buffers currently on the candidates' queues stay there |
287 | * static during the GC, even though the dequeue is done | 297 | * during the garbage collection. |
288 | * before the detach without atomicity guarantees. | 298 | * |
299 | * We also know that no new candidate can be added onto the | ||
300 | * receive queues. Other, non candidate sockets _can_ be | ||
301 | * added to queue, so we must make sure only to touch | ||
302 | * candidates. | ||
289 | */ | 303 | */ |
290 | list_for_each_entry_safe(u, next, &gc_inflight_list, link) { | 304 | list_for_each_entry_safe(u, next, &gc_inflight_list, link) { |
291 | long total_refs; | 305 | long total_refs; |
@@ -299,6 +313,7 @@ void unix_gc(void) | |||
299 | if (total_refs == inflight_refs) { | 313 | if (total_refs == inflight_refs) { |
300 | list_move_tail(&u->link, &gc_candidates); | 314 | list_move_tail(&u->link, &gc_candidates); |
301 | u->gc_candidate = 1; | 315 | u->gc_candidate = 1; |
316 | u->gc_maybe_cycle = 1; | ||
302 | } | 317 | } |
303 | } | 318 | } |
304 | 319 | ||
@@ -325,14 +340,24 @@ void unix_gc(void) | |||
325 | list_move(&cursor, &u->link); | 340 | list_move(&cursor, &u->link); |
326 | 341 | ||
327 | if (atomic_long_read(&u->inflight) > 0) { | 342 | if (atomic_long_read(&u->inflight) > 0) { |
328 | list_move_tail(&u->link, &gc_inflight_list); | 343 | list_move_tail(&u->link, ¬_cycle_list); |
329 | u->gc_candidate = 0; | 344 | u->gc_maybe_cycle = 0; |
330 | scan_children(&u->sk, inc_inflight_move_tail, NULL); | 345 | scan_children(&u->sk, inc_inflight_move_tail, NULL); |
331 | } | 346 | } |
332 | } | 347 | } |
333 | list_del(&cursor); | 348 | list_del(&cursor); |
334 | 349 | ||
335 | /* | 350 | /* |
351 | * not_cycle_list contains those sockets which do not make up a | ||
352 | * cycle. Restore these to the inflight list. | ||
353 | */ | ||
354 | while (!list_empty(¬_cycle_list)) { | ||
355 | u = list_entry(not_cycle_list.next, struct unix_sock, link); | ||
356 | u->gc_candidate = 0; | ||
357 | list_move_tail(&u->link, &gc_inflight_list); | ||
358 | } | ||
359 | |||
360 | /* | ||
336 | * Now gc_candidates contains only garbage. Restore original | 361 | * Now gc_candidates contains only garbage. Restore original |
337 | * inflight counters for these as well, and remove the skbuffs | 362 | * inflight counters for these as well, and remove the skbuffs |
338 | * which are creating the cycle(s). | 363 | * which are creating the cycle(s). |