Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyEval_GetLocals() leaks locals #118934

Open
colesbury opened this issue May 10, 2024 · 2 comments
Open

PyEval_GetLocals() leaks locals #118934

colesbury opened this issue May 10, 2024 · 2 comments
Labels
3.13 bugs and security fixes 3.14 new features, bugs and security fixes topic-C-API type-bug An unexpected behavior, bug, or error

Comments

@colesbury
Copy link
Contributor

colesbury commented May 10, 2024

Bug report

PyEval_GetLocals() is documented as returning a borrowed reference. It now returns a new reference, which causes callers to leak the local variables:

cpython/Python/ceval.c

Lines 2478 to 2479 in 35c4361

PyObject *locals = _PyEval_GetFrameLocals();
return locals;

cc @gaogaotiantian @markshannon

@colesbury colesbury added type-bug An unexpected behavior, bug, or error topic-C-API 3.13 bugs and security fixes 3.14 new features, bugs and security fixes labels May 10, 2024
@gaogaotiantian
Copy link
Member

This is tricky, because after PEP 669, the reference PyEval_GetLocals() gets, is the only reference, for the function-level cases. We used to have a f_locals dict in the frame to host the dictionary, but we do not anymore. Each frame.f_locals call generates a new proxy and locals() (or PyEval_GetLocals()) turns that to a dict. That's the only reference, we can't return a borrowed reference.

So we either

  • Change PyEval_GetLocals() to return a new reference, or
  • Have a list in the frame object to host all the results from PyEval_GetLocals() call (because each call could result in a different dict, it's a snapshot), and release those when the frame is released.

Which is the lesser of two evils?

@encukou
Copy link
Member

encukou commented May 13, 2024

Per PEP 667:

PyEval_GetLocals() will be implemented roughly as follows:

PyObject *PyEval_GetLocals(void) {
    PyFrameObject * = ...; // Get the current frame.
    if (frame->_locals_cache == NULL) {
        frame->_locals_cache = PyEval_GetFrameLocals();
    }
    return frame->_locals_cache;
}

As with all functions that return a borrowed reference, care must be taken to ensure that the reference is not used beyond the lifetime of the object.

i.e. a proxy should be created on first acccess, and then returned.

Is that not an option any more?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 bugs and security fixes 3.14 new features, bugs and security fixes topic-C-API type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants