Segmentation fault in va_args parsing - c

Why does the code below gives EXC_BAD_ACCESS, could not access memory?
int combine_strings(char **outputStr,...)
{
va_list ap;
char *s, *out=0;
int len=0;
va_start(ap,outputStr);
while(s=va_arg(ap,char *))
{
len+=strlen(s);
}
va_end(ap);
if(!(out=malloc(len+1)))
exit(1);
*outputStr=out;
va_start(ap,outputStr);
while(s=va_arg(ap,char *))
{
len=strlen(s);
memcpy(out,s,len);
out+=len;
}
va_end(ap);
*out=0;
return 0;
}

I have to disagree with the other previous posters. The original code does not iterate over the same va_list twice. It creates two different ones and iterates over each of them in turn, even though the same variable is used to hold both lists.
In fact, I managed to run the function properly. Hence, my guess is that the problem is in how the function was called. Here is how I called it, note the trailing NULL and the setup of the output parameter:
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
// ... combine_strings() goes here...
int main()
{
char * res;
char * * output = &res;
combine_strings(output, "FOO", "BAR", "BAZ", NULL);
printf("%s\n", *output);
}
The code above outputs FOOBARBAZ as expected.

You cannot iterate over the same va_list twice. You need to create a copy using va_copy().
int combine_strings(char **outputStr,...)
{
va_list ap, ap2;
char *s, *out=0;
int len=0;
va_start(ap,outputStr);
va_copy(ap2, ap);
while(s=va_arg(ap2,char *))
{
len+=strlen(s);
}
va_end(ap2);
if(!(out=malloc(len+1)))
exit(1);
*outputStr=out;
while(s=va_arg(ap,char *))
{
len=strlen(s);
memcpy(out,s,len);
out+=len;
}
va_end(ap);
*out=0;
return 0;
}

Using va_start twice in one function is difficult to get to work on all platforms. See here for more information.
Probably best to use va_copy.

The easy way to answer this kind of question is to run it in a debugger. You'll get a full stack trace, code pointer, and you'll be able to look at the values of all the variables.
To use gdb, first compile the program with debugging symbols (-g in gcc). Then, run it:
gdb program_name
(gdb) run
It will crash and you'll be able to see why.

Related

why called function can get arguments of parent-function by va_start()?

