system/core
Révision | 3e872c4ce7d83fee2673694644559f6fa9bdcbe0 (tree) |
---|---|
l'heure | 2016-07-20 18:01:29 |
Auteur | Du, Changbin <changbin.du@inte...> |
Commiter | Chih-Wei Huang |
adb: sockets: fix race condition between input_thread and fdevent callback
The transport's input_thread clean up all of its sockets by
close_all_sockets function before it exit. All sockets's callback is
invoked by local_socket_event_func by fdevent thread(the main thread).
But there is no synchronization between these two thread. Missing
synchronization can cause crash or unexpected behaviour. So far has
observed two crash issue:
1. after close_all_sockets return, still has sockets pending. Then when
2. calling the socket's close callback(local_socket_close) but a socket
Change-Id: I9787b9ca7949946e977e102812518799051b9c67
Tracked-On: https://jira01.devtools.intel.com/browse/OAM-12574
Signed-off-by: Du, Changbin <changbin.du@intel.com>
Reviewed-on: https://android.intel.com:443/461670
@@ -85,6 +85,13 @@ struct asocket { | ||
85 | 85 | ** but packets are still queued for delivery |
86 | 86 | */ |
87 | 87 | int closing; |
88 | + /* flag: set when this socket is running. | |
89 | + */ | |
90 | + int running; | |
91 | + /* flag: force close this socket. if this socket is running, other | |
92 | + ** thread set this flag to request close it. | |
93 | + */ | |
94 | + int force_close; | |
88 | 95 | |
89 | 96 | /* flag: quit adbd when both ends close the |
90 | 97 | ** local service socket |
@@ -53,16 +53,13 @@ static asocket local_socket_closing_list = { | ||
53 | 53 | .prev = &local_socket_closing_list, |
54 | 54 | }; |
55 | 55 | |
56 | -// Parse the global list of sockets to find one with id |local_id|. | |
57 | -// If |peer_id| is not 0, also check that it is connected to a peer | |
58 | -// with id |peer_id|. Returns an asocket handle on success, NULL on failure. | |
59 | -asocket *find_local_socket(unsigned local_id, unsigned peer_id) | |
56 | +static asocket * | |
57 | +find_socket_in_list(asocket *list, unsigned local_id, unsigned peer_id) | |
60 | 58 | { |
61 | 59 | asocket *s; |
62 | 60 | asocket *result = NULL; |
63 | 61 | |
64 | - adb_mutex_lock(&socket_list_lock); | |
65 | - for (s = local_socket_list.next; s != &local_socket_list; s = s->next) { | |
62 | + for (s = list->next; s != list; s = s->next) { | |
66 | 63 | if (s->id != local_id) |
67 | 64 | continue; |
68 | 65 | if (peer_id == 0 || (s->peer && s->peer->id == peer_id)) { |
@@ -70,11 +67,24 @@ asocket *find_local_socket(unsigned local_id, unsigned peer_id) | ||
70 | 67 | } |
71 | 68 | break; |
72 | 69 | } |
73 | - adb_mutex_unlock(&socket_list_lock); | |
74 | 70 | |
75 | 71 | return result; |
76 | 72 | } |
77 | 73 | |
74 | + | |
75 | +// Parse the global list of sockets to find one with id |local_id|. | |
76 | +// If |peer_id| is not 0, also check that it is connected to a peer | |
77 | +// with id |peer_id|. Returns an asocket handle on success, NULL on failure. | |
78 | +asocket *find_local_socket(unsigned local_id, unsigned peer_id) | |
79 | +{ | |
80 | + asocket *result = NULL; | |
81 | + | |
82 | + adb_mutex_lock(&socket_list_lock); | |
83 | + result = find_socket_in_list(&local_socket_list, local_id, peer_id); | |
84 | + adb_mutex_unlock(&socket_list_lock); | |
85 | + return result; | |
86 | +} | |
87 | + | |
78 | 88 | static void |
79 | 89 | insert_local_socket(asocket* s, asocket* list) |
80 | 90 | { |
@@ -113,10 +123,14 @@ void remove_socket(asocket *s) | ||
113 | 123 | } |
114 | 124 | } |
115 | 125 | |
126 | +// Note: after return, all sockets refer to transport @t should be closed. | |
127 | +// (Because the atransport is going to removed.) | |
128 | +// force_close && running flag are to implement this. | |
116 | 129 | void close_all_sockets(atransport *t) |
117 | 130 | { |
118 | 131 | asocket *s; |
119 | 132 | |
133 | + D("close all sockets of transport %p\n", t); | |
120 | 134 | /* this is a little gross, but since s->close() *will* modify |
121 | 135 | ** the list out from under you, your options are limited. |
122 | 136 | */ |
@@ -124,7 +138,17 @@ void close_all_sockets(atransport *t) | ||
124 | 138 | restart: |
125 | 139 | for(s = local_socket_list.next; s != &local_socket_list; s = s->next){ |
126 | 140 | if(s->transport == t || (s->peer && s->peer->transport == t)) { |
127 | - local_socket_close_locked(s); | |
141 | + // set force_close flag since transport is going to be removed. | |
142 | + // we need ensure the socket is closed after we return. | |
143 | + s->force_close = 1; | |
144 | + // avoid race condition with pending fdevent | |
145 | + if (s->running) { | |
146 | + // unlock to give a chance to close socket after running | |
147 | + adb_mutex_unlock(&socket_list_lock); | |
148 | + adb_sleep_ms(10); // sleep to relax cpu | |
149 | + adb_mutex_lock(&socket_list_lock); | |
150 | + } else | |
151 | + local_socket_close_locked(s); | |
128 | 152 | goto restart; |
129 | 153 | } |
130 | 154 | } |
@@ -193,8 +217,18 @@ static void local_socket_ready(asocket *s) | ||
193 | 217 | |
194 | 218 | static void local_socket_close(asocket *s) |
195 | 219 | { |
220 | + unsigned local_id = s->id; | |
221 | + unsigned peer_id = s->peer ? s->peer->id : 0; | |
222 | + asocket *sk; | |
223 | + | |
196 | 224 | adb_mutex_lock(&socket_list_lock); |
197 | - local_socket_close_locked(s); | |
225 | + // we may race with close_all_sockets (called by input-thread), | |
226 | + // so need to check if socket already destoried. | |
227 | + sk = find_socket_in_list(&local_socket_list, local_id, peer_id); | |
228 | + if (!sk) | |
229 | + sk = find_socket_in_list(&local_socket_closing_list, local_id, peer_id); | |
230 | + if (sk) | |
231 | + local_socket_close_locked(s); | |
198 | 232 | adb_mutex_unlock(&socket_list_lock); |
199 | 233 | } |
200 | 234 |
@@ -250,9 +284,10 @@ static void local_socket_close_locked(asocket *s) | ||
250 | 284 | } |
251 | 285 | |
252 | 286 | /* If we are already closing, or if there are no |
253 | - ** pending packets, destroy immediately | |
287 | + ** pending packets, or need force close it, then | |
288 | + ** destroy immediately. | |
254 | 289 | */ |
255 | - if (s->closing || s->pkt_first == NULL) { | |
290 | + if (s->closing || s->force_close || s->pkt_first == NULL) { | |
256 | 291 | int id = s->id; |
257 | 292 | local_socket_destroy(s); |
258 | 293 | D("LS(%d): closed\n", id); |
@@ -272,8 +307,12 @@ static void local_socket_close_locked(asocket *s) | ||
272 | 307 | static void local_socket_event_func(int fd, unsigned ev, void* _s) |
273 | 308 | { |
274 | 309 | asocket* s = reinterpret_cast<asocket*>(_s); |
310 | + s->running = 1; | |
275 | 311 | D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev); |
276 | 312 | |
313 | + if (s->force_close) | |
314 | + goto out; | |
315 | + | |
277 | 316 | /* put the FDE_WRITE processing before the FDE_READ |
278 | 317 | ** in order to simplify the code. |
279 | 318 | */ |
@@ -287,7 +326,7 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s) | ||
287 | 326 | ** be processed in the next iteration loop |
288 | 327 | */ |
289 | 328 | if (errno == EAGAIN) { |
290 | - return; | |
329 | + goto out; | |
291 | 330 | } |
292 | 331 | } else if (r > 0) { |
293 | 332 | p->ptr += r; |
@@ -296,6 +335,7 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s) | ||
296 | 335 | } |
297 | 336 | |
298 | 337 | D(" closing after write because r=%d and errno is %d\n", r, errno); |
338 | + s->running = 0; | |
299 | 339 | s->close(s); |
300 | 340 | return; |
301 | 341 | } |
@@ -314,6 +354,7 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s) | ||
314 | 354 | */ |
315 | 355 | if (s->closing) { |
316 | 356 | D(" closing because 'closing' is set after write\n"); |
357 | + s->running = 0; | |
317 | 358 | s->close(s); |
318 | 359 | return; |
319 | 360 | } |
@@ -372,7 +413,7 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s) | ||
372 | 413 | ** this handler function will be called again |
373 | 414 | ** to process FDE_WRITE events. |
374 | 415 | */ |
375 | - return; | |
416 | + goto out; | |
376 | 417 | } |
377 | 418 | |
378 | 419 | if (r > 0) { |
@@ -387,7 +428,9 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s) | ||
387 | 428 | if ((s->fde.force_eof && !r) || is_eof) { |
388 | 429 | D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", |
389 | 430 | is_eof, r, s->fde.force_eof); |
431 | + s->running = 0; | |
390 | 432 | s->close(s); |
433 | + return; | |
391 | 434 | } |
392 | 435 | } |
393 | 436 |
@@ -398,7 +441,13 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s) | ||
398 | 441 | */ |
399 | 442 | D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd); |
400 | 443 | |
401 | - return; | |
444 | + goto out; | |
445 | + } | |
446 | +out: | |
447 | + s->running = 0; | |
448 | + if (s->force_close) { | |
449 | + D("LS(%d): force closing (fd=%d)\n", s->id, s->fd); | |
450 | + s->close(s); | |
402 | 451 | } |
403 | 452 | } |
404 | 453 |
@@ -406,6 +455,7 @@ asocket *create_local_socket(int fd) | ||
406 | 455 | { |
407 | 456 | asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket))); |
408 | 457 | if (s == NULL) fatal("cannot allocate socket"); |
458 | + memset(s, 0, sizeof(asocket)); | |
409 | 459 | s->fd = fd; |
410 | 460 | s->enqueue = local_socket_enqueue; |
411 | 461 | s->ready = local_socket_ready; |
@@ -551,6 +601,7 @@ asocket *create_remote_socket(unsigned id, atransport *t) | ||
551 | 601 | adisconnect* dis = &reinterpret_cast<aremotesocket*>(s)->disconnect; |
552 | 602 | |
553 | 603 | if (s == NULL) fatal("cannot allocate socket"); |
604 | + memset(s, 0, sizeof(asocket)); | |
554 | 605 | s->id = id; |
555 | 606 | s->enqueue = remote_socket_enqueue; |
556 | 607 | s->ready = remote_socket_ready; |
@@ -883,6 +934,7 @@ static asocket *create_smart_socket(void) | ||
883 | 934 | D("Creating smart socket \n"); |
884 | 935 | asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket))); |
885 | 936 | if (s == NULL) fatal("cannot allocate socket"); |
937 | + memset(s, 0, sizeof(asocket)); | |
886 | 938 | s->enqueue = smart_socket_enqueue; |
887 | 939 | s->ready = smart_socket_ready; |
888 | 940 | s->shutdown = NULL; |