为什么使用 memcpy 将数据复制到缓冲区会导致缓冲区溢出?

Why copying data into buffer using memcpy cause buffer to overflow?

我有这个函数,但有时它会在粗体行 memcpy(&(sin6->sin6_addr), &(inet->address[0] ), 16); 尝试在应用程序外部执行此功能,但从未引起问题。大家怎么看为什么应用程序会在这次调用时崩溃?

struct mariyo_inet {
    uint8_t length;        /* Generally 4 or 16- the length of the address, in bytes. */
    uint8_t netmask;       /* The number of significant bits in the address. */
    uint8_t address[16];   /* The address itself. */
} __attribute__((packed));

void mariyo_inet_to_sockaddr(struct mariyo_inet *inet, struct sockaddr *sock, socklen_t *len, uint16_t port_ne)
{
    if (inet->length == 16) {
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sock;
        sin6->sin6_family = AF_INET6;
        sin6->sin6_port = port_ne;
#ifndef __linux__
        sin6->sin6_len = sizeof(*sin6);
#endif
        if (len) *len = sizeof(*sin6);
        **memcpy(&(sin6->sin6_addr), &(inet->address[0]), 16);**
    } else if (inet->length == 4) {
        struct sockaddr_in *sin = (struct sockaddr_in *) sock;
        sin->sin_family = AF_INET;
        sin->sin_port = port_ne;
#ifndef __linux__
        sin->sin_len = sizeof (*sin);
#endif
        if (len) *len = sizeof(*sin);
        memcpy(&(sin->sin_addr),
               &(inet->address[0]),
               4);
    }
}

这是应用程序堆栈跟踪,

#7  0x00000000007c94b9 in mariyo_inet_to_sockaddr (inet=inet@entry=0x7ff31a7da940, sock=sock@entry=0x7ff31a7da900, len=len@entry=0x7ff31a7da8f0, port_ne=port_ne@entry=0) at mariyo.c:4959
        sin6 = 0x7ff31a7da900
#8  0x000000000062847a in enzyme_mconn_icmp_transmit_cb () at enzyme_mconn_icmp.c:522
        ret = <optimized out>
        mconn_icmp = <optimized out>
        icmp_buf_len = <optimized out>
        icmp_linear_buf = 0x61d000d6cec8 "E"
        iph = 0x61d000d6cec8
        iph_len = <optimized out>
        icmp_hdr = <optimized out>
        icmpreq_cookie = 0x604000845f68
        icmpinfo = 0x619000355298
        icmpfail_cookie = <optimized out>
        ip_tlen = 1228
#9  0x0000000000603595 in enzyme_mconn_dequeue_data (mconn=mconn@entry=0x624010002b90, need_to_lock=0) at enzyme_mconn.c:547
        ret = 0
        tx_len = 1228
        __FUNCTION__ = "enzyme_mconn_dequeue_data"
#10 0x0000000000608ee5 in enzyme_mconn_send_data_to_client (mconn=mconn@entry=0x624010002b90, buf=buf@entry=0x60e000188218, buf_len=buf_len@entry=1228, need_to_lock=need_to_lock@entry=1) at enzyme_mconn.c:668
        res = 0
        enq_len = <optimized out>
        drop_len = <optimized out>
        __FUNCTION__ = "enzyme_mconn_send_data_to_client"
#11 0x0000000000609b42 in enzyme_client_process_rx_data (mconn=mconn@entry=0x624010003148, buf=buf@entry=0x60e000188218, buf_len=buf_len@entry=1228) at enzyme_mconn.c:952
        peer = 0x624010002b90
        res = <optimized out>
        __FUNCTION__ = "enzyme_client_process_rx_data"
        ip_proto = <optimized out>

这是它对调用者的看法,

int mariyo_string_to_inet(const char *in_str, struct mariyo_inet *inet)
{
    char tmp[51];
    //
    // filling tmp here from in_str
    //
    /* IPv4 + IPv6 in same address == bad */
    if (saw_dot || saw_colon) {
        if (saw_colon) {
            /* IPv6 (Note: Supports IPv4 in IPv6) */
            if (!inet_pton(AF_INET6, tmp, &(inet->address[0]))) {
                //fprintf(stderr, "%s:%d: Could not parse IP address <%s>\n", __FILE__, __LINE__, tmp);
                return 1;
            }
            if (!slash) netmask = 128;
            if (netmask > 128) {
                //fprintf(stderr, "%s:%d: Bad netmask > 128 on IP address <%s>\n", __FILE__, __LINE__, in_str);
                return 1;
            }
            inet->length = 16;
    }
    return 0;
}


{
    .....
    socklen_t sock_len;
    struct mariyo_inet inet_ip;
    struct sockaddr sock_addr;

    memset(&inet_ip, 0, sizeof(struct mariyo_inet));
    mariyo_string_to_inet(icmpinfo->probe_info.dest_ip, &inet_ip);

    memset(&sock_addr, 0, sizeof(struct sockaddr));
    mariyo_inet_to_sockaddr(&inet_ip, &(sock_addr), &sock_len, 0);
    ....
}

struct sockaddr 不能保证足够大(而且通常不够大)以容纳 IPv6 地址。

改用struct sockaddr_storage