以下代碼在第 33 次迭代中生成分段錯誤。有問題的代碼行是memcpy(data, datagram, 4096);. 一個簡單的如何memcpy產生分段錯誤?
我的程式在 gdb 中的輸出如下:
(gdb) r
Starting program: /home/yaakov/mmap/minimal
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9
Iteration 10
Iteration 11
Iteration 12
Iteration 13
Iteration 14
Iteration 15
Iteration 16
Iteration 17
Iteration 18
Iteration 19
Iteration 20
Iteration 21
Iteration 22
Iteration 23
Iteration 24
Iteration 25
Iteration 26
Iteration 27
Iteration 28
Iteration 29
Iteration 30
Iteration 31
Iteration 32
Iteration 33
Program received signal SIGSEGV, Segmentation fault.
0x00005555555556e0 in main (argc=1, argv=0x7fffffffe618) at mmap/minimal.c:151
151 memcpy(data, datagram, 4096);
(gdb) bt
#0 0x00005555555556e0 in main (argc=1, argv=0x7fffffffe618) at mmap/minimal.c:151
產生這種分段錯誤的最小復制器如下:
#include <string.h>
#include <sys/mman.h>
#include <stdint.h>
#include <sys/socket.h>
#include <features.h> /* for the glibc version number */
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h> /* the L2 protocols */
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* The L2 protocols */
#endif
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
volatile struct tpacket_hdr * ps_header_start;
volatile int fd_socket;
volatile int data_offset = 0;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
static int mode_loss = 0;
static char * str_devname= NULL;
static int c_buffer_sz = 1024*8;
static int c_buffer_nb = 1024;
static int c_sndbuf_sz = 0;
struct tpacket_req s_packet_req;
int main( int argc, char ** argv )
{
uint32_t size;
struct sockaddr_ll my_addr;
struct ifreq s_ifr;
int i_index = 0;
int ec;
int i_ifindex;
int tmp;
fd_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(fd_socket == -1)
{
perror("socket");
return 1;
}
/* clear structure */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = PF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
str_devname = "enp3s0";
//strcpy (str_devname, ifname);
/* initialize interface struct */
strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
/* Get the broad cast address */
ec = ioctl(fd_socket, SIOCGIFINDEX, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return 1;
}
/* update with interface index */
i_ifindex = s_ifr.ifr_ifindex;
s_ifr.ifr_mtu = 7200;
/* update the mtu through ioctl */
ec = ioctl(fd_socket, SIOCSIFMTU, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return 1;
}
/* set sockaddr info */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = ETH_P_ALL;
my_addr.sll_ifindex = i_ifindex;
/* bind port */
if (bind(fd_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)) == -1)
{
perror("bind");
return 1;
}
/* prepare Tx ring request */
s_packet_req.tp_block_size = c_buffer_sz;
s_packet_req.tp_frame_size = c_buffer_sz;
s_packet_req.tp_block_nr = c_buffer_nb;
s_packet_req.tp_frame_nr = c_buffer_nb;
/* calculate memory to mmap in the kernel */
size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
/* set packet loss option */
tmp = mode_loss;
if (setsockopt(fd_socket, SOL_PACKET, PACKET_LOSS, (char *)&tmp, sizeof(tmp))<0)
{
perror("setsockopt: PACKET_LOSS");
return 1;
}
/* send TX ring request */
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
return 1;
}
/* change send buffer size */
if(c_sndbuf_sz) {
printf("send buff size = %d\n", c_sndbuf_sz);
if (setsockopt(fd_socket, SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz))< 0)
{
perror("getsockopt: SO_SNDBUF");
return 1;
}
}
/* get data offset */
data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
/* mmap Tx ring buffers memory */
ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (ps_header_start == (void*)-1)
{
perror("mmap");
return 1;
}
while (1)
{
char * data;
char datagram[4096];
struct tpacket_hdr * ps_header;
int ec_send = 0;
printf("Iteration %d\n", i_index);
ps_header = ((struct tpacket_hdr *)((void *)ps_header_start) (c_buffer_sz*i_index));
data = ((void*) ps_header) data_offset;
memset (datagram, 0, 4096);
memcpy(data, datagram, 4096);
/* update packet len */
ps_header->tp_len = c_buffer_sz;
/* set header flag to USER (trigs xmit)*/
ps_header->tp_status = TP_STATUS_SEND_REQUEST;
//int ec_send;
static int total=0;
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Wait end of transfer */
//ec_send = sendto(fd_socket,NULL,0,MSG_DONTWAIT,(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
ec_send = sendto(fd_socket,NULL,0,0,(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
i_index ;
if (i_index >= c_buffer_nb) {
i_index = 0;
}
}
}
請注意,介面名稱被硬編碼為enp3s0. 您可能必須更改它才能使代碼在您的系統上運行。
編輯:
這是請求的除錯資訊:
(gdb) p data
$1 = 0x7ffff7dbf020 <__GI_tolower> "\363\017", <incomplete sequence \372\215\227\200>
(gdb) p ps_header_start
$2 = (volatile struct tpacket_hdr *) 0x7ffff757f000
(gdb) p size
$3 = 8388608
(gdb)
將十進制轉換8388608為十六進制給出800000. 添加800000到7ffff757f000給7FFFF7D7F000. data指標確實越界了。
uj5u.com熱心網友回復:
問題在這里:
ps_header = ((struct tpacket_hdr *)((void *)ps_header_start) (c_buffer_sz*i_index));
您正在使用ps_header_start,將其轉換為 a struct tpacket_hdr *,然后添加緩沖區大小。但是當你添加到 C 中的指標時,添加的值將乘以所指向型別的大小。換句話說,不是添加c_buffer_size * i_index到起始指標,而是實際上添加c_buffer_sz * i_index * sizeof(struct tpacket_hdr)到它。
計算下一幀槽地址的正確方法是:
ps_header = (struct tpacket_hdr *)((char *)ps_header_start (c_buffer_sz * i_index));
將起始指標轉換為 a char *(因為 的大小char為 1),然后將幀大小和索引的乘積添加到其中,然后將其轉換回struct tpacket_hdr *.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/410347.html
標籤:
