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;
se开发者_运维知识库rv_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.
精彩评论