Skip to content

PyOS_vsnprintf() underflow leads to memory corruption #46840

Closed

Description

@jnferguson
mannequin
BPO 2588
Nosy @gpshead, @amauryfa, @abalkin

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 = 'https://github.com/gpshead'
closed_at = <Date 2008-06-02.00:08:34.074>
created_at = <Date 2008-04-08.15:55:09.533>
labels = ['type-security', 'interpreter-core']
title = 'PyOS_vsnprintf() underflow leads to memory corruption'
updated_at = <Date 2009-04-14.13:54:38.391>
user = 'https://bugs.python.org/jnferguson'

bugs.python.org fields:

activity = <Date 2009-04-14.13:54:38.391>
actor = 'psss'
assignee = 'gregory.p.smith'
closed = True
closed_date = <Date 2008-06-02.00:08:34.074>
closer = 'gregory.p.smith'
components = ['Interpreter Core']
creation = <Date 2008-04-08.15:55:09.533>
creator = 'jnferguson'
dependencies = []
files = []
hgrepos = []
issue_num = 2588
keywords = ['patch']
message_count = 12.0
messages = ['65174', '65184', '65186', '65226', '65227', '65228', '65242', '65246', '65254', '67399', '67620', '85969']
nosy_count = 5.0
nosy_names = ['gregory.p.smith', 'amaury.forgeotdarc', 'belopolsky', 'jnferguson', 'psss']
pr_nums = []
priority = 'normal'
resolution = 'fixed'
stage = None
status = 'closed'
superseder = None
type = 'security'
url = 'https://bugs.python.org/issue2588'
versions = ['Python 2.5']

Activity

  1. jnferguson commented on Apr 8, 2008

    jnfergusonmannequin
    MannequinAuthor

    The PyOS_vsnprintf() contains the caveat that the length parameter
    cannot be zero, however this is only enforced via assert() which is
    compiled out. As a result if the length parameter is zero then the
    function will underflow and write a null byte to invalid memory.

    53 int
    54 PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
    55 {
    56 int len; /* # bytes written, excluding \0 */
    57 #ifndef HAVE_SNPRINTF
    58 char *buffer;
    59 #endif
    60 assert(str != NULL);
    61 assert(size > 0);
    62 assert(format != NULL);
    [...]
    65 len = vsnprintf(str, size, format, va);
    [...]
    91 str[size-1] = '\0';
    92 return len;
    93 }

  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    and removed
    stdlibStandard Library Python modules in the Lib/ directory
    on Apr 8, 2008
  3. amauryfa commented on Apr 8, 2008

    @amauryfa
    Contributor

    I think that programming errors against the python API are best checked
    with asserts: I develop in development mode (with asserts enabled), then
    I want my released program to run at full speed.

    Other thoughts?

  4. jnferguson commented on Apr 8, 2008

    jnfergusonmannequin
    MannequinAuthor

    I can generally agree with that, and I admit I haven't verified all of
    the code paths here- theres several hundred of them leading into this
    function, are you positive all of them are safe? (seems like it would be
    easier to just move the check into an if than sitting down and verifying
    that all XXX hundred code paths are safe).

    In the other bug, I have verified code paths into it, for instance test
    the misallocation poc in 2586 as an example

  5. abalkin commented on Apr 9, 2008

    @abalkin
    Member

    As long as snprintf is used with a fixed size buffer using an idiom

    snprintf(buffer, sizeof(buffer), ..)

    there is no issue because sizeof(buffer) cannot be zero. AFAICT, this
    is how python uses PyOS_vsnprintf wrapper.

    On the other hand, may this is a good opportunity to revisit the
    decision to make PyOS_vsnprintf semantics different from C99 vsnprintf.

    C99 defines snprintf semantics as follows:

    int snprintf(char *restrict s, size_t n,
           const char *restrict format, ...);

    The snprintf() function shall be equivalent to sprintf(), with the
    addition of the n argument which states the size of the buffer referred
    to by s. If n is zero, nothing shall be written and s may be a null
    pointer. Otherwise, output bytes beyond the n-1st shall be discarded
    instead of being written to the array, and a null byte is written at the
    end of the bytes actually written into the array.

    <http://www.opengroup.org/onlinepubs/000095399/functions/printf.html\>

  6. jnferguson commented on Apr 9, 2008

    jnfergusonmannequin
    MannequinAuthor

    I do agree with your point about snprintf(..., sizeof(x), ...)-- my
    single biggest point (and maybe i'm just not seeing it), is that there
    appears to be no good reason for having this caveat and in turn its
    essentially just code waiting to break; with as commonly used of a
    function as it is, it's really a matter of when and not so much if.

    While no one seems to ever use it this way, don't forget that a good
    alternative to asprintf() is calling sprintf() with a length of zero to
    get the length (in compliant implementations), allocating the memory and
    then calling it again.

  7. abalkin commented on Apr 9, 2008

    @abalkin
    Member

    On Tue, Apr 8, 2008 at 9:21 PM, Justin Ferguson <report@bugs.python.org> wrote:

    ..
    While no one seems to ever use it this way, don't forget that a good
    alternative to asprintf() is calling sprintf() with a length of zero to
    get the length (in compliant implementations), allocating the memory and
    then calling it again.

    Remember that PyOS_vsnprintf was introduced back in 2001 when
    (according to the comments in the file) not all platforms provided c99
    compliant implementations. If you can verify that the situation has
    changes for the supported platforms, I think you will have a good case
    for making the wrapper c99 compliant.

  8. jnferguson commented on Apr 9, 2008

    jnfergusonmannequin
    MannequinAuthor

    Actually, I'm not sure things are any better today- even the same
    operating system but different versions have inconsistencies, for
    instance on some versions of RHEL the vsnprintf() can fail during
    unicode conversion. MSVCRT still returns -1 on truncation, et cetera.

    That said, theres plenty of other implementations that manage this
    without the potential of underflowing a buffer

  9. abalkin commented on Apr 9, 2008

    @abalkin
    Member

    On Wed, Apr 9, 2008 at 1:16 PM, Justin Ferguson <report@bugs.python.org> wrote:
    ..

    That said, theres plenty of other implementations that manage this
    without the potential of underflowing a buffer

    Do you have in mind something like the following?

    ===================================================================

    --- Python/mysnprintf.c (revision 62211)
    +++ Python/mysnprintf.c (working copy)
    @@ -88,6 +88,7 @@
            PyMem_FREE(buffer);
     Done:
     #endif
    -       str[size-1] = '\0';
    +       if (size > 0)
    +               str[size-1] = '\0';
            return len;
     }

    I would be +0 on such change.

  10. jnferguson commented on Apr 9, 2008

    jnfergusonmannequin
    MannequinAuthor

    Yep, that works for me.

  11. self-assigned this
    on May 25, 2008
  12. gpshead commented on May 26, 2008

    @gpshead
    Member

    Fixed in trunk r63734 using Alexander's suggested fix.

    I will backport this to release25-maint.

  13. gpshead commented on Jun 2, 2008

    @gpshead
    Member

    Fixed in release25-maint r63883.

  14. psss commented on Apr 14, 2009

    psssmannequin
    Mannequin

    Justin, is there any reproducer available for this issue?
    Thanks!

  15. transferred this issue fromon Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

interpreter-core(Objects, Python, Grammar, and Parser dirs)type-securityA security issue

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions