Wednesday, January 5, 2022

[SOLVED] Segmentation fault after first call to dprintf() or vdprintf()

Issue

I am using Ubuntu 20.04.3 on a Oracle Virtual Box.

I have few calls to printf() functions that use file descriptors, but get segmentation fault after the second call. I tried fsync(fd) and fdatasync(fd) but it didn't solve the problem:

logFileFd = open("./logFile.log", O_APPEND);

if (logFileFd == -1)
{
    printf("\ncan't create file %s :[%s]", logFileName, strerror(err));
    exit(ERROR_OPEN_LOG_FILE);
}

va_start(a_list, format);
sprintf(logStr, "%s, %s, INFO, %s, %d - ", __DATE__, __TIME__, __BASE_FILE__, __LINE__);
printf("%s", logStr);
dprintf(logFileFd, "%s", logStr);
vprintf(format, a_list);
vdprintf(logFileFd, format, a_list);
va_end(a_list);
close(logFileFd);

the segmentation fault occurs at the line: vdprintf(logFileFd, format, a_list);

Can any one assist why is that?

Thanks a lot !


Solution

Quoting man page for vfprintf(3):

The functions vprintf(), vfprintf(), vsprintf(), vsnprintf() are equivalent to the functions printf(), fprintf(), sprintf(), snprintf(), respectively, except that they are called with a va_list instead of a variable number of arguments. These functions do not call the va_end macro. Because they invoke the va_arg macro, the value of ap is undefined after the call.

Additionally on the man page for stdarg(3) you can read that:

If ap is passed to a function that uses va_arg(ap,type) then the value of ap is undefined after the return of that function.

The problem in your code is that you are using va_list a_list twice - first in call to vprintf(), then in call to vdprintf(). After the first call, a_list value is undefined.

Man stdarg(3) states that "Multiple traversals of the list, each bracketed by va_start() and va_end() are possible". Try applying following modification:

sprintf(logStr, "%s, %s, INFO, %s, %d - ", __DATE__, __TIME__, __BASE_FILE__, __LINE__);
printf("%s", logStr);
dprintf(logFileFd, "%s", logStr);

va_start(a_list, format);
vprintf(format, a_list);
va_end(a_list);

va_start(a_list, format);
vdprintf(logFileFd, format, a_list);
va_end(a_list);

Additionally, please review the flags used in call to open(). According to man page open(2):

The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR

I would propose to apply following modification:

logFileFd = open("./logFile.log", O_WRONLY | O_APPEND);


Answered By - trdo