C 代码缓冲区溢出问题
我正在编写一个小型 tcp echo 服务器,用于测试 Linux 上的缓冲区溢出。我有两个略有不同的服务器代码版本。当发送过大的缓冲区时,它首先会按照读取函数中的预期溢出,从而导致分段错误。对于代码的第二个版本,我在接受、读取和写入函数周围添加了一个 While (1) 循环,以便服务器在正常使用下不会退出,但是当将相同的缓冲区发送到第二个服务器时,不会发生溢出并且服务器根本不会崩溃。我无法弄清楚为什么,代码在 while 循环之外是相同的。任何帮助将不胜感激。 :)
服务器 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;
}
服务器 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;
}
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当缓冲区溢出时,您无法预测程序将做什么。该行为取决于缓冲区之后发生的情况以及超长输入中的具体情况。这些事情可能取决于程序的不相关部分(编译的地址),甚至可能取决于运行时变化的加载地址之类的东西。
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.
缓冲区溢出会导致堆栈被覆盖,特别是函数的返回地址。实际的溢出本身并不是导致分段错误的原因,而是当您稍后从发生溢出的函数返回时,返回地址已损坏。 (缓冲区溢出导致的其他可能的段错误包括从被覆盖的指针访问内存,或使用已被覆盖的函数指针等)。
在您的示例中, while 循环阻止您到达 return 语句,因此当您的缓冲区溢出并且返回地址被破坏时,该返回地址永远不会被使用,因此不会发生段错误。
如果您想验证是否发生溢出,我建议您在调试器中进行观察,或者打印出 serv_addr 和 cli_addr 结构中的值,我预计这些值会被溢出破坏。
另外,如果您想查看溢出引起的段错误,请将 recv 调用及其目标缓冲区移至单独的函数中,然后从 while(1) 循环内部调用该函数。当带有recv的函数返回时应该发生段错误。
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.