【问题标题】:SegFault on memcpy and source and destination are defined and initialized定义和初始化 memcpy 上的 SegFault 和源和目标
【发布时间】:2021-04-04 07:05:34
【问题描述】:

通过复制到已初始化的指针,我得到了 segFault。这是相关代码

*pay = (char *)malloc(sizeof(t1) + 1020 /*1000=payload*/);
memset(*pay, 0, sizeof(t1) + 1020);
struct ethhdr *eth = (struct ethhdr *) *pay;
struct iphdr *ip = (struct iphdr *) (*pay + sizeof(struct ethhdr));
struct tcphdr *tcp = (struct tcphdr *) (*pay + sizeof(struct ethhdr) + sizeof(struct iphdr));

我做到了

 memcpy(eth->h_dest, p->h_source, sizeof(eth->h_source));

但上面的行导致了段错误。我在其他线程memcpy((p)->h_source, received_packet_eth->h_source, sizeof(eth->h_source)); 中像这样分配h_source

h_source 在结构内部struct packets *p 这就是它的定义struct packets *p 我在其他线程中初始化和分配的方式,见上文。所以struct packets *p 不是一个未初始化的指针。我确信它是在发生 segFault 时分配的。

unsigned char h_source[ETH_ALEN];   /* source ether addr    */

感谢您的帮助

更新这是函数的完整代码

void get_payload_to_send(struct packets *p, char **pay)
{
    printf("%s --->>> %s", p->ip_source, p->ip_dest);
    int t1;
    *pay = (char *)malloc(sizeof(t1) + 1020 /*1000=payload*/);
    memset(*pay, 0, sizeof(t1) + 1020);
    struct ethhdr *eth = (struct ethhdr *) *pay;
    struct iphdr *ip = (struct iphdr *) (*pay + sizeof(struct ethhdr));
    struct tcphdr *tcp = (struct tcphdr *) (*pay + sizeof(struct ethhdr) + sizeof(struct iphdr));

    //if (p->h_proto == ntohs(p->h_proto) == ETH_P_IP)
    {
        if (p->syn == 1 && p->ack == 0 && p->fin == 0)
        {
            ///Ethernet
            //memcpy(eth->h_dest, p->h_source, sizeof(eth->h_source));

            eth->h_proto = htonl(ETH_P_IP);
            //memcpy(eth->h_source, p->h_dest, sizeof(eth->h_dest));
            ////IP

            // memcpy(ip->saddr, inet_addr(p->ip_dest), sizeof(ip->saddr));
            struct sockaddr_in s1, s2;
            s1.sin_family = AF_INET;
            s1.sin_port = htons(80);
            s1.sin_addr.s_addr = inet_addr(p->ip_source);

            s2.sin_family = AF_INET;
            s2.sin_port = htons(5009);
            s2.sin_addr.s_addr = inet_addr(p->ip_dest);
            //ip->saddr = s1.sin_addr.s_addr;
            sleep(2);
            ip->daddr = s2.sin_addr.s_addr;
            char c[20];
            memcpy(ip->saddr, inet_addr(p->ip_dest), sizeof(c) - 1);
            ip->ihl = 5;
            ip->version = 4;
            ip->tos = 0;
            ip->tot_len = sizeof(t1) + 1020;
            srand(1001);
            ip->id = htonl(rand());
            ip->frag_off = 0;
            ip->ttl = 225;
            ip->protocol = IPPROTO_TCP;
            ip->check = 0;
            tcp->syn = 1;
            tcp->ack = 1;

            memcpy(ip->daddr, inet_addr(p->ip_source), sizeof(c) - 1);

            //ip->check = csum((unsigned short *)*pay, ip->tot_len);
        }
        else if (p->syn == 0 && p->ack == 1)
        {

        }
    }
    //printf("%saddr %s -->>", p->ip_source);
    //printf("daddr %s\n", p->ip_dest);
    //populate eth
}

这是对上述函数进行函数调用的代码

