Skip to content

Commit d68f74f

Browse files
committed
fix doublefree
1 parent 5455120 commit d68f74f

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

crates/restorekit-sys/patches/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ compiles the patched copy.
1515
plist chunks with no recovery; one dropped USB write aborted the whole
1616
restore. Reconnect to the data port and re-send the component from the
1717
start, up to 3 attempts.
18+
- `0006-fix-double-free-in-dfu-open-retry.patch` — libirecovery's
19+
`irecv_open_with_ecid()` frees the client on its error paths without clearing
20+
`*pclient`, so `irecv_open_with_ecid_and_attempts()` frees the same pointer
21+
again on its next iteration. Apple Silicon Macs hit it reliably when they
22+
re-enumerate from DFU into recovery mode after iBSS. Drive the retry loop
23+
from `dfu.c`, clearing the pointer after each failed attempt.
1824

1925
## Updating a patch
2026

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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

Comments
 (0)