|
| 1 | +diff --git a/src/dfu.c b/src/dfu.c |
| 2 | +index 6ba23e7..bd3d693 100644 |
| 3 | +--- a/src/dfu.c |
| 4 | ++++ b/src/dfu.c |
| 5 | +@@ -41,6 +41,35 @@ static int dfu_progress_callback(irecv_client_t client, const irecv_event_t* eve |
| 6 | + return 0; |
| 7 | + } |
| 8 | + |
| 9 | ++/* irecv_open_with_ecid_and_attempts() closes the caller's client at the top of |
| 10 | ++ * every retry, but irecv_open_with_ecid() frees the client on each of its own |
| 11 | ++ * error paths without clearing *pclient. A failed attempt therefore hands back |
| 12 | ++ * a dangling pointer that the next iteration frees again, aborting the process |
| 13 | ++ * with "double free detected in tcache 2". |
| 14 | ++ * |
| 15 | ++ * It fires right after iBSS: the target re-enumerates from DFU into recovery |
| 16 | ++ * mode, the first open wins the race to libusb_open() but loses it at |
| 17 | ++ * set_configuration/set_interface, and the second attempt trips over the |
| 18 | ++ * corpse. Drive the retries here instead, clearing the pointer after each |
| 19 | ++ * failure so nothing is ever freed twice. */ |
| 20 | ++static irecv_error_t dfu_open_with_ecid_retry(irecv_client_t* pclient, uint64_t ecid, int attempts) |
| 21 | ++{ |
| 22 | ++ int i; |
| 23 | ++ |
| 24 | ++ *pclient = NULL; |
| 25 | ++ for (i = 0; i < attempts; i++) { |
| 26 | ++ if (irecv_open_with_ecid(pclient, ecid) == IRECV_E_SUCCESS) { |
| 27 | ++ return IRECV_E_SUCCESS; |
| 28 | ++ } |
| 29 | ++ /* irecv_open_with_ecid() already freed the client it allocated. */ |
| 30 | ++ *pclient = NULL; |
| 31 | ++ logger(LL_DEBUG, "Connection failed. Waiting 1 sec before retry.\n"); |
| 32 | ++ sleep(1); |
| 33 | ++ } |
| 34 | ++ |
| 35 | ++ return IRECV_E_UNABLE_TO_CONNECT; |
| 36 | ++} |
| 37 | ++ |
| 38 | + int dfu_client_new(struct idevicerestore_client_t* client) |
| 39 | + { |
| 40 | + irecv_client_t dfu = NULL; |
| 41 | +@@ -54,7 +83,7 @@ int dfu_client_new(struct idevicerestore_client_t* client) |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | +- if (irecv_open_with_ecid_and_attempts(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { |
| 46 | ++ if (dfu_open_with_ecid_retry(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { |
| 47 | + logger(LL_ERROR, "Unable to connect to device in DFU mode\n"); |
| 48 | + return -1; |
| 49 | + } |
| 50 | +@@ -85,7 +114,7 @@ irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client) |
| 51 | + irecv_error_t dfu_error = IRECV_E_SUCCESS; |
| 52 | + irecv_device_t device = NULL; |
| 53 | + |
| 54 | +- if (irecv_open_with_ecid_and_attempts(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { |
| 55 | ++ if (dfu_open_with_ecid_retry(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { |
| 56 | + return NULL; |
| 57 | + } |
| 58 | + |
0 commit comments