Problems with C code for buffer overflow - c

I'm writing a small tcp echo server for testing buffer overruns on Linux. I have two slightly different versions of the server code. When an over sized buffer is sent the the first it overflows as expected in the read function causing a Segmentation Fault. For the second version of the code I added a While (1) loop around the accept, read, and write functions so that the server will not exit under normal use, however when the same buffer is sent to the second server there is no overflow and the server does not crash at all. I'm having trouble figuring out why, the code is identical short of the while loop. Any help would be very appreciated. :)
SERVER 1
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen;
char recv[512];
bzero(recv,512);
struct sockaddr_in serv_addr, cli_addr;
if (argc < 2) exit(1);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) exit(1);
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) exit(1);
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) exit(1);
int n = read(newsockfd,recv,1024);
if (n < 0) exit(1);
write(newsockfd,recv,n);
close(newsockfd);
return 0;
}
SERVER 2
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen;
char recv[512];
bzero(recv,512);
struct sockaddr_in serv_addr, cli_addr;
if (argc < 2) exit(1);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) exit(1);
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) exit(1);
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) continue;
int n = read(newsockfd,recv,1024);
if (n < 0) continue;
write(newsockfd,recv,n);
close(newsockfd);
}
return 0;
}

Buffer overflows cause the stack to be overwritten, in particular the return address from a function. The actual overflow itself isn't what causes the segmentation fault, it's that when you later on return from the function that had the overflow, the return address has been corrupted. (Other possible segfaults from a buffer overflow include accessing memory from overwritten pointers, or using function pointers that have been overwritten, etc).
In your example, the while loop is preventing you from ever reaching the return statement, so while your buffer is being overflowed and your return address clobbered, that return address is never used, so the segfault doesn't occur.
If you want to verify that the overflow is occurring, I would recommend either watching in a debugger, or printing out the values inside the serv_addr and cli_addr structures, which I would expect would be clobbered by your overflow.
Also if you want to see the segfault from overflow, move the recv call and its destination buffer into a separate function, then call that function from inside the while(1) loop. The segfault should occur when the function with recv in it returns.

You cannot predict what your program will do when there is a buffer overflow. The behavior depends on what happens to be after the buffer and exactly what's in the overly-long input. Those things may depend on unrelated parts of your program (what addresses things are compiled at), and possibly even things like load addresses that change from run to run.

Related

How to convert a simple client server TCP program into non blocking one

Hi I was reading about non-blocking calls using select() from Beej's guide, but I'm still confused as to how to change my simple client-server code to one that is non blocking. Could anyone tell me what changes do I need to make in the server code as well as the client code for that?
Here's the server code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
close(sockfd);
return 0;
}
Here is the client code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
close(sockfd);
return 0;
}
The way select(2) makes things non-blocking is by waiting for I/O to become possible without blocking (e.g., when new data is available). When using select(), you wouldn't normally have to put the monitored descriptors into non-blocking mode (with e.g. SOCK_NONBLOCK), so in a sense select() is specifically about avoiding having to use non-blocking I/O.
select() is used to wait for events on multiple descriptors at once. An event here is something that would make it possible to read(2) from or (depending on how you use select()) write(2) to the descriptor without blocking.
As an example, you could use select() to simultaneously wait for new client connections and data from already connected clients in your server (assuming you extend it to handle multiple clients). To do this, you would use select() to monitor both sockfd and any descriptors you get back from accept(2). Without select(), you would have to use some form of non-blocking I/O (or separate threads) instead to avoid getting stuck in e.g. the accept() until a new client connects, which would prevent you from seeing data from other clients in the meantime. That would be both messier to implement and also less efficient than sleeping in a single location.
select() doesn't do any I/O by itself. It only notifies you when I/O becomes possible without blocking. You pass it a set of descriptors, and it tells you whenever I/O becomes possible on any of them. (And also tells you which descriptors it's possible on.)
In addition to waiting for data on sockets, you could use select() to wait for e.g. user input on stdin at the same time. There are many different types of descriptors that can be select()ed on.

lpthread libary difference between Solaris and GNU/Linux when linking with gcc

In Linux(Debian 7), when I run the server, the pointer to array *arg for *doSomething it crashes with segmentation fault.
(Line int *arg = malloc(sizeof(*arg));)
When I run the same code in Solaris, it runs like a charm.
Everything is compiled at its own respective OS (running at Solaris, compiled at Solaris, etc) with no errors or warnings.
A code snippet of the main body of the server without the doSomething method
int main(int argc, char *argv[])
{
int sockfd, portno,pErr;
socklen_t clilen;
//char buffer[256];
//char servPlay[10];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR on binding");
while(1) {
int newsockfd;
printf("Listening\n");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
printf("Client %s connected\n", inet_ntoa(cli_addr.sin_addr));
int *arg = malloc(sizeof(*arg));
if(arg ==NULL) {
fprintf(stderr, "No memory for thread\n");
}
*arg=newsockfd;
pErr = pthread_create(NULL,NULL,doSomething,arg);
if(pErr!=0)
{
error("Cannot cread thread\n");
//break;
}
}
close(sockfd);
return 0;
}
More probably than not mnunberg hit the mark and the crash happens in pthread_create() rather than the line you think.
man pthread_create:
Before returning, a successful call to pthread_create() stores the
ID of the new thread in the buffer pointed to by thread; this identifier
is used to refer to the thread in subsequent calls to other pthreads
functions.
It would be pointless allowing to pass a NULL pointer as the first argument (thread).

C http server stops after unpredictable time

I am writing a custom HTTP server in C for my OpenWrt router. It makes use of the uclibc library. I am only using one static buffer which I am very careful not to overflow, and no multi-threading is involved. It doesn't contain any complex data structures, and what it does is that:
it listen() s on the socket
reads the request after accept() ing
gets an html page by sending an http request to a predefined remote server (not a proxy)
sends the result through the accepted connection, and closes both.
The program would just stop running after some time, (it can be on receiving the first request, or after working under heavy strain for more that 2 hours). I am getting no error messages through the console, or anything, the program just stops. I have watched it and, as expected it doesn't consume more and more memory as it runs...
Is it possible that the kernel stops it if it thinks its abusing the CPU? How do I overcome that?
Are there some quirks to watch for in socket programming in C that are known to cause such crashes?
Can the stability issues be caused by using the Barrier Bracker (bleeding edge) branch of OpenWrt? Although the router itself never stops working...
Where do I start to look for the source of the problem?
Ok, first, I would like to thank everybody for helping. After writing a lot of netcat testers, I have pinpointed the problem. The program would crash - end without a single error message, if the connection is closed by the client before the last write or read occurs.
The write or read would raise a SIGPIPE signal which by default crashed the program if not handled manually... More info here: How to prevent SIGPIPE or prevent the server from ending?
This seems to be similar to what your trying to do,
as shown on http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html
Is this socket setup the same/similar to what your performing in your code?
/* A simple server in the internet domain using TCP
The port number is passed as an argument */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
return 0;
}

Am I creating the Protocol correctly?

I am trying to make a protocol (built ontop of TCP) that can send strings from the client to the server on port 457. Here is what I have so far:
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"U got your messaze",18);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
close(sockfd);
return 0;
}
Client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
close(sockfd);
return 0;
}
I feel as though I am not setting up the protocol properly. Am I?
You should write a specification for your protocol first. Then, after reviewing it, you implement the specification. You are basically asking us to reverse engineer your protocol. This is a backwards approach to creating a protocol. You should already have a clear intent of what your program needs to do before you begin implementing it.
Your server program acts as a limited kind of ECHO server, in that no more than 255 bytes of input from the client is accepted. Whatever the server was able to read is logged to the console, and the message U got your messaze is sent to the client as a response.
Some issues you may want to address:
Although unlikely, it is possible that your read() call returns with less data than what the client sent, even if the client sends less than 256 bytes. For example, if the client sends aaaaaaaaaa one byte at a time, your server might only see the first a, and assume it is a complete message.
You don't take precautions against writing to an already closed connection. This may generate SIGPIPE, and cause your program to exit unexpectedly.
Signals generally may interrupt your system calls. You should detect this condition and restart your system calls if that occurs.
You have some incorrect types:
htons expects uint16_t as parameter
htons((uint16_t)portno);
read() and write() expects ssize_t
ssize_t n;
bzero is deprecated, use
memset(buffer, 0, sizeof(buffer));
bcopy is deprecated, use
memmove(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
NUL terminate buffer when read() is used
/* bzero(buffer,256); Not needed */
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
buffer[n]= '\0'; /* here */
printf("Here is the message: %s\n",buffer);
And note that modern programs uses send() and recv() instead of read() and write()
Finally, don't use magic numbers
fgets(buffer, 255, stdin);
instead:
fgets(buffer, sizeof(buffer), stdin); /* 256 */
And why 255? the fgets() function shall read bytes from stream into the array pointed to by s, until n-1 bytes are read, so 256 is correct.

Why is this custom network-service crashing (written in C)?

I wrote this program which listens on a given port and then, once a connection is received, outputs a single line of text and disconnects. It runs for days, processing thousands of queries, but then (inevitably) crashes and I have to go restart it. Wondering if anyone sees anything wrong with it, or (alternatively) if anyone can suggest a way to make it more robust.
int main(int argc, char *argv[])
{
srand(time(0));
int sockfd, newsockfd, portno;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1)
{
unsigned char write_val;
unsigned char y[BYTES];
int i, j;
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
fill_buffer(y); // fills buffer y with a 128-bit string; not included here
for (i=BYTES-1; i >= 0; i--)
{
const void* ZERO = (void *)"0";
const void* ONE = (void *)"1";
for (j=7; j >= 0; j--)
write(newsockfd, (y[i] >> j) & 1 ? ONE : ZERO, 1);
}
write(newsockfd, "\n", 1);
close(newsockfd);
}
close(sockfd);
return 0;
}
anyone sees anything wrong with it
While this code could be made more efficient (by writing all the bytes in one single pass for example), there's no obvious flaw there.
That makes the unpublished part of your code a decent candidate for the problem:
fill_buffer(y); // fills buffer y with a 128-bit string; not included here
If you read more bytes than y[]'s size then you will crash.
or (alternatively) if anyone can suggest a way to make it more robust
Try enlarging the size of this y[] buffer (doubling it can't hurt).
And make sure that fill_buffer() can't read more than BYTES characters.
Publish this missing code in case of doubt.
You could also compile your code with debug symbols and dump a backtrace (with symbols) in a file from your signal handler. This way, if your program crashes, you will know where and why.
The code looks good with some comments.
One somewhat important comment:
portno should be declared as unsigned short. This works OK with an Intel-like (little endian) processor but it won't be portable to a processor with different endianness.
Anyway it was not the reason for your process crashing.
Obviously the crash occurs while executing within the 'while', and by looking at the code, if it crashes for a buffer overflow, the only possibility is within fill_buffer.
If you show the definition of BYTES and fill_buffer it will be easier to help you.
Now, if it is not a buffer overflow, there is the possibility that it aborts in the write if the client closed the connection before the server writes into the socket. In that case the process will receive a signal SIGPIPE and it will abort if the code does not handle that signal.
You can also ignore SIGPIPE with:
signal(SIGPIPE, SIG_IGN);
Other possibility is if you are doing something weird with write_val and you're not showing that code.
I hope this helps.

Resources