mirror of
https://github.com/aaru-dps/libaaruformat.git
synced 2026-09-14 10:58:38 +00:00
The read caches sized themselves as MAX_CACHE_SIZE / (SectorSize << shift), but ctx->shift is only ever assigned on the DDT v1 path, and the context is zeroed at open. On every DDT v2 image the divisor therefore collapsed to the sector size and the limit came out as 262144 entries, while each block_cache entry holds a whole decompressed data block of SectorSize << dataShift bytes, 8 MiB with the default data_shift of 12. That is a ceiling of roughly 2 TiB rather than the intended 512 MiB, so the count limit was never reached: a sequential sector-by-sector pass cached the entire decompressed image and freed none of it until aaruf_close(). Comparing two 40 GB images this way was killed by the OOM reaper. An entry count cannot express a memory budget when the values vary in size, and here they vary by five orders of magnitude: block_header_cache holds BlockHeader structs while block_cache holds megabyte payloads, yet both were given the same limit. Track the byte size of each value instead and evict least-recently-used entries until the cache is back under budget, and drop the block geometry from cache initialization entirely. The block cache gets the 512 MiB MAX_CACHE_SIZE it always intended; block headers get a separate 8 MiB budget, which covers far more blocks than the payload cache can hold. The entry just inserted is never evicted, so callers may keep using the pointer they handed over for the rest of the call. That also removes the use-after-free that a zero limit used to cause: the old eviction loop started at the hash head and could free the entry just added, after which the caller read from freed memory.