Skip to content

_Py_Mangle reads out of bounds for one-char identifiers #159164

Description

@Tony123-tech

Bug description

_Py_Mangle() in Python/symtable.c calls PyUnicode_READ_CHAR(ident, 1) without first checking that ident has at least two characters. If ident is a one-character string (e.g. "_"), this reads one code point past the end of the unicode buffer.

The read is silent in release builds because a 1-char ASCII unicode object has enough padding for the null terminator, so ASAN does not trip. But it is still UB per the PyUnicode_READ_CHAR contract.

Reproducer

class C:
    _

Call path

_Py_Mangle entry check:

if (privateobj == NULL || !PyUnicode_Check(privateobj) ||
    PyUnicode_READ_CHAR(ident, 0) != '_' ||
    PyUnicode_READ_CHAR(ident, 1) != '_') {
    return Py_NewRef(ident);
}

For ident == "", READ_CHAR(ident, 0) != '' is false, so READ_CHAR(ident, 1) is evaluated — out of bounds.

Comparison

_Py_IsPrivateName() in the same file does guard with nlen < 3 before indexing. The guard is missing from _Py_Mangle().

Suggested fix

Add PyUnicode_GET_LENGTH(ident) < 2 || to the condition.

Linked PRs

Activity

  1. added 2 commits that reference this issue on Oct 11, 2026
  2. ZeroIntensity commented on Oct 11, 2026

    @ZeroIntensity
    Member

    Sorry, but this isn't a bug. The docs for PyUnicode_READ_CHAR explicitly allow this:

    CPython implementation detail: Accept reading the trailing null character at index PyUnicode_GetLength().

    There's also a comment in PyUnicode_READ_CHAR that states this is allowed:

    // Tolerate reading the NUL character at str[len(str)]
    assert(index <= PyUnicode_GET_LENGTH(unicode));

    The proposed fix is harmless, but it's unnecessary and encourages people to look for more cases like this.

  3. StanFromIreland commented on Oct 11, 2026

    @StanFromIreland
    Member

    In the future, please include sanitiser logs actually demonstrating the bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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