void *task_sender(void *args) {
    struct struct_super_struct *super = (struct struct_super_struct *)args;

    struct packets *p = super->p; //(struct packets *)args;
    printf("Sender\n");
    while (1)
    {
        //printf("got uppder loop SENDER\n");

        pthread_mutex_lock(&mutex);
       
        while (nop == 0)
        {
            pthread_cond_wait(&cond, &mutex);
            //printf("finished waiting in SENDER\n");
        }
        int i = 0;

        printf("NOP is greater than 2 in SENDER %d\n");

        while (nop > 0)
        {
            // check_and_process_connection(super,(p+i));

            //if (p == NULL) break;
            if (strcmp("192.168.10.25", (p+i)->ip_dest) == 0 && (p+i)->syn == 1 && (p+i)->ack == 0)
            {
                //sleep(1);

                //printf("%s\n", (p+i)->ip_dest);
                //if (strcmp((p+i)->ip_dest, "192.168.10.25") == 0)
                {
                    char *pac;
                    struct packets *pt = (p+i);
                    get_payload_to_send((p+i), &pac);
                    struct sockaddr_in temp;
                    struct ethhdr *eth = (struct ethhdr *)pac;
                    struct iphdr *ip = (struct iphdr *)(pac + sizeof(struct ethhdr));
                    struct tcphdr *tcp = (struct tcphdr *)(pac + sizeof(struct ethhdr) + sizeof(struct iphdr));
                    temp.sin_addr.s_addr = ip->saddr;
                    char *source = inet_ntoa(temp.sin_addr);
                    struct sockaddr_in temp1;
                    temp1.sin_addr.s_addr = ip->daddr;
                    char *dest = inet_ntoa(temp1.sin_addr);
                    printf("%s >> %s \n", (p+i)->ip_source, (p+i)->ip_dest);
                    //if (strcmp("192.168.10.25",dest) == 0 && strcmp("192.168.10.25", source) == 0)
                    {
                        ///temp1.sin_addr.s_addr = ip->daddr;
                        printf("should be\n");
                        printf("frm %s to %s syc:%d ack:%d \n",
                               inet_ntoa(temp.sin_addr),
                               inet_ntoa(temp1.sin_addr),
                               tcp->syn, tcp->ack);
                    }
                    /*          
                    printf("__________________________________________________\n");
                    printf("lastop: %d\n", (p+i)->lastop);
                    printf("source: %s %d\n", (p+i)->ip_source, (p+i)->tcp_source);
                    printf("dest: %s %d\n", (p+i)->ip_dest, (p+i)->tcp_dest);
                    printf("syc: %d\n", (p+i)->syn);
                    printf("ack: %d\n", (p+i)->ack);
                    printf("seq: %d\n", (p+i)->seq);
                    printf("ack seq: %d\n", (p+i)->ack_seq);
                    (p+i)->lastop = 0;
                    printf("lastop: %d\n", (p+i)->lastop);
                    */
                }
                //printf("dest port: %d\n", (p+i)->tcp_dest);
            }
            //else { printf("source ip: %s\n", (p+i)->ip_source); printf("dest ip: %s\n", (p+i)->ip_dest); }
            //p++;
            nop--;
            i++;
            //printf("just processed packet SENDER\n");
        }
        //p = NULL;
    
        pthread_mutex_unlock(&mutex);
        nop = 0;
        int s = pthread_cond_signal(&cond);
    }
}

更新 2

struct packets {
    int syn;
    char payload[1000];
//    pthread_mutex_t  mutex;
    int lastop;
    unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
    unsigned char h_source[ETH_ALEN];   /* source ether addr    */
    __be16      h_proto;
    char ip_source[20];
    char ip_dest[20];
    int tcp_source;
    int tcp_dest;
    int seq;
    int ack_seq;

    int ack;
    int fin;
    int rst;
    int window;
    int nop;
    struct ethhdr *eth;
    struct iphdr *ip;
    struct tcphdr *tcp;
}; // *p=NULL;

注意我发现用gdb调试的是memcpy(eth->h_dest, p->h_source, sizeof(eth->h_source));eth->hdestNULL

【问题讨论】:

  • *pay=(char *) malloc 不要转换 malloc 的结果。
  • 这很重要,为什么。这不是我得到 segFault 的原因。我是吗?
  • 这段代码中的所有内容都是错误的。 我们没有看到 ethernet、ip 或 tcp 标头的声明,但不能保证您可以添加以太网的 14 字节大小标头并降落到与 IP 标头正确对齐的位置。
  • 我确实看到了,因此我的 cmets 在那里。我上面所说的与Dude在那里所说的并不矛盾。您的代码不构成minimal reproducible example,它只是一些转储到问题框中。这很容易分辨,因为您甚至都不关心删除注释代码!
  • 尝试通过 valgrind 运行您的代码。如果你对内存管理不善,它会告诉你在哪里。

标签: c linux


【解决方案1】:

您报告说,鉴于此设置...

    void get_payload_to_send(struct packets *p,char **pay)
    {
        printf("%s --->>> %s",p->ip_source,p->ip_dest);
        int t1;
        *pay=(char *) malloc(sizeof(t1)+1020/*1000=payload*/);
        memset (*pay, 0, sizeof(t1)+1020);
        struct ethhdr *eth = (struct ethhdr *) *pay;
        struct iphdr *ip = (struct iphdr *) (*pay + sizeof(struct ethhdr));
        struct tcphdr *tcp = (struct tcphdr *) (*pay + sizeof(struct ethhdr) + sizeof(struct iphdr));

...你发现这个memcpy() 抛出了一个段错误:

memcpy(eth->h_dest,p->h_source,sizeof(eth->h_source));

,gdb 向您显示eth->h_dest 在调用时为空。

好吧,目标指针为空完全解释了段错误。如果eth->h_dest 是一个指针,并且所有位为零是其类型的空指针表示,那么当然它是空的。您可以通过 memset() 调用将其设置为全零。

您需要在将数据复制到那里之前为eth->h_dest 分配足够的(额外)空间以指向,或者使用指针分配将其指向现有对象。上下文不够清楚,我无法判断哪个更合适。

【讨论】:

  • 发布后我完全按照你所说的做了else use pointer assignment to point it to an existing object 这件事前段时间已修复。但对我来说,你的信息非常重要。谢谢你。你也可以看看这个问题吗stackoverflow.com/questions/67042985/…
  • 你能告诉我一些关于copying ethernet frames to user buffer的问题吗?这是我问stackoverflow.com/questions/67054910/…的问题。顺便说一句,非常感谢您回答上述链接中的问题。我完全接受了答案,再次感谢。
  • 如果你知道任何内核编程,请查看上面的评论链接。如果您需要对问题进行任何更改,请告诉我。在过去的两天里,我有点卡住了,可能只是一个评论。我仍然没有收到任何回复。
【解决方案2】:

这些行有两个市长错误:

struct ethhdr *eth = (struct ethhdr *) *pay;
struct iphdr *ip = (struct iphdr *) (*pay + sizeof(struct ethhdr));
struct tcphdr *tcp = (struct tcphdr *) (*pay + sizeof(struct ethhdr) + sizeof(struct iphdr));
  • pay* 指向的内存是使用幻数分配的,ethhdriphdrtcphdr 的大小可能相同,但我不想处理该代码,访问边界是可能的。

  • 关于对齐,ethhdr 可能必须落在 X 的倍数上,但 pay 不是。这将触发一个错误(malloc 将返回一个地址,其对齐至少为max_align_t,但任何额外的添加都可能发生)。

【讨论】:

  • 在引用的行中没有严格的别名违规,它们似乎也没有造成以后会促进此类问题的情况。指向的内存在被写入之前没有有效的类型。
  • @JohnBollinger 我已经更新了我的答案,以澄清为什么我认为存在严格的混叠问题。
  • 关于对齐,我想我们说的是一样的,如果我错了,请纠正我。
  • its preceding paragraph 的上下文中阅读SAR,并特别注意SAR 本身中“有效类型”一词的谨慎使用(第6.5/8 段)。如果您仍然认为您看到了 SAR 违规,那么应该使用您认为所涉及对象的有效类型以及用于访问它们的左值的类型来扩充此答案中的解释。
  • @JohnBollinger 你是对的,我 100% 确信 memset 会改变有效类型,但事实并非如此。感谢您的时间和参考。
【解决方案3】:

问题是您没有提供指向memset() 的正确指针:

char *pay=(char *) malloc(sizeof(t1)+1020);
memset (*pay, 0, sizeof(t1)+1020);

使用动态分配的数组pay 中的第一个char 作为目标地址清除内存。

正确的形式是

char *pay = malloc(sizeof(t1)+1020);
memset (pay, 0, sizeof(t1)+1020);

这是因为pay 是指向动态分配数组的指针,而*pay 指向其第一个元素的值,就像pay[0] 一样。

(在 C 中,不需要来自 void * 的显式强制转换,例如来自 malloc() 的返回值。)


同样的错误重复多次:

struct ethhdr *eth = (struct ethhdr *) *pay;
struct iphdr *ip = (struct iphdr *) (*pay + sizeof(struct ethhdr));
struct tcphdr *tcp = (struct tcphdr *) (*pay + sizeof(struct ethhdr) + sizeof(struct iphdr));

这些声明和初始化指向从动态分配数组pay 中的第一个字符派生的地址的指针,而不是相对于数组开头的地址。

同样,它们的正确形式是

struct ethhdr *eth = (struct ethhdr *) pay;
struct iphdr *ip = (struct iphdr *) (pay + sizeof(struct ethhdr));
struct tcphdr *tcp = (struct tcphdr *) (pay + sizeof(struct ethhdr) + sizeof(struct iphdr));

因为*pay指的是pay数组中第一个元素的值,而pay指的是数组的地址。


显然,实际代码是

char **pay;

*pay = malloc(sizeof (t1) + 1020);
memset (*pay, 0, sizeof (t1) + 1020);

这缺少两项基本检查:

  1. pay 不为 NULL

  2. malloc() 成功了

代码可能因上述症状而失败。在最低,将其重写为


assert(pay != NULL);
*pay = malloc(sizeof (t1) + 1020);
assert(*pay != NULL);
memset(*pay, 0, sizeof (t1) + 1020);

ethiptcp 变量定义保持其原始形式(即,*pay)。

【讨论】:

  • 现在在线程二的get_payload_to_send 函数内的这一行ip->daddr=s2.sin_addr.s_addr; 抛出异常。 GDB 说 ip0xe 但是当我尝试打印 ip->daddr gdb 说 Cannot access memory at address 0x1e
  • @666:您的代码中始终存在相同的错误。你使用pay数组的第一个元素的值*pay,而你应该使用数组的地址pay
  • @Glärbo pay 的类型为 char **,因此这些都不适用。
  • @Glärbo 函数中的行是*pay=(char *) malloc(sizeof(t1)+1020/*1000=payload*/);。该函数的签名是void get_payload_to_send(struct packets *p,char **pay)。所以*pay 是一个char *,它包含malloc 返回的地址。
猜你喜欢
  • 1970-01-01
  • 2013-08-22
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多