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