The following function MsCommand_push
does not work as expected when compiled with Apple clang version 15.0.0 (clang-1500.3.9.4)
. It is supposed to take as input a variable number of pointer to char (pointing to null-terminating strings) and append them to a buffer. It is part of a command line interpreter and its purpose is to fill a buffer with a string that will be used to launch processes.
The function uses the stdarg.h
types and macros. It works as expected on Linuces (compiled with gcc), Windows (compiled with gcc), macOS x86-64 (compiled with clang). It has been working as expected for decades under those environments and others such as SunOS and other UNIX flavors.
On macOS arm64, compiled with clang 15, the assignment to str
sometimes does not retrieve the first element of the remaining parameters.
/* global variables accessed in the function */
static char *cmd_buffer = NULL;
static int buffer_size = 0;
static int cmd_len = 0;
static int free_len = 0;
static const int init_buffer_size = 1024;
/* typical call: MsCommand_push(1, "cp "); */
void MsCommand_push(unsigned nb, ...)
{
int new_len = 0;
const char *str = NULL;
char *tmp = NULL;
unsigned int i = 0;
va_list param;
va_list param2;
va_start(param, nb);
va_copy(param2,param);
for (i=0; i < nb; i++)
{
str = va_arg(param, char *); /* <--- va_arg returns garbage in first iteration! */
new_len = new_len + strlen(str);
}
va_end(param);
if (free_len < new_len)
{
char *tmp = cmd_buffer;
cmd_buffer = calloc(cmd_len + result, sizeof(char));
memcpy(cmd_buffer, tmp, cmd_len * sizeof(char));
buffer_size = cmd_len + result;
free_len = result;
}
ASSERT(cmd_len > 0);
tmp = cmd_buffer + cmd_len - 1;
str = NULL;
for (i=0; i < nb; i++)
{
str = va_arg(param2, char *);
sprintf(tmp, "%s", str);
tmp = tmp + strlen(str);
}
va_end(param2);
cmd_len = new_len + cmd_len;
free_len = free_len - new_len;
}
/* function called before MsCommand_push */
void MsCommand_init() {
ASSERT((cmd_buffer == NULL) && (init_buffer_size > 0));
cmd_buffer = (char *)malloc(init_buffer_size);
free_len = init_buffer_size - 1;
cmd_len = 1;
cmd_buffer[0] = '\0';
buffer_size = init_buffer_size;
}
To make the problem more self-contained and easier to reproduce, I changed the code so that the faulty behavior occurs in a much more simple context than in the actual cases.
To do so, I wrote the following main
function:
int main(int argc,
char **argv)
{
extern int MsFile_dummy1();
extern void MsCommand_push(unsigned nb, ...);
extern void MsCommand_init();
MsCommand_init();
MsCommand_push(1, "cp ");
if (MsFile_dummy1() == 1) {
return 1;
}
return 0;
}
The call to MsCommand_push
from main
works as expected.
However the call through MsFile_dummy1()
falls into the anomalous behavior. This function is just a proxy to include the call:
In another file, we have
int MsFile_dummy1() {
MsCommand_push(1, "cp ");
return 0;
}
What could be wrong and how could I fix this issue?
Note: I found a 10-year old discussion on this topic and I suppose it is not up-to-date.
The cause for this problem is that the declaration of the variadic function MsCommand_push
is not visible when function MsFile_dummy1
is compiled.
The root cause is that the proper header file had not been included... and the compiler flags included -Wno-implicit-function-declaration
.
Thank you for all the helpful comments that pushed me to do a deeper investigation of this problem.