Skip to content

Walking a cache that nothing else holds reads freed memory #69

Description

@Malkiz223

keys(), values() and items() return an iterator that does not hold a reference to the cache, only a raw cursor into it. If the cache is not stored anywhere else, it is freed while the iterator is still alive.

import cachebox


def make_cache():
    cache = cachebox.LRUCache(100)
    cache['a'] = 1
    cache['b'] = 2
    return cache


print('cachebox', cachebox.__version__)
print('walking a cache that nothing else holds ...', flush=True)

print('got:', [key for key in make_cache().keys()])

On main this dies with an access violation instead of printing the keys (Windows, CPython 3.12). A stored iterator does the same once the last reference to the cache goes away.

6.2.2 from PyPI prints the right keys, but only while nothing has reused the freed memory. Fill another cache before the walk and it silently returns that cache's keys instead:

import cachebox

c = cachebox.LRUCache(100)
c['a'] = 1
c['b'] = 2
it = c.keys()
del c

c2 = cachebox.LRUCache(1000)
for i in range(1000):
    c2[f'x{i}'] = i

print('got:', list(it))

On 6.2.2 this prints keys of c2, different ones on every run: got: ['x3', 'x4'], got: ['x21', 'x22'] and so on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions