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.
Related
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.
Assume, I have one line bash script that executes everything it has in arguments
#!/bin/bash
$1
So, the command ./one_line_script.sh "ls -lh" works fine.
Also I have C code, that accepts arguments and send them to my bash script
int main (int argc, char* argv[])
{
char command[1000];
sprintf(command, "/path/to/one_line_script.sh %s", argv[1]);
system(command);
return 0;
}
And here I've got a problem, because ./c_program "ls -lh" returns only ls output. It doesn't understand few arguments. How do I need to modify my C code, so it could accept few arguments?
I would recommend to use fork and exec directly to avoid quoting issues altogether. Consider for example what happens if a ' is contained within an argument - when doing sprintf cmd-line butchering this leads to broken command lines.
int pid = fork();
if(pid == 0) {
execl("/bin/sh", "sh", "-c", arg1, arg2, arg3, 0);
} else {
int status=0;
waitpid(pid, &status, 0);
}
You need to quote it in your sprintf too, or bash will only receive one argument :)
sprintf(command, "/path/to/one_line_script.sh '%s'", argv[1]);
I've added quotes (') around the %s.
You can also use $# instead of $1 in your bash script, so it will take all arguments passed to it.
Try:
int i;
command[0] = '\0';
for (i = 1; i < argc; i++)
strcat (command, argv[i]);
system (command);
This should work, but please be aware that it has a lot of security hazards: first of all executing any possible command you get on the command line might allow users to do things they normally aren't allowed to (don't setuid your program!). Then the buffer might easily overflow and allow all kinds of stack smashing. So I'd say: only use this program as a learning tool, to learn manipulation of argc/argv and to begin thinking about security. Don't even compile it!
I have one problem with variable length argument debug log print function. I will just simulate the code here.
void SecondPrint(int level, const char* format,...)
{
//Printing the log here
va_list arg;
va_start(arg, format);
vprintf(format, arg);
va_end(arg);
}
void FirstPrint(int level, const char* format,...)
{
SecondPrint(level,format);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
"SecondPrint" is supposed to print "100 Error message" as expected, But its not printing like that its printing " Error message".
I am not getting whats wrong with this one. I am suspecting the way of calling "SecondPrint" from "FirstPrint" function. FirstPrint is receiving remaining arguments through ... but its invoking SecondPrint with level and format arguments.
I can't use SecondPrint from main directly. I have to use FirstPrint and FirstPrint has to call SecondPrint to print the log. So how can I achieve this one.. I thought to use VA_ARGS but it is for only macro definitions not in function definition.
And one more thing I can't do like *#define FirstPrint(a,b,...) SecondPrint(a,b,...)*
any help is highly appreciated thanks in advance.
C varargs are not designed to be passed more than one level; the kind of necessary stack manipulation required is too deep for the language. Typically, in a case like this, you would have a version of SecondPrint analagous to vprintf -- SecondPrintV or similar, and you would have FirstPrint invoke SecondPrintV after extracting the varargs, rather than invoking SecondPrint directly (and, for consistency, usually have SecondPrint invoke SecondPrintV internally).
You need to pass a va_list to SecondPrint, so it can access the arguments to FirstPrint
void SecondPrint(int level, const char* format, va_list args)
{
//Printing the log here
vprintf(format, arg);
}
void FirstPrint(int level, const char* format,...)
{
va_list args;
va_start(args, format);
SecondPrint(level, format, args);
va_end(args);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
It may be possible to do it in specific platform-specific ways, or (since the arguments are exactly the same in the same order) use assembly to "replace" the current function frame with the function to call. GCC also has an extension to do it with the __builtin_apply_args() and __builtin_apply() functions (although you must somehow figure out how to compute the size of the stack argument data).
(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)
I'm writing an assignment which involves adding some functionality to PostgreSQL on a Solaris box. As part of the assignment, we need to print some information on the client side (i.e.: using elog.)
PostgreSQL already has lots of helper methods which print out the required information, however, the helper methods are packed with hundreds of printf calls, and the elog method only works with c-style strings.
Is there I way that I could temporarily redirect printf calls to a buffer so I could easily send it over elog to the client?
If that's not possible, what would be the simplest way to modify the helper methods to end up with a buffer as output?
If you define your own version of printf and link to it prior to the libc version, your version will supersede the standard version.
You should also be able to supersede the standard version by using LD_PRELOAD to load a library that has printf defined.
To write your own printf, you will want to use stdarg functionality:
int printf(const char *fmt, ...)
{
int rv;
va_list ap;
va_start(ap, fmt);
if (redirect_printf)
{
#ifdef HAVE_VLOG
// If you have a vlog function that takes a va_list
vlog(fmt, ap);
rv = ...;
#else
char buffer[LARGESIZE];
rv = vsnprintf(buffer, sizeof(buffer), fmt, ap);
log(buffer);
#endif;
}
else
{
rv = vprintf(fmt, ap);
}
return rv;
}
This simple version will truncate data when the final formatted output is greater than LARGESIZE. If you don't want that, you can also call vsnprintf first with a NULL buffer to get the final size, do a dynamic allocation and then a second call to vsprintf to format the buffer.
You're wrong — elog supports format strings just like printf. Here's an example from Postgres source code:
elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);
So all you need is to add elog where there is printf using the same parameters.
The simplest way is to modify the helper methods to call sprintf(). Whether or not you can hack that in easily, I don't know. Maybe
#define printf(...) sprintf(buffer, __VA_ARGS__)
Will do it for you. You'll still need to define buffer for each helper function, and get its contents returned to whoever cares about them.
If you can tolerate the use of a temporary file you could redirect standard out with the freopen() call:-
newstdout = freopen("/tmp/log", "w", stdout);
This will force all the printf's to be written to /tmp/log instead of the console output. At some convenient point later in your program you could then open the same file for reading:-
readfd = fopen("/tmp/log", "r");
and forward the contents that have been added using something like this:-
void forward_to_elog(void)
{
int bytesread;
char buf[100];
memset(buf,0,100);
do {
memset(buf,0,100);
bytesread = fread(buf, sizeof(buf)-1, 1, readfd);
/* call elog(buf) */ ;
} while(bytesread);
}
If you keep the file open you can call forward_to_elog() multiple times to incrementally forward the contents that have been added.
The tmpnam() function can be used to get a name for the temporary file if you don't want to have to statically code one.