This is a minimal reproduction of the warnings I see. Obviously UB can be bad, but I think while many of the below situations are okay, there's some really nasty uses and I need to determine which require corrective action.
#include <stdarg.h>
#include <stdio.h>
#include <limits.h>
typedef struct _thing {
char first[4];
char second[10];
char last[111];
}THING;
void custom_printf(char* _format, ...) __attribute__((format(printf, 1,2)));
void custom_printf(char* _format, ...)
{
// get buffer from some source
char buffer[1024];
va_list ap;
va_start(ap, _format);
vsnprintf(buffer, 1024, _format, ap);
va_end(ap);
// use buffer for some purpose
}
int main(){
custom_printf("HI THERE%d");
custom_printf("HI THERE", 1);
custom_printf("val: %d", (void*)0);
custom_printf("val: %p", 0);
custom_printf("val: %lld", 1);
custom_printf("val: %s", (THING){"A", "AA", "CCCC"});
custom_printf("val: %0.30s","HI");
custom_printf("val: %d",LLONG_MAX);
}
The warnings see include:
<source>: In function 'main':
<source>:26:5: warning: format '%d' expects a matching 'int' argument [-Wformat]
<source>:27:5: warning: too many arguments for format [-Wformat-extra-args]
<source>:28:5: warning: format '%d' expects argument of type 'int', but argument 2 has type 'void *' [-Wformat]
<source>:29:5: warning: format '%p' expects argument of type 'void *', but argument 2 has type 'int' [-Wformat]
<source>:30:5: warning: format '%lld' expects argument of type 'long long int', but argument 2 has type 'int' [-Wformat]
<source>:31:5: warning: format '%s' expects argument of type 'char *', but argument 2 has type 'THING' [-Wformat]
<source>:32:5: warning: '0' flag used with '%s' gnu_printf format [-Wformat]
<source>:33:5: warning: format '%d' expects argument of type 'int', but argument 2 has type 'long long int' [-Wformat]
<source>:34:1: warning: control reaches end of non-void function [-Wreturn-type]
Compiler returned: 0
It's my understanding that the above has many flavors of UB here. After looking around I've seen that I should just fix the above. Now I want to eventually fix them all, but for now my curiousity is making me wonder which is the worst scenario. I'd assume that cases like the first where I'm not passing in enough items.
It's my understanding that in the above I have:
- Popping off stack that doesn't exist
- Not popping enough off the stack
- Padding a string with leading zeros
- Casting integer to pointer
- Casting a struct that can be cased to
Out of the above I'm fairly certain that anything that pops off the stack that doesn't exist will lead to the worst scenario. But I'm also wondering what the other severe cases are.