How do you call vsnprintf() safely?

后端 未结 3 636
借酒劲吻你
借酒劲吻你 2021-01-14 02:06

I\'m porting some very old (> 10y) C code to modern Linuxes. I\'m getting segmentation faults within a custom-written vsnprintf() wrapper (apparently its task is to detect d

相关标签:
3条回答
  • 2021-01-14 02:23

    This is the correct way to use snprintf and vsnprintf on every operating system except SunOS 4 (which has been obsolete for 20 years), so your problem is somewhere else.

    I'll make a pure guess and say that I'm almost certain that your problem is that you're passing the va_list ap into vsnprintf which consumes it and then you expect it to be reset on the next call. This is incorrect and has stopped working in gcc many years ago (because it only worked on certain architectures).

    Change:

    n = vsnprintf(p, size, fmt, ap);
    

    To:

    va_list apc;
    va_copy(apc, ap);
    n = vsnprintf(p, size, fmt, apc);
    va_end(apc);
    

    And see if that helps.

    Here's a simple test to see what's going on:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    
    void
    foo(const char *fmt, va_list ap)
    {
    #ifdef BAD
        vprintf(fmt, ap);
    #else
        va_list apc;
        va_copy(apc, ap);
        vprintf(fmt, apc);
        va_end(apc);
    #endif
        vprintf(fmt, ap);
    }
    
    void
    bar(const char *fmt, ...)
    {
        va_list ap;
        va_start(ap, fmt);
        foo(fmt, ap);
        va_end(ap);
    }
    
    int
    main(int argc, char **argv)
    {
        bar("foo %s\n", "bar");
        return 0;
    }
    

    When run I get this:

    $ cc -o foo foo.c && ./foo
    foo bar
    foo bar
    $ cc -DBAD -o foo foo.c && ./foo
    foo bar
    foo ����
    
    0 讨论(0)
  • 2021-01-14 02:38

    Unsure on yours, but my man page on variable argument lists says:

    COMPATIBILITY
    These macros are not compatible with the historic macros they replace. A backward compatible version can be found in the include file <varargs.h>.

    As you said it is very old code, maybe the va_list received in this routine is not the va_list expected by vsnprintf. You should first try to extract all parameters with one header then the other to be sure (normally vsnprintf is stdarg.h compatible)

    0 讨论(0)
  • 2021-01-14 02:43

    As I understand the code, its purpose is to detect the size needed by sprintf to fully write the output string in the buffer. There is a function that do this for you: asprintf (or vasprintf here).

    Prototype:

    int vasprintf(char **strp, const char *fmt, va_list ap);
    

    Just use it as follow:

    String strVPrintf(const String fmt, va_list ap)
    {
        char *ans;
        int n;
        n = vasprintf(&ans, fmt, ap);
        // do the checks
        return ans;
    }
    

    With this function, you do not need this wrapper any more, I think.

    0 讨论(0)
提交回复
热议问题