【问题标题】:How to fix "free(): invalid next size (normal) | Canceled (memory dump written)" in C如何在 C 中修复“free(): invalid next size (normal) | Canceled (memory dumpwritten)”
【发布时间】:2019-02-12 01:33:50
【问题描述】:

我正在编写一个实际发送原始数据包和嗅探网络的程序。我有程序要构建、发送数据包和嗅探网络。他们每个人都(大部分)工作得很好。如果我尝试实现一个主函数,它首先调用函数来构建和发送数据包,然后是嗅探的函数,我得到一个free(): Invalid next size (normal) | Cancelled (memory dump written) 错误。

我试图自己找出问题所在。我在不同的地方调用了startSniffer() - 函数。实际上在 send_tcp_packet 之前构建 tcp 数据包 (build_tcp_packet),之后也是。如果我在build_tcp_packet 之前调用startSniffer() 函数,它就可以工作。所以我在memcpy(...)之前调用startSniffer()-build_tcp_packet中的函数,它可以工作。所以我假设错误是因为memcpy(...)。我删除了memcpy(...),程序运行良好。但实际上程序需要memcpy(...) 在 TCP-Header 中设置 Payload。

那么您对我的代码有什么想法和帮助吗???

void startBuilding() {

    unsigned char *data;

    unsigned int packet_size;
    unsigned int data_size;

    src_addr.sin_family = AF_INET;
    inet_aton(srcHost, &src_addr.sin_addr);

    dst_addr.sin_family = AF_INET;
    inet_aton(destHost, &dst_addr.sin_addr);

    data = (char*) malloc(strlen(sendingData) + 1);

    strcpy((char *) data, sendingData);

    data_size = strlen(sendingData);

    switch (ipProto) {
    case IPPROTO_TCP:

        printf("[+] Send TCP packet...\n");

        src_addr.sin_port = htons(tcpSourcePort);
        dst_addr.sin_port = htons(tcpDestPort);

        if (open_RawSocket(ipProto)) {
            send_tcp_packet(raw_socket, src_addr, dst_addr, data, data_size);

        }

}



unsigned int build_tcp_packet(struct sockaddr_in src_addr,
    struct sockaddr_in dst_addr, unsigned char *tcp_packet,
    unsigned char *data, unsigned int data_size) {

    printf("[+] Build TCP packet...\n\n");

    unsigned int length;
    struct tcpheader *tcp;
    length = TCPHDRSIZE + data_size;
    tcp = (struct tcpheader *) tcp_packet;

    tcp->th_sport = src_addr.sin_port;
    tcp->th_dport = dst_addr.sin_port;

    tcp->th_seq = htonl(tcpSeqNum);
    tcp->th_acknum = tcpAcknum;

    tcp->th_reserved = tcpReserved;
    tcp->th_off = tcpOffSet;

    tcp->th_fin = tcpFinFlag;
    tcp->th_syn = tcpSynFlag;
    tcp->th_rst = tcpRstFlag;
    tcp->th_psh = tcpPshFlag;
    tcp->th_ack = tcpAckFlag;
    tcp->th_urg = tcpUrgFlag;
    tcp->th_cwr = tcpCwrFlag;
    tcp->th_ece = tcpEceFlag;

    tcp->th_win = htons(tcpWin);

    tcp->th_urp = tcpUrp;

    tcp->th_sum = tcpSum;

//      startSniffer(); // If i start startSniffer here, it works fine.
    memcpy(tcp_packet + TCPHDRSIZE, data, data_size);
//      startSniffer(); //If i start sartSniffer here, it did not work, and i get the error. 

    return length;
 }


unsigned int build_ip_packet(struct in_addr src_addr, struct in_addr dst_addr,
    uint8_t protocol, unsigned char *ip_packet, unsigned char *data,
    unsigned int data_size) {

    printf("[+] Build IP packet...\n\n");

    struct ipheader *ip;
    ip = (struct ipheader*) ip_packet;

    ip->ip_v = ipv;
    ip->ip_hl = iphl;

    ip->ip_tos = iptype;
    ip->ip_id = htons(ipId);

    if (calculateIpLenInBuildPacket) {
        ip->ip_len = htons(iphl * 4 + data_size);
    } else {
        ip->ip_len = htons(ipLen);
    }

    ip->ip_off = ipOffset;

    ip->ip_moreFrag = ipMoreFragmentFlag;
    ip->ip_doNotFrag = ipDoNotFragmentFlag;
    ip->ip_reserved = ipReserved;
    ip->ip_frag_offset1 = ipFragOffset;

    ip->ip_ttl = ipTTL;
    ip->ip_p = ipProto;

    ip->ip_sum = ipSum;
    ip->iph_sourceip = src_addr.s_addr;
    ip->iph_destip = dst_addr.s_addr;

    return sizeof(struct ipheader) + data_size;
}


int main(int argc, char **argv) {

    startBuilding();

    startSniffer();

}

void send_tcp_packet(int raw_sock, struct sockaddr_in src_addr,
    struct sockaddr_in dst_addr, uint8_t *data, unsigned int data_size) {
    unsigned int packet_size;
    unsigned int ip_payload_size;
    unsigned char *packet;


    packet = (char*) malloc(strlen(data) + 1);
    memset(packet, 0, ETH_DATA_LEN);



    ip_payload_size = build_tcp_packet(src_addr, dst_addr,
        packet + sizeof(struct ipheader), data, data_size);

    packet_size = build_ip_packet(src_addr.sin_addr, dst_addr.sin_addr,
    IPPROTO_TCP, packet, packet + sizeof(struct ipheader), ip_payload_size);

    setOption_RawSocket(raw_sock);

    if (sendto(raw_sock, packet, packet_size, 0, (struct sockaddr *) &dst_addr,
        sizeof(dst_addr)) < 0) {
        perror("sendto");

        exit(1);
    } else {

        printf("Send! \n\n");


    }
    close(raw_sock);

}

【问题讨论】:

  • 请尝试制作更简洁的minimal reproducible example。应该可以将其减少为对 malloc 和 free 的几次误用。
  • 您的问题可能来自内存管理元数据的损坏。可能的原因包括超出动态分配的缓冲区、双重释放分配的内存以及在释放目标后继续使用指针。这很可能发生在程序实际崩溃之前的某个时间。如果您无法通过代码分析发现问题,那么使用 Valgrind 等内存使用调试器将是一个不错的选择。
  • 你需要调试这个。您的代码中的某处可能存在缓冲区溢出。我建议您仔细查看您提到的memcpy,并检查您复制的字节数是否超出允许范围。
  • github.com/google/sanitizers/wiki/AddressSanitizer 对于调试此类问题非常有用。它是 GCC 的一部分,易于使用。
  • @JohnBollinger 如果我没有打电话给startSniffer(),代码工作正常吗?这让我很困惑。

标签: c free memcpy


【解决方案1】:

这是危险的

packet = (char*) malloc(strlen(data) + 1);
memset(packet, 0, ETH_DATA_LEN);

只要ETH_DATA_LEN 大于strlen(data) + 1,就会损坏堆,不会立即检测到堆损坏,但可能会导致后续分配或释放(空闲)失败。

建议:

packet = calloc( ETH_DATA_LEN, 1 ) ;

您轻率地假设data 是一个以空字符结尾的字符串,并且无论如何都应该使用data_size 来确定@​​987654327@ 的长度,但是由于数据包大小必然比数据负载大,所以两者不正确。

在这种情况下你没有释放packet,所以也有内存泄漏。由于ETH_DATA_LEN 是一个常量,所以无论如何都不需要动态分配。相反,您可能有:

char* packet[ETH_DATA_LEN] = {0} ;

【讨论】:

  • @Sebastian :做你喜欢的事。 calloc() 对于此处的解决方案不是必需的,但是如果您要立即分配 memsetting 为零,则更安全,因为您不需要确保分配并且 memset 的大小相同 - 这是这种情况下的错误.
  • 请注意,鉴于仅这一个函数中的危险做法的数量,我不建议我发现它们,甚至没有查看其他函数。
  • 实际上我无论如何都需要动态分配。为了数据包将正确发送。是否有其他方法可以创建动态变量,而不会发生内存泄漏?......
  • 知道了。在我发送数据包之前,我会先初始化我的数据包:))) 如果你愿意,我可以分享解决方案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-20
  • 2011-01-19
  • 2014-07-04
  • 2022-12-26
  • 1970-01-01
  • 1970-01-01
  • 2021-04-29
相关资源
最近更新 更多