idevicerestore crashes (in some situations) when trying to
connect to a device after placing it in recovery mode when a second
device is attached.
The conditions to reproduce this seem to require at least two
devices attached to USB - one device as the target for
idevicerestore (specified using the -i option and the devices ECID)
in DFU mode, and one or more additional devices in "normal mode"
that are connected but otherwise inactive.
The SIGSEGV does not always occur with this configuration, but
once a configuration which causes the fault exists, the SIGSEGV
occurs deterministically for that particular configuration. The
order of devices as reported by idevice_get_device_list() may be
important - the SIGSEGV occurs when checking the "normal mode"
device before the target device has finished booting into recovery
mode. The order of the USB ports the devices are attached to may
also be a factor.
The SIGSEGV occurs due to some kind of data-corruption that
occurs in restore_is_current_device() between opening the
restored_client_t instance to the "normal" mode device and freeing
it. Freeing the corrupted handle causes the SIGSEGV.
The recent
patch to libusbmuxd to add a mutex to prevent simultaneous
access does not seem to affect this issue.
Stepping through the code in restore_is_current_device():
restore_error = restored_client_new(device, &restored, "idevicerestore");
// This succeeds... snipping error handling code.
// POINT A
restore_error = restored_query_type(restored, &type, &version);
if ((restore_error == RESTORE_E_SUCCESS) && type && (strcmp(type, "com.apple.mobile.restored") == 0)) {
debug("%s: Connected to %s, version %d\n", __func__, type, (int)version);
} else {
// This branch is reached and...
info("%s: device %s is not in restore mode\n", __func__, uuid);
// The SIGSEGV occurs in this function
// POINT B
restored_client_free(restored);
idevice_free(device);
return 0;
}
The corruption occurs between point A and point B. For example,
at POINT A the restored_client_t handle has members such that:
- restored->parent points to the libplist client structure and
seems correct
- restored->uuid points to a string that contains the correct
uuid for the device... e.g. the string contains
"e3a2828ad3ed..."
- restored->label points to a string that contains
"idevicerestore" (as copied from the literal)
- restored->info points to a structure in allocated memory
region and seems correct
... but at POINT B ...
- restored->parent still points to the libplist client
structure and seems correct
- restored->uuid points to the same string, but the first 4
bytes are corrupted. It seems the first four bytes of the string
have been overwritten by a pointer to something... e.g. the string
contains "[4 non-ascii bytes for the pointer]828ad3ed..."
- restored->label points to the same string, but the first 4
bytes are corrupted. Again, the first four bytes of the string have
been overwritten by a pointer to something (different from the
uuid-string-corrupting-pointer)... e.g. it contains"[4 non-ascii
bytes for the point]icerestore"
- restored->info points to a location in the code area of the
process, not the same location as at point A. The SIGSEGV occurs
when free() is called on this (by the libplist cleanup code for
this structure).
Commenting out the call to restored_client_free() prevents the
SIGSEGV, but obviously leaks.
Any ideas what could be causing this?