Skip to content

ctypes.create_string_buffer does not add NUL if len(init) == size #69011

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

Closed
pohlt mannequin opened this issue Aug 7, 2015 · 10 comments
Closed

ctypes.create_string_buffer does not add NUL if len(init) == size #69011

pohlt mannequin opened this issue Aug 7, 2015 · 10 comments
Labels
3.8 (EOL) end of life 3.9 only security fixes 3.10 only security fixes easy stdlib Python modules in the Lib dir topic-ctypes type-bug An unexpected behavior, bug, or error

Comments

@pohlt
Copy link
Mannequin

pohlt mannequin commented Aug 7, 2015

BPO 24823
Nosy @terryjreedy, @amauryfa, @abalkin, @ezio-melotti, @meadori, @eryksun, @pohlt, @willingc
Files
  • create_string_buffer.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2015-08-07.10:18:08.042>
    labels = ['easy', 'type-bug', '3.8', '3.9', '3.10', 'ctypes', 'library']
    title = 'ctypes.create_string_buffer does not add NUL if len(init) == size'
    updated_at = <Date 2021-03-19.01:57:45.139>
    user = 'https://github.com/pohlt'

    bugs.python.org fields:

    activity = <Date 2021-03-19.01:57:45.139>
    actor = 'eryksun'
    assignee = 'docs@python'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)', 'ctypes']
    creation = <Date 2015-08-07.10:18:08.042>
    creator = 'tom.pohl'
    dependencies = []
    files = ['41558']
    hgrepos = []
    issue_num = 24823
    keywords = ['patch', 'easy']
    message_count = 8.0
    messages = ['248183', '248211', '248219', '248222', '257862', '257876', '258561', '389050']
    nosy_count = 10.0
    nosy_names = ['terry.reedy', 'amaury.forgeotdarc', 'belopolsky', 'ezio.melotti', 'meador.inge', 'docs@python', 'eryksun', 'tom.pohl', 'willingc', 'krista']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue24823'
    versions = ['Python 3.8', 'Python 3.9', 'Python 3.10']

    Linked PRs

    @pohlt
    Copy link
    Mannequin Author

    pohlt mannequin commented Aug 7, 2015

    From the ctypes.create_string_buffer docs:
    """If a bytes object is specified as first argument, the buffer is made one item larger than its length so that the last element in the array is a NUL termination character. An integer can be passed as second argument which allows to specify the size of the array if the length of the bytes should not be used."""

    Based on this documentation I would expect a NUL-terminated byte array in any case. However, when I do this

    >>> for size in range(5, 2, -1): print(size, ctypes.create_string_buffer(b'123', size).raw)
    5 b'123\x00\x00'
    4 b'123\x00'
    3 b'123'

    I get b'123' for size=3 without a NUL. My expectation would be the same exception as I get for create_string_buffer(b'123', 2).

    @pohlt pohlt mannequin added topic-ctypes type-bug An unexpected behavior, bug, or error labels Aug 7, 2015
    @eryksun
    Copy link
    Contributor

    eryksun commented Aug 7, 2015

    Not every buffer is null-terminated. That's just the assumption used if the size isn't specified. The documentation can possibly be reworded to make this clearer, but the function itself shouldn't be changed.

    @eryksun eryksun added docs Documentation in the Doc dir easy labels Aug 7, 2015
    @pohlt
    Copy link
    Mannequin Author

    pohlt mannequin commented Aug 7, 2015

    I agree: not every buffer is null-terminated.

    But the function name suggests that it creates a _string_ buffer which will most likely be used as an input to a C function. There, it can easily trigger a buffer overflow without a null termination which can be considered a severe security risk.

    @pohlt pohlt mannequin removed the docs Documentation in the Doc dir label Aug 7, 2015
    @pohlt
    Copy link
    Mannequin Author

    pohlt mannequin commented Aug 7, 2015

    If one needs to set a general buffer (i.e. not a null-terminated string buffer) one could always use:

    >> string = (ctypes.c_char*4)()
    >> string.raw = b'abcd'

    @krista
    Copy link
    Mannequin

    krista mannequin commented Jan 9, 2016

    Patch containing checking for buffer size, so that NULL value is the last byte as C standard specifies. Raises ValueError exception if initial value does not fit into to the buffer with NULL char.

    This should decrease the possibility of creating security issues.

    @eryksun
    Copy link
    Contributor

    eryksun commented Jan 9, 2016

    I didn't want to change the function in lieu of breaking someone's code. If this change is accepted, then it at least needs a documentation note to indicate the new behavior.

    @eryksun eryksun added docs Documentation in the Doc dir stdlib Python modules in the Lib dir labels Jan 9, 2016
    @terryjreedy
    Copy link
    Member

    (Tracker notes:

    I added as nosy the people listed as active 'experts' for ctypes on https://docs.python.org/devguide/experts.html#experts. This was easily done by going to the end of the nosy list, typing a comma ',', typing 'ctypes', and then clicking the box that appeared. This can be done for any module and the other topics listed on the page.

    The Documentation component is for issues that only change the docs, and not the code. That is why Documentation issues are auto-assigned to docs@python. Adding 'Documentation' amounts to rejecting this patch or anything else that changes the code.

    asyncio, ctypes, IDLE (idlelib), IO, and (T)tkinter are all parts of the stdlib and AFAIK, issues marked for them do not have to also be marked 'Library'.)
    ---

    I looked at ctypes.py with hg annotate. Create_string_buffer is part of Thomas Heller's original 2006-03-08 patch that moved ctypes from an external source into the stdlib. The only changes are in the isinstance class checks and the raise statement; the conditional bodies, including the one in question, are unchanged.

    Tom, we disagree on our reading of the current docs. The default number of NULL bytes added is 1. Is the second argument required to be large enough to keep the number positive? You think yes, I think no, though I agree with Eryk that the second quoted sentence could and should be clearer. I will not assume that T. Heller meant 'yes' when he wrote 'no' in the code. What do the listed experts think?

    If the doc matches the code, there is no implementation bug and this is not a behavior issue. It is still possible to request a design change as an enhancement. I think this would require agreement of at least two core developers. A deprecation notice would normally be needed. A third possibility is to decide that this is a security issue severe enough to possibly break code in 3.6 and possibly sooner. I think this would require pydev discussion.

    One problem with changing ctypes is that it is not used in the stdlib, so we have no local examples to draw on. In this case, the question would be how often is 'size' used to suppress the default NULL byte and how legitimate are such uses.

    @terryjreedy terryjreedy added docs Documentation in the Doc dir and removed docs Documentation in the Doc dir stdlib Python modules in the Lib dir topic-ctypes labels Jan 18, 2016
    @berkerpeksag berkerpeksag removed the docs Documentation in the Doc dir label Feb 29, 2016
    @eryksun
    Copy link
    Contributor

    eryksun commented Mar 19, 2021

    The Documentation component is for issues that only change the docs

    That's not clear in the triaging guide for the multi-select component field. (However, it is clearly stated as such for the GitHub PR label "type-documentation".) If that's really the case, then I'll manually nosy the docs team in cases such as this. When wording is disputed and needs to be clarified, I want an expert at documentation to propose or review the change.

    Adding 'Documentation' amounts to rejecting this patch or anything
    else that changes the code.

    That was not my intent. I accepted Tom's position that "string" means a C string, which must be null-terminated. So I added the "Lib" tag and left it as a "behavior" issue instead of changing it to "enhancement".

    I'm concerned that the old c_buffer() function is defined to call create_string_buffer(), and it's not officially deprecated in the docs or the source code. (There's a commented-out deprecation warning.) A related concern is that the documentation says that the length of a byte-string initializer should not be used if the size is specified, which allows creating a character-array that's not null-terminated. If it raises a ValueError in this case, the wording should be clear that the value of size must be large enough to set the initial value as a null-terminated string. I also would want c_buffer() to get a separate implementation in this case.

    If accepted, create_unicode_buffer(init, size) should also be changed to require that init is set as a null-terminated string.

    @eryksun eryksun added stdlib Python modules in the Lib dir 3.8 (EOL) end of life 3.9 only security fixes 3.10 only security fixes topic-ctypes labels Mar 19, 2021
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @pohlt
    Copy link

    pohlt commented Sep 22, 2022

    I'm curious how many years will pass until this issue get its CVE entry. 🤔

    encukou pushed a commit that referenced this issue May 29, 2025
    …32858)
    
    This adds a warning about the possibly-missing NUL terminator, but in a way
    that doesn't make it sound like a bug/wart.
    miss-islington pushed a commit to miss-islington/cpython that referenced this issue May 29, 2025
    …pythonGH-132858)
    
    This adds a warning about the possibly-missing NUL terminator, but in a way
    that doesn't make it sound like a bug/wart.
    (cherry picked from commit b783e17)
    
    Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
    miss-islington pushed a commit to miss-islington/cpython that referenced this issue May 29, 2025
    …pythonGH-132858)
    
    This adds a warning about the possibly-missing NUL terminator, but in a way
    that doesn't make it sound like a bug/wart.
    (cherry picked from commit b783e17)
    
    Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
    encukou pushed a commit that referenced this issue May 29, 2025
    GH-132858) (GH-134882)
    
    This adds a warning about the possibly-missing NUL terminator, but in a way
    that doesn't make it sound like a bug/wart.
    (cherry picked from commit b783e17)
    
    Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
    encukou pushed a commit that referenced this issue May 29, 2025
    …H-132858) (GH-134881)
    
    This adds a warning about the possibly-missing NUL terminator, but in a way
    that doesn't make it sound like a bug/wart.
    (cherry picked from commit b783e17)
    
    Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
    @encukou
    Copy link
    Member

    encukou commented May 29, 2025

    No CVE that I'm aware of, but, 10 years until it got a bright red warning in the docs. (See the PR preview until the published docs updated.)
    Hopefully the new docs make the behaviour clearer.

    Thank you @StanFromIreland for pushing this through!

    @encukou encukou closed this as completed May 29, 2025
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.8 (EOL) end of life 3.9 only security fixes 3.10 only security fixes easy stdlib Python modules in the Lib dir topic-ctypes type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    5 participants