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

Reference counting bug with manually allocated heap types #128923

Open
colesbury opened this issue Jan 16, 2025 · 1 comment
Open

Reference counting bug with manually allocated heap types #128923

colesbury opened this issue Jan 16, 2025 · 1 comment
Assignees
Labels
3.14 new features, bugs and security fixes topic-free-threading type-bug An unexpected behavior, bug, or error type-crash A hard crash of the interpreter, possibly with a core dump

Comments

@colesbury
Copy link
Contributor

colesbury commented Jan 16, 2025

Bug report

Found by @vfdev-5.

This is specific to the free threading build and 3.14.

XLA/Jax uses the following code to create a heap type:

  // We need to use heap-allocated type objects because we want to add
  // additional methods dynamically.
...
    nb::str name = nb::str("PmapFunction");
    nb::str qualname = nb::str("PmapFunction");
    PyHeapTypeObject* heap_type = reinterpret_cast<PyHeapTypeObject*>(
        PyType_Type.tp_alloc(&PyType_Type, 0));
    // Caution: we must not call any functions that might invoke the GC until
    // PyType_Ready() is called. Otherwise the GC might see a half-constructed
    // type object.
    CHECK(heap_type) << "Unable to create heap type object";
    heap_type->ht_name = name.release().ptr();
    heap_type->ht_qualname = qualname.release().ptr();
   ...

https://github.com/openxla/xla/blob/19a8e8e05fb34c5c4b8c38c9a8225e89f008c8c1/xla/python/pmap_lib.cc#L1027-L1058

In other words, the heap type is created by by calling PyType_Type.tp_alloc and filling in the fields, instead of the more common use of PyType_FromSpec. This leaves unique_id zero initialized. The problem is that unique_id=0 currently looks like a valid unique id for per-thread reference counting, which leads to reference counting errors and use-after-frees.

I think we should change the per-thread reference counting so that unique_id=0 is the sentinel value indicating that it's not assigned instead of the current unique_id=-1 convention.

Full repro

Linked PRs

@colesbury colesbury added 3.14 new features, bugs and security fixes topic-free-threading type-bug An unexpected behavior, bug, or error type-crash A hard crash of the interpreter, possibly with a core dump labels Jan 16, 2025
@colesbury colesbury self-assigned this Jan 16, 2025
colesbury added a commit to colesbury/cpython that referenced this issue Jan 16, 2025
In the free threading build, the per thread reference counting uses a
unique id for some objects to index into the local reference count
table. Use 0 instead of -1 to indicate that the id is not assigned. This
avoids bugs where zero-initialized heap type objects look like they have
a unique id assigned.
@hawkinsp
Copy link
Contributor

(And for posterity, we did update our code to use PyType_FromSpec after finding this, which was a somewhat overdue cleanup, but it was still a surprising behavior change.)

Yhg1s pushed a commit that referenced this issue Jan 17, 2025
In the free threading build, the per thread reference counting uses a
unique id for some objects to index into the local reference count
table. Use 0 instead of -1 to indicate that the id is not assigned. This
avoids bugs where zero-initialized heap type objects look like they have
a unique id assigned.
Yhg1s added a commit that referenced this issue Jan 17, 2025
…128951)

Fix pydoc's docclass() for classes inheriting from object without the `__module__` attribute, like `_testcapi.HeapType`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.14 new features, bugs and security fixes topic-free-threading type-bug An unexpected behavior, bug, or error type-crash A hard crash of the interpreter, possibly with a core dump
Projects
None yet
Development

No branches or pull requests

2 participants