使用valgrind调试socket发送非字符串数据时的内存泄漏问题
在之前的文章:使用Socket发送非字符串数据中实现了socket发送非字符串数据的功能,我本以为是个完美的方案。但是在使用valgrind调试的时候发现了内存越界的问题。由于我是初学C,面对这个问题显得有些手足无措。还好经过了半天的试错,终于解决了。
问题出在这段代码上
int pos = 0;
int len = 0;
while (pos < needSend)
{
len = send(sockfd, buffer + pos, BUFFER_SIZE, 0);
if (len <= 0)
{
printf("ERROR");
break;
}
pos += len;
}这段代码虽然通过buffer + pos来移动指针发送数据,但是没有计算剩余的数据量,每次都以BUFFER_SIZE大小发送。最后造成指针偏移超过buffer边界。使用gcc -g -fsanitize=address -fno-omit-frame-pointer -std=c11 -Wall -Wextra -Werror source.c -o target编译
=================================================================
==3775==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f240122c104 at pc 0x7f24050dfc1e bp 0x7ffdd3797240 sp 0x7ffdd37969e8
WRITE of size 792 at 0x7f240122c104 thread T0
#0 0x7f24050dfc1d (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x4ec1d)
#1 0x55cad9e243ca in main /home/dongchen/桌面/socket/largeDataSend.c:180
#2 0x7f2404cc1bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
#3 0x55cad9e23fb9 in _start (/home/dongchen/桌面/socket/largeDataSend+0xfb9)
0x7f240122c104 is located 0 bytes to the right of 4000004-byte region [0x7f2400e5b800,0x7f240122c104)
allocated by thread T0 here:
#0 0x7f240516fb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
#1 0x55cad9e2438c in main /home/dongchen/桌面/socket/largeDataSend.c:175
#2 0x7f2404cc1bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x4ec1d)
Shadow bytes around the buggy address:
0x0fe50023d7d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe50023d7e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe50023d7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe50023d800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0fe50023d810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0fe50023d820:[04]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fe50023d830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fe50023d840: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fe50023d850: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fe50023d860: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fe50023d870: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==3775==ABORTING接收到就是WRITE读取写入越界,发送端就是READ读取越界。
后来通过搜索找到了How to send messages with larger length than the buffer in socket programming?
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
int sendall(int s, char *buf, int *len)
{
int total = 0; // how many bytes we've sent
int bytesleft = *len; // how many we have left to send
int n;
while(total < *len) {
n = send(s, buf+total, bytesleft, 0);
if (n == -1) { break; }
total += n;
bytesleft -= n;
}
*len = total; // return number actually sent here
return n==-1?-1:0; // return -1 onm failure, 0 on success
}
#pragma GCC diagnostic pop上面的#pragma部分是用来忽略使用gcc编译时提示 error: ‘n’ may be used uninitialized in this function [-Werror=maybe-uninitialized]这个报错的
通过计算剩余的bytesleft来保证内存不越界
其实len和n都是指实际发送的字节数或者实际收到的字节数。
放上没有内存泄漏的完整代码,两边都是Linux版本
服务端
#include <sys/types.h>
#include <sys/socket.h> // 包含套接字函数库
#include <stdio.h>
#include <netinet/in.h> // 包含AF_INET相关结构
#include <arpa/inet.h> // 包含AF_INET相关操作的函数
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#define HELLO_WORLD_SERVER_PORT 6666
#define LENGTH_OF_LISTEN_QUEUE 20
typedef struct
{
int ab;
int num[1000000];
}Node;
int recvall (int s,char *buf,int *len)
{
int total = 0;
int bytesleft = *len;
int n;
while (total<*len)
{
n= recv(s,buf+total,bytesleft,0);
if(n==-1){break;}
total += n;
bytesleft -=n;
}
*len = total;
return n==-1?-1:0;
}
int main(void)
{
// set socket's address information
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
// create a stream socket
int server_socket = socket(PF_INET, SOCK_STREAM, 0);
if (server_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
//bind
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
printf("Server Bind Port: %d Failed!\n", HELLO_WORLD_SERVER_PORT);
exit(1);
}
// listen
if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
{
printf("Server Listen Failed!\n");
exit(1);
}
while(1)
{
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
if (new_server_socket < 0)
{
printf("Server Accept Failed!\n");
break;
}
Node *myNode=(Node*)malloc(sizeof(Node));
int needRecv=sizeof(Node);
char *buffer=(char*)malloc(needRecv);
int result = recvall(new_server_socket,buffer,&needRecv);
memcpy(myNode,buffer,needRecv);
printf("recv over ab=%d num[0]=%d num[999999]=%d\n",myNode->ab,myNode->num[0],myNode->num[999999]);
free(buffer);
free(myNode);
if(result ==0)
{
printf("Recv over!!!\n");
break;
}
}
close(server_socket);
return 0;
}客户端
#include <sys/types.h>
#include <sys/socket.h> // 包含套接字函数库
#include <stdio.h>
#include <netinet/in.h> // 包含AF_INET相关结构
#include <arpa/inet.h> // 包含AF_INET相关操作的函数
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#define MYPORT 6666
typedef struct
{
int ab;
int num[1000000];
}Node;
int sendall(int s, char *buf, int *len)
{
int total = 0;
int bytesleft = *len;
int n;
while(total < *len) {
n = send(s, buf+total, bytesleft, 0);
if (n == -1) { break; }
total += n;
bytesleft -= n;
}
*len = total;
return n==-1?-1:0;
}
int main()
{
///sockfd
int sock_cli = socket(AF_INET,SOCK_STREAM, 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(MYPORT);
servaddr.sin_addr.s_addr = inet_addr("192.168.8.220");
if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("connect");
exit(1);
}
Node *myNode=(Node*)malloc(sizeof(Node));
myNode->ab=123;
myNode->num[0]=110;
myNode->num[999999]=99;
int needSend=sizeof(Node);
char *buffer=(char*)malloc(needSend);
memcpy(buffer,myNode,needSend);
int result = sendall(sock_cli, buffer,&needSend);
if(result==0)
{
printf("Send over!!!\n");
}
free(buffer);
free(myNode);
close(sock_cli);
return 0;
}现在唯一的错误就是myNode中num[1]到num[999998]没有初始化了。
评论已关闭