Skip to content

Let's get rid of sprintf() #101703

Open
Open

Description

@gvanrossum

On macOS with Xcode 14 we now get warnings like these:

Objects/unicodeobject.c:736:16: warning: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Wdeprecated-declarations]

While we can prove to ourselves that these specific sprintf() calls are safe, the warnings are annoying, but disabling them would also disable other, more useful warnings (or so @Yhg1s tells me on Discord). So let's just switch these to snprintf().

Linked PRs

Activity

  1. terryjreedy commented on Feb 8, 2023

    @terryjreedy
    Member

    There are 49 hits in our .c code + some in externals.

  2. added 2 commits that reference this issue on Feb 9, 2023
  3. serhiy-storchaka commented on Feb 9, 2023

    @serhiy-storchaka
    Member

    Interesting, there were issues with portability of snprintf (on Windows) in the past. I hope they were fixed in last 20 years.

  4. erlend-aasland commented on Feb 9, 2023

    @erlend-aasland
    Contributor

    Interesting, there were issues with portability of snprintf (on Windows) in the past. I hope they were fixed in last 20 years.

    CC. @eryksun

  5. eryksun commented on Feb 9, 2023

    @eryksun
    Contributor

    Supported builds of Python 3.5+ on Windows use Visual Studio 2015+ and the Universal C Runtime (UCRT), for which snprintf() and vsnprintf() conform to C99. In Python 3.9+, "Include/pyerrors.h" no longer defines these two functions as macros that call non-standard _snprintf() and _vsnprintf().

    PyOS_vsnprintf() in "Python/mysnprintf.c" was apparently overlooked when "pyerrors.h" was updated. It should have been modified to always call vsnprintf(). Also, all PyOS_snprintf() and PyOS_vsnprintf() calls can be replaced with snprintf() and vsnprintf().

  6. terryjreedy commented on Apr 24, 2023

    @terryjreedy
    Member

    I closed #103733 as duplicate of this.

  7. gvanrossum commented on Mar 19, 2024

    @gvanrossum
    MemberAuthor

    @terryjreedy @sobolevn Can this be closed? I haven't seen those warnings in a while, though I see plenty of sprintf() calls in the code still.

  8. terryjreedy commented on Mar 20, 2024

    @terryjreedy
    Member

    I have no involvement with C code, so fine with me. @serhiy-storchaka ?

  9. sobolevn commented on Mar 20, 2024

    @sobolevn
    Member

    I've tried to compile CPython with explicit -Wdeprecated-declarations set in PY_STDMODULE_CFLAGS and got no warnings. However, sprintf() is still widely used:

    » ag 'sprintf\('  
    Misc/HISTORY
    24555:  the standard sprintf() and vsprintf() C lib APIs, these versions
    33626:similar to sprintf() in C.  The right argument is either a single
    33627:value or a tuple of values.  All features of Standard C sprintf() are
    
    Python/flowgraph.c
    276:        sprintf(arg, "arg: %d ", i->i_oparg);
    279:        sprintf(arg, "target: %p [%d] ", i->i_target, i->i_oparg);
    
    Python/pystrtod.c
    1204:        exp_len = sprintf(p, "%+.02d", exp);
    
    Python/specialize.c
    368:        sprintf(buf, "%s%s.txt", dirname, hex_name);
    
    Objects/typeobject.c
    5591:        sprintf(msg, "type_traverse() called on non-heap type '%.100s'",
    
    Objects/unicodeobject.c
    789:        size = sprintf(str, "&#%d;", PyUnicode_READ(kind, data, i));
    2596:                    sprintf(buffer, fmt, va_arg(*vargs, long)) :
    2597:                    sprintf(buffer, fmt, va_arg(*vargs, unsigned long));
    2601:                    sprintf(buffer, fmt, va_arg(*vargs, long long)) :
    2602:                    sprintf(buffer, fmt, va_arg(*vargs, unsigned long long));
    2606:                    sprintf(buffer, fmt, va_arg(*vargs, Py_ssize_t)) :
    2607:                    sprintf(buffer, fmt, va_arg(*vargs, size_t));
    2610:                len = sprintf(buffer, fmt, va_arg(*vargs, ptrdiff_t));
    2614:                    sprintf(buffer, fmt, va_arg(*vargs, intmax_t)) :
    2615:                    sprintf(buffer, fmt, va_arg(*vargs, uintmax_t));
    2619:                    sprintf(buffer, fmt, va_arg(*vargs, int)) :
    2620:                    sprintf(buffer, fmt, va_arg(*vargs, unsigned int));
    2672:        len = sprintf(number, "%p", va_arg(*vargs, void*));
    8358:            sprintf(buffer, "&#%d;", (int)PyUnicode_READ_CHAR(unicode, collpos));
    
    Objects/bytesobject.c
    269:                sprintf(buffer, "%ld", va_arg(vargs, long));
    272:                sprintf(buffer, "%zd", va_arg(vargs, Py_ssize_t));
    275:                sprintf(buffer, "%d", va_arg(vargs, int));
    283:                sprintf(buffer, "%lu", va_arg(vargs, unsigned long));
    286:                sprintf(buffer, "%zu", va_arg(vargs, size_t));
    289:                sprintf(buffer, "%u", va_arg(vargs, unsigned int));
    296:            sprintf(buffer, "%i", va_arg(vargs, int));
    302:            sprintf(buffer, "%x", va_arg(vargs, int));
    328:            sprintf(buffer, "%p", va_arg(vargs, void*));
    564:/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) */
    
    Parser/string_parser.c
    134:                sprintf(p, "\\U%08x", chr);
    
    Programs/_freeze_module.c
    124:    sprintf(filename, "<frozen %s>", name);
    
    Modules/_testexternalinspection.c
    234:    sprintf(maps_file_path, "/proc/%d/maps", pid);
    
    Modules/_pickle.c
    2110:            sprintf(pdata, "%c%ld\n", INT,  val);
    
    Modules/unicodedata.c
    1329:        sprintf(buffer, "CJK UNIFIED IDEOGRAPH-%X", code);
    
    Modules/_testcapimodule.c
    991:        sprintf(buffer, "%s module: \"%s\" attribute: \"%s\"", \
    
    Modules/getnameinfo.c
    142:        sprintf(numserv, "%d", ntohs(port));
    
    Modules/_datetimemodule.c
    1629:        sprintf(freplacement, "%06d", TIME_GET_MICROSECOND(object));
    1631:        sprintf(freplacement, "%06d", DATE_GET_MICROSECOND(object));
    1633:        sprintf(freplacement, "%06d", 0);
    
    Modules/_ctypes/stgdict.c
    651:            sprintf(buf, "%s:%s:", fieldfmt, fieldname);
    
    Modules/_ctypes/_ctypes.c
    398:                sprintf(buf, "%zd,", shape[k]);
    400:                sprintf(buf, "%zd)", shape[k]);
    2538:    cp += sprintf(cp, "%x", Py_SAFE_DOWNCAST(index, Py_ssize_t, int));
    2547:        cp += sprintf(cp, ":%x", Py_SAFE_DOWNCAST(target->b_index, Py_ssize_t, int));
    3269:        sprintf(mangled_name, "_%s@%d", name, i*4);
    4768:    sprintf(name, "%.200s_Array_%Id",
    4771:    sprintf(name, "%.200s_Array_%ld",
    
    

    Not sure why this warning is gone.

  10. erlend-aasland commented on Mar 20, 2024

    @erlend-aasland
    Contributor

    Well, we risk that when the next version your toolchain of choice is updated, the warnings will reappear. IMO we might as well fix them. @sobolevn's PR was a good start, but only needed to be split in more manageable chunks.

  11. sobolevn commented on Mar 20, 2024

    @sobolevn
    Member

    I can redo some parts of it, there were rather easy. I'll start with them today.

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

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions