Calibre consumes all memory then crashes when connecting an MTP device
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
calibre |
Fix Released
|
Undecided
|
Unassigned |
Bug Description
There is a problem where connecting a new device via MTP causes Calibre to consume all available memory until the system crashes or an OOM process kills Calibre. This is because Calibe's MTP driver tries to use Storage IDs and Object Handles interchangeably. While both identifiers are defined as UINT32s, they represent different things.
Presently, the constructor for the FilesystemCache class enumerates all of the Storage Locations on a device and creates FileOrFolder entries for them in the cache. (https:/
The constructor for the FileOrFolder class then misinterprets the `id` field on the Storage Location as an Object Handle
(https:/
instead of a Storage ID and adds it to the the cache's object index in `id_map`. (https:/
Later, when FileOrFolder instances are created for filesystem objects, the FileOrFolder constructor relocates objects at the root of the file hierarchy to reside under the Storage Location object pre-populated in the filesystem cache. (https:/
I suspect this done in an attempt to prefix the path for objects in the filesystem with the name of the storage location under which they reside (e.g. MainStorage/
When an object is added to the cache with an Object Handle that is numerically equivalent to one of the Storage IDs, the FileOrFolder constructor will replace the pre-populated Storage Location in the cache's object index (`id_map`) with the new object. Because the objects in the root of the filesystem were relocated to reside under the Storage Location, replacing it makes the new object an ancestor of all objects in the file system, including itself, creating a cyclical reference.
Afterward, calls to `full_path` on FileOrFolder instances will attempt to traverse the cyclical reference and construct what would be an infinitely long string until it consumes all of the system's free memory and Calibre crashes.
Most MTP implementations that I have seen sequentially assign Object Handles starting with 1 to objects as they are encountered in an MTP session. Once 65536 objects have been assigned handles, the implementations starts assigning values that may be numerically identical to Storage IDs (i.e. the first logical partition of the first writable storage location would be assigned the Storage ID 0x00010001 = 65537). Reproducing this issue will be specific to implementation details and Storage IDs for each MTP device, but in most cases, populating the filesystem on the primary storage location with at least 65537 files and folders and removing Calibre's driveinfo and metadata files should be sufficient.
I'll attach a patch that I have used to address the issue when working with my device.
Hmm, well it's on my TODO list to rewrite the FilesystemCache to better
support multiple storages (currently IIRC it uses a single id_map for
all storages, which depending on MTP implementation might cause object id
clashes between storages). When I do that, I will fix object_id clashes
between storage ids and filesystem objects as well.