I implemented a function with variable arguments below:
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#define ERR_BUFFER_SIZE 4096
void errPrint(const char *format, ...)
{
va_list argptr;
va_start(argptr, format);
char buf[ERR_BUFFER_SIZE];
vsnprintf(buf, sizeof(buf), format, argptr);
fprintf(stderr, "%s", buf);
va_end(argptr);
}
Then I hope to implement another named "errExit()" based on the function above.
I just tried like below.It works as I hoped but I dont't think it correct.
void errExit(const char *format, ...)
{
errPrint(format);
exit(EXIT_FAILURE);
}
I tried errExit("hello,%s,%s,%s", "arg1","arg2", "arg3"); and it printed "hello,arg1,arg2,arg3" correctly.
But after I added two line code like below, it throwed error Segmentation fault.
void errExit(const char *format, ...)
{
char buf[4096];//added
strcpy(buf, format);//added
errPrint(format);
exit(EXIT_FAILURE);
}
I am very confused.
According to what I learned:
There is only one argument format in the stack of called function errPrint(). I can't believe va_start() will get arguments from the its parent-function errExit().
Since errPrint() works "correctly", why it doesn't work after I add the two lines code? It seems that the code added have no effect.
(My English is not well, hoping you everyone can stand my statement. Thank you! )
In errExit() you need to pass the variable arguments to another function via a va_list argument. As you were not, vsnprintf will eventually crash trying to access non-existing arguments on the stack.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ERR_BUFFER_SIZE 4096
void verrPrint(const char *format, va_list ap) {
char buf[ERR_BUFFER_SIZE];
vsnprintf(buf, sizeof(buf), format, ap);
fprintf(stderr, "%s", buf);
}
void errPrint(const char *format, ...) {
va_list ap;
va_start(ap, format);
verrPrint(format, ap);
va_end(ap);
}
void errExit(const char *format, ...) {
va_list ap;
va_start(ap, format);
verrPrint(format, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
int main(void) {
errPrint("What is the meaning of %s\n", "life?");
errExit("%d\n", 42);
return 0;
}
Other answers explain what you need to do. This answer is about the asm details of why it happened to work.
In your first errExit, if you look at the compiler-generated assembly for x86-64, all the incoming args would still be in registers when calling errPrint(format);. Even though you don't tell the compiler to pass them, it doesn't do anything that uses registers before calling that other function.
So it happens to work even though you didn't explicitly pass along any args beyond the first (format). It compiles as if you'd written a function that passed along its first 6 args, assuming the x86-64 System V calling convention, and that the args were all integer or pointer (such as char*).
In the C abstract machine, your code is meaningless and has undefined behaviour when errPrint makes a va_list argptr and passes it on to a function (vsnprintf) that references more than 0 elements of that list.
It might help to look at asm for a function that does pass on multiple args:
void foo(char *a, char *b, char *c, char *d, char *f);
int bar(char *a, char *b, char *c, char *d, char *f)
{
foo(a, b, c, d, f);
return 1; // some code after the call, so it can't compile to a tailcall
}
On Godbolt, compiling for Linux with GCC12:
# GCC12 -O3
bar:
# incoming args are in RDI, RSI, RDX, RCX, R8, R9 in that order
# same place a callee will look for them, so passing them on is trivial
sub rsp, 8 # re-align the stack so RSP%16 == 0
call foo
mov eax, 1 # return-value register = 1
add rsp, 8 # restore the stack pointer
ret # pop return address into RIP
Your errExit has equivalent asm before the call, so if there are register args, they get passed on even though you didn't tell the C compiler about that.
errExit:
sub rsp, 8
xor eax, eax # Variadic functions get AL = # of args passed in XMM regs
call errPrint
mov edi, 1
call exit # GCC knows exit() is noreturn
If there was any code before call errPrint, such as a call to strcpy, that would of course step on those arg-passing registers. So they have different values when call errPrint runs, except for the one arg you told the C compiler about. It will save that in a call-preserved register across a call strcpy.
If you'd compiled this for a calling convention with fewer register args, like 32-bit x86 (gcc -m32) where there are only stack args, the pointers that vsnprintf references with %s conversions be from errExit's stack frame, above any args it intended to pass. So probably a stack slot of padding for alignment, then in a debug build maybe a saved EBP, maybe even errExit's own return address.
If you're curious, use %p conversions to print the pointer values instead of trying to dereference and segfaulting.
C doesn't have a way to specify that it should pass on only the register args; if you want to pass on an unknown number of args from a ..., you have to do it with a va_list which works for any number of args, including 7 or more on x86-64 System V. That's a good thing because some targets wouldn't have any register args, so just saving/restoring the register args would pass on 0 args.
I'm showing asm only to understand why one version happened to work, not as a suggestion for anything you can do to write safe and portable C that can compile to asm like this, even if there's no other work before the call errPrint.
Thank you everyone.I have learned your from your answers.
I choose to change my code like below.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ERR_BUFFER_SIZE 4096
void errPrint(const char *format, va_list arg_list)
{
char buf[ERR_BUFFER_SIZE];
vsnprintf(buf, sizeof(buf), format, arg_list);
fprintf(stderr, "%s", buf);
}
void errExit(const char *format, va_list arg_list)
{
errPrint(format, arg_list);
exit(EXIT_FAILURE);
}
void v_exec(void (*func)(const char *, va_list), const char *format, ...)
{
va_list arg_list;
va_start(arg_list, format);
func(format, arg_list);
va_end(arg_list);
}
int main(void)
{
v_exec(errExit,"%s%d", "hello",520);
return 0;
}

How to customize the C printf function in such a way that it automatically adds a message log?

I'm developping a C project with different kind of users. For logging and debugging I would really appreciate a customized printf function that just adds at the beginning of the messages a label that indacates the type of users. Is it possible without rewriting the printf function from scratch?
Here's a crude example. my_print() is called in the same way as printf(), but it produces some output before processing the arguments. Of course, what that additional output is and where it comes from is something that needs to be determined in a specific application.
The significant fact is that we can provide a printf-like function without reimplementing printf().
#include <stdarg.h>
#include <stdio.h>
void my_printf (const char *fmt,...)
{
va_list ap;
va_start (ap, fmt);
printf ("MY INFO... ");
vprintf (fmt, ap);
va_end (ap);
}
int main (int argc, char **argv)
{
my_printf ("The answer is %d\n", 42);
return 0;
}

How to use macro for calling function?

I want to call function according to func_name string.
My code is here below:
#define MAKE_FUNCNAME func_name##hello
void call_func(void* (*func)(void))
{
func();
}
void *print_hello(void)
{
printf("print_hello called\n");
}
int main(void)
{
char func_name[30] = "print_";
call_func(MAKE_FUNCNAME);
return 0;
}
But this code doesn't work. I want code to work like call_func(print_hello). But preprocessor treated my code like call_func("print_hello"). How to use macro in C to make my exception? Or is it not possible using C?
Then problem with your code is that the value of func_name is only known at run-time.
You can however to it like this:
#define MAKE_FUNCNAME(FUNCNAME) FUNCNAME##hello
void call_func(void* (*func)(void))
{
func();
}
void *print_hello(void)
{
printf("print_hello called\n");
}
int main(void)
{
call_func(MAKE_FUNCNAME(print_));
return 0;
}
But it is not possible to use a string value within macro parameters like in your code snippet.
If you want to get call functions with their names using string values you can use a table to store function pointer with function names like this:
struct {
const char *name;
void (*ptr)(void);
};
You can use an array of this structure to find out the function pointer at run-time using a string value. This is the most common solution to using run-time strings to call functions using their names.
You can't do that. The value of func_name is known at run-time (even though it is a const char *), while you want to determine what to call at precompile-time. You should turn your cpp macro into something different (such as an if/switch statement or using an indirection).
Maybe you could have a look to dlsym().
Not sure I really understand the question, but if you want to "build" the function name at runtime and then call the corresponding function, it should be possible with dlsym()
/* compile with: gcc example.c -ldl -rdynamic */
#include <dlfcn.h>
#include <stdio.h>
int print_hello(void)
{
return printf("hello\n");
}
int main(int argc, char *argv[])
{
const char *name = "print_hello";
if (argc == 42)
print_hello(); /* for compiler not to remove print_hello at
* compile time optimisation in this example*/
void *handle = dlopen(NULL /* self */, RTLD_NOW);
int (*f)(void) = dlsym(handle, name);
f();
return dlclose(handle);
}

Good Verbosity Macro (C99)

I'm looking to write what I would imagine is a fairly common macro. I want to emulate the repeated "-v" options on many POSIX programs by defining a bunch of macros of the following form:
#define V1(str, ...) if(optv >= 1){printf("%s: "str,prog,__VA_ARGS__);}
int main(int argc, char* argv[])
{
// ... stuff ...
int i = 1;
V1("This contains a variable: %d\n",i);
}
// Output:
// ./program: This contains a variable: 1
where optv counts the number of "-v" options found on the command line and prog contains the program name (neither shown). This works well, but the problem is that I have to use a variable. V1("Output") will generate a compiler error. I could always use V1("Output%s","") but there should be a cleaner solution.
The GNU C preprocessor has a special feature that lets you delete the trailing comma when there are no arguments filling the variadic portion by prepending the token-pasting operator ## to __VA_ARGS__:
#define V1(str, ...) if(optv < 1); else printf("%s: "str,prog, ## __VA_ARGS__)
Alternatively, if you wish to remain fully C99 compliant, you could incorporate the the format string parameter into the ellipsis, but in this instance you'll also need to refactor your code since you want to include the extra prog parameter between the format string and the varargs. Something like this might work:
#define V1(...) if(optv < 1); else myprintf(prog, __VA_ARGS__)
int myprintf(const char *prog, const char *fmt, ...)
{
// Print out the program name, then forward the rest onto printf
printf("%s: ", prog);
va_list ap;
va_start(ap, fmt);
int ret = vprintf(fmt, ap);
va_end(ap);
return ret;
}
Then, V1("Output") expands to myprintf(prog, "Output") without using any non-C99 compiler extensions.
EDIT
Also note that I inverted the if condition in the macro, due to some weird issues that can arise if you invoke the macro inside an if statement without braces—see this FAQ for a detailed explanation.
Why don't you use 2 different macros for each verbosity level; one which prints a message and variable, and one which just prints a message?
You should probably write yourself a small support function so that you can do the job cleanly:
extern void vb_print(const char *format, ...);
#define V1(...) do { if (optv >= 1) vb_print(__VA_ARGS__); } while (0)
I assume that both optv and prog are global variables. These would go into a header (you wouldn't write them out in the programs themselves, would you?).
The function can be:
#include <stdio.h>
#include <stdarg.h>
extern const char *prog;
void vb_print(const char *format, ...)
{
va_list args;
va_start(args, format);
printf("%s:", prog);
vprintf(format, args);
va_end(args);
}
There's no rocket science in there. You can tweak the system to your heart's content, allowing a choice of where the information is written, flushing the output, ensuring there's a newline at the end, etc.
Try this:
#define V1X(str, ...) if(optv >= 1) {printf("%s: "str,prog,__VA_ARGS__);} else
#define V1(...) V1X(__VA_ARGS__,0)
I believe that fixes the problem you described, and the else at the end fixed another problem.

Pass format string through procedure to fprintf

I'm working on a program that generates an FDF file for filling in an Adobe PDF form. Currently, I have various procedures that are called, as before each one terminates, it opens the FDF file, writes its particular data, and closes the file.
I'm trying to put the code for writing the data to the FDF file in a separate procedure, and then have the other procedures call it. This would allow for a greater separation of the code by task, and if I had to change it in the future, I could do so easily.
I'm having some problems though, as I need to pass varying amounts of variables to fprintf. That is, I used to have a line like this:
fprintf(fp,"<</T(f1_44(0))/V(%d)>>\n",wages);
I wrote the below function, but I can't get it to work:
void writeToFDF(FILE *fp, char *filename, char *data, char *arg)
{
/* independent method for writing to FDF file */
/* open file */
fp=fopen(filename,"a");
fprintf(fp,data,arg);
/* close file */
fclose(fp);
}
So in theory, I would then in the parent function call:
writeToFDF(fp,"1040.fdf","<</T(f1_44(0))/V(%d)>>\n",wages);
I'm getting errors right now, and I believe it has to do with the last part of the function's arguments. Also, how can I get it to work if I have multiple arguments?
Thank you.
You need vfprintf. It will accept a variable list of arguments.
Like this:
void writeToFDF(FILE *fp, char *filename, char *data, ...)
{
va_list pl;
/* independent method for writing to FDF file */
/* open file */
fp=fopen(filename,"a");
va_start (pl, data);
vfprintf(fp,data,pl);
/* close file */
fclose(fp);
va_end(pl);
}
EDIT: while I tried to stay as close to your original code, ephemient's solution that omits the FILE *fp argument is in fact better: fp has only local significance (it's opened and closed in this function) so it should not be passed around.
This is what vfprintf is for.
#include <stdarg.h>
#include <stdio.h>
void writeToFDF(const char *filename, const char *format, ...) {
va_list ap;
FILE *fp = fopen(filename, "a");
va_begin(ap, format);
vfprintf(fp, format, ap);
va_end(ap);
fclose(fp);
}
You will need to use a Variadic function. See Forward an invocation of a variadic function in C
#include <stdio.h>
#include <stdarg.h>
void foo(FILE *file, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(file, fmt, ap);
va_end(ap);
}

Resources