I recently helped someone troubleshoot a bug and saw an interesting case. The following program will have Segmentation Fault, can you see the problem?
Variadic functions in C are implemented as
va_end defined by
<stdarg.h>. Here is a simple example.
va_list is an “implementation-defined” data structure. Its function is equivalent to an iterator of function parameters. We must first initialize the start of the iterator with
va_start, then read each parameter in turn with
va_arg, and finally release all the resources required by
va_list itself can also be passed as an argument to other functions. Commonly used for
printf functions starting with
v, such as
Go back to the code at the beginning of this article. The code wants to first call
vsnprintf to calculate the amount of memory it needs, then allocate enough memory, and then call
vsnprintf again to convert
args into strings.
The root of the problem is that the
args iterator comes to the end of the
vsnprintf function when it is first called. the C language standard has the following notation for the behavior of the
- As the functions
va_argmacro, the value of
argafter the return is indeterminate. – C 11 (N1570), p. 327
In other words, if you read the function argument from the same
va_arg after the
vsnprintf return, the value of
va_arg after the return is indeterminate. In my test environment,
va_arg(args, const char *) returns the wrong address, which leads to a Segmenetation Fault.
How to fix it
Because we have to access the arguments twice, we should make a copy of
va_copy before accessing the arguments for the first time. Example.
Alternatively, if you can use the GNU extension function (compiled with the definition
_GNU_SOURCE), we can call the
vasprintf function directly.
vasprintf will directly calculate the required memory space, configure the memory, and output the string.
However, the actual code I encountered was not configured with
malloc memory, so I did not use this modification.