BPF 验证器因对数据包的无效访问而失败

BPF verifier fails because of invalid access to packet

我正在尝试从 XDP 程序中的 TLS hello 数据包的 SNI 扩展中获取服务器名称。当我尝试加载它时,我从 BPF 验证程序中收到以下错误: invalid access to packet

struct server_name {
    char server_name[256];
};

struct extension {
    __u16 type;
    __u16 len;
} __attribute__((packed));

struct sni_extension {
    __u16 list_len;
    __u8 type;
    __u16 len;
} __attribute__((packed));

#define SERVER_NAME_EXTENSION 0

SEC("xdp")
int collect_ips_prog(struct xdp_md *ctx) {
    char *data_end = (char *)(long)ctx->data_end;
    char *data = (char *)(long)ctx->data;

    if (data_end < (data + sizeof(__u16))) {
        goto end;
    }

    __u16 extension_method_len = __bpf_htons(*(__u16 *) data);

    data += sizeof(__u16);

    for(int i = 0; i < extension_method_len; i += sizeof(struct extension)) {
        if (data_end < (data + sizeof(struct extension))) {
            goto end;
        }

        struct extension *ext = (struct extension *) data;

        data += sizeof(struct extension);

        ///////////////////// (A) ////////////////////
        if (data_end < ((char *) ext) + sizeof(struct extension)) {
            goto end;
        }

        if (ext->type == SERVER_NAME_EXTENSION) { // Error happens here
            struct server_name sn;

            if (data_end < (data + sizeof(struct sni_extension))) {
                goto end;
            }

            struct sni_extension *sni = (struct sni_extension *) data;

            data += sizeof(struct sni_extension);

            __u16 server_name_len = __bpf_htons(sni->len);

            for(int sn_idx = 0; sn_idx < server_name_len; sn_idx++) {
                if (data_end < data + sn_idx) {
                    goto end;
                }

                if (sn.server_name + sizeof(struct server_name) < sn.server_name + sn_idx) {
                    goto end;
                }

                sn.server_name[sn_idx] = data[sn_idx];
            }

            sn.server_name[server_name_len] = 0;
            goto end;
        }

        __u16 ext_len = __bpf_htons(ext->len);

        if (ext_len > 30000) {
            goto end;
        }

        if (data_end < data + ext_len) {
            goto end;
        }

        data += ext_len;
        i += ext_len;
    }

end:
    return XDP_PASS;
}

忽略data没有指向TLS数据包扩展长度字段的开头;我没有包括进入该字段的代码,因为上面的代码足以重现我所看到的问题。

这是我尝试加载此程序时错误日志的结尾。最后的错误发生在 if (ext->type == SERVER_NAME_EXTENSION) {:

from 31 to 12: R0_w=pkt(id=14,off=58,r=0,umax_value=42000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=56,umax_value=42056,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=14,off=58,r=0,umax_value=42000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=14,off=58,r=62,umax_value=42000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=56,umax_value=42056,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=14,off=62,r=62,umax_value=42000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0_w=pkt(id=14,off=58,r=62,umax_value=42000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=56,umax_value=42056,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=14,off=62,r=62,umax_value=42000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=56,umax_value=42056,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=14,off=62,r=62,umax_value=42000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0_w=pkt(id=15,off=62,r=0,umax_value=45000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=60,umax_value=45060,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=15,off=62,r=0,umax_value=45000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=15,off=62,r=66,umax_value=45000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=60,umax_value=45060,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=15,off=66,r=66,umax_value=45000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0_w=pkt(id=15,off=62,r=66,umax_value=45000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=60,umax_value=45060,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=15,off=66,r=66,umax_value=45000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=60,umax_value=45060,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=15,off=66,r=66,umax_value=45000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0_w=pkt(id=16,off=66,r=0,umax_value=48000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=64,umax_value=48064,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=16,off=66,r=0,umax_value=48000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=16,off=66,r=70,umax_value=48000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=64,umax_value=48064,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=16,off=70,r=70,umax_value=48000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0_w=pkt(id=16,off=66,r=70,umax_value=48000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=64,umax_value=48064,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=16,off=70,r=70,umax_value=48000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=64,umax_value=48064,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=16,off=70,r=70,umax_value=48000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0_w=pkt(id=17,off=70,r=0,umax_value=51000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=68,umax_value=51068,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=17,off=70,r=0,umax_value=51000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=17,off=70,r=74,umax_value=51000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=68,umax_value=51068,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=17,off=74,r=74,umax_value=51000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0_w=pkt(id=17,off=70,r=74,umax_value=51000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=68,umax_value=51068,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=17,off=74,r=74,umax_value=51000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=68,umax_value=51068,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=17,off=74,r=74,umax_value=51000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0=pkt(id=18,off=74,r=0,umax_value=54000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=0,umin_value=72,umax_value=54072,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5=pkt(id=18,off=74,r=0,umax_value=54000,var_off=(0x0; 0xffffffff)) R6=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0=pkt(id=18,off=74,r=78,umax_value=54000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=0,umin_value=72,umax_value=54072,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=18,off=78,r=78,umax_value=54000,var_off=(0x0; 0xffffffff)) R6=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0=pkt(id=18,off=74,r=78,umax_value=54000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=0,umin_value=72,umax_value=54072,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=18,off=78,r=78,umax_value=54000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3=inv(id=0,umin_value=72,umax_value=54072,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=18,off=78,r=78,umax_value=54000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0_w=pkt(id=19,off=78,r=0,umax_value=57000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=76,umax_value=57076,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=19,off=78,r=0,umax_value=57000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=19,off=78,r=82,umax_value=57000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=76,umax_value=57076,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=19,off=82,r=82,umax_value=57000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0_w=pkt(id=19,off=78,r=82,umax_value=57000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=76,umax_value=57076,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=19,off=82,r=82,umax_value=57000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=76,umax_value=57076,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=19,off=82,r=82,umax_value=57000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0_w=pkt(id=20,off=82,r=0,umax_value=60000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=80,umax_value=60080,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=20,off=82,r=0,umax_value=60000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=20,off=82,r=86,umax_value=60000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=80,umax_value=60080,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=20,off=86,r=86,umax_value=60000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0_w=pkt(id=20,off=82,r=86,umax_value=60000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=80,umax_value=60080,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=20,off=86,r=86,umax_value=60000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=80,umax_value=60080,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=20,off=86,r=86,umax_value=60000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0_w=pkt(id=21,off=86,r=0,umax_value=63000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=84,umax_value=63084,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=21,off=86,r=0,umax_value=63000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=21,off=86,r=90,umax_value=63000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=84,umax_value=63084,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=21,off=90,r=90,umax_value=63000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
15: (71) r7 = *(u8 *)(r0 +1)
16: (67) r7 <<= 8
17: (4f) r7 |= r6
; if (ext->type == SERVER_NAME_EXTENSION) {
18: (15) if r7 == 0x0 goto pc+13
 R0_w=pkt(id=21,off=86,r=90,umax_value=63000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=84,umax_value=63084,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=21,off=90,r=90,umax_value=63000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; __u16 ext_len = __bpf_htons(ext->len);
19: (71) r6 = *(u8 *)(r0 +2)
20: (71) r0 = *(u8 *)(r0 +3)
21: (67) r0 <<= 8
22: (4f) r0 |= r6
23: (dc) r0 = be16 r0
; if (ext_len > 3000) {
24: (25) if r0 > 0xbb8 goto pc+7
 R0_w=inv(id=0,umax_value=3000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=84,umax_value=63084,var_off=(0x0; 0xffff),s32_min_value=0,s32_max_value=65535,u32_max_value=65535) R4=inv17179869184 R5_w=pkt(id=21,off=90,r=90,umax_value=63000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
25: (0f) r5 += r0
last_idx 25 first_idx 31
regs=1 stack=0 before 24: (25) if r0 > 0xbb8 goto pc+7
regs=1 stack=0 before 23: (dc) r0 = be16 r0
regs=1 stack=0 before 22: (4f) r0 |= r6
regs=41 stack=0 before 21: (67) r0 <<= 8
regs=41 stack=0 before 20: (71) r0 = *(u8 *)(r0 +3)
regs=40 stack=0 before 19: (71) r6 = *(u8 *)(r0 +2)
26: (0f) r3 += r0
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
27: (67) r3 <<= 32
28: (0f) r3 += r4
29: (c7) r3 s>>= 32
30: (bf) r0 = r5
; for(int i = 0; i < extension_methods_len; i += sizeof(struct extension)) {
31: (6d) if r1 s> r3 goto pc-20

from 31 to 12: R0_w=pkt(id=22,off=90,r=0,umax_value=66000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=88,umax_value=66088,var_off=(0x0; 0x1ffff),s32_min_value=0,s32_max_value=131071,u32_max_value=131071) R4=inv17179869184 R5_w=pkt(id=22,off=90,r=0,umax_value=66000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (data_end < (data + sizeof(struct extension))) {
12: (07) r5 += 4
; if (data_end < (data + sizeof(struct extension))) {
13: (2d) if r5 > r2 goto pc+18
 R0_w=pkt(id=22,off=90,r=0,umax_value=66000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=88,umax_value=66088,var_off=(0x0; 0x1ffff),s32_min_value=0,s32_max_value=131071,u32_max_value=131071) R4=inv17179869184 R5_w=pkt(id=22,off=94,r=0,umax_value=66000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0
; if (ext->type == SERVER_NAME_EXTENSION) {
14: (71) r6 = *(u8 *)(r0 +0)
invalid access to packet, off=90 size=1, R0(id=22,off=90,r=0)
R0 offset is outside of the packet
processed 477 insns (limit 1000000) max_states_per_insn 4 total_states 9 peak_states 9 mark_read 2 

我原以为如果语句 (A) 就足以验证 ext 是否指向有效地址,尽管一开始不需要它,因为 if (data_end < (data + sizeof(struct extension))) {检查。

我在使用 __s16 ext_len 时遇到此错误。我也不明白它失败的地方是 14: (71) r6 = *(u8 *)(r0 +0) 的说明。然后 len 字段是 __u16,所以它不应该做 *(u16 *) 吗?

我是 运行 内核 5.13.0-19-generic。

我想我找到了问题的核心。验证器跟踪关于变量的几个属性,这些属性允许它确定程序是否可以访问它不应该访问的数据。这些属性之一是 umax_value,它跟踪最大无符号整数值,它可能是动态的。

由于数据包的大小有限 verifier asserts that the umax_value of an offset into the packet may never exceed MAX_PACKET_OFF(65536).

每次程序循环时,我们将 ext_len 添加到 data,因为 ext_len 是一个 __u16,其最大 uint 值默认为 65536。该程序使用以下语句将其限制为 30000:

if (ext_len > 30000) {
  goto end;
}

但是,dataumax_value 会在每次迭代中累积。我们可以在验证者日志中看到这一点:

; if (data_end < (data + sizeof(struct extension))) { 13: (2d) if r5 > r2 goto pc+18 R0_w=pkt(id=22,off=90,r=0,umax_value=66000,var_off=(0x0; 0xffffffff)) R1=inv(id=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umin_value=88,umax_value=66088,var_off=(0x0; 0x1ffff),s32_min_value=0,s32_max_value=131071,u32_max_value=131071) R4=inv17179869184 R5_w=pkt(id=22,off=94,r=0,umax_value=66000,var_off=(0x0; 0xffffffff)) R6_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R7_w=inv(id=0) R10=fp0 ; if (ext->type == SERVER_NAME_EXTENSION) { 14: (71) r6 = *(u8 *)(r0 +0)

umax_value 大于 65536,因此出现错误。

现在,要解决此问题,我们需要更改代码,使 data 不能超过 65536。我们通过指定最大迭代次数(扩展)并设置每个扩展的最大大小来实现此目的。我修改了程序以添加这些约束,我选择了最大 32 扩展和每个扩展最大 2048 字节,这似乎是合理的值 (32 * 2048 = 65536),这些可以更改。

#include <stddef.h>
#include <linux/bpf.h>
#include "./bpf_endian.h"

#define SEC(NAME) __attribute__((section(NAME), used))

struct server_name
{
    char server_name[256];
};

struct extension
{
    __u16 type;
    __u16 len;
} __attribute__((packed));

struct sni_extension
{
    __u16 list_len;
    __u8 type;
    __u16 len;
} __attribute__((packed));

#define SERVER_NAME_EXTENSION 0

SEC("xdp")
int collect_ips_prog(struct xdp_md *ctx)
{
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    void *cursor = (void *)(long)ctx->data;

    if (data_end < (cursor + sizeof(__u16)))
    {
        goto end;
    }

    __s64 extension_method_len = *(__u16 *)cursor;
    if (extension_method_len < 0)
    {
        goto end;
    }

    cursor += sizeof(__u16);

    for (int i = 0; i < 32; i++)
    {
        struct extension *ext;

        if (cursor > extension_method_len + data)
        {
            goto end;
        }

        if (data_end < (cursor + sizeof(*ext)))
        {
            goto end;
        }

        ext = (struct extension *)cursor;

        cursor += sizeof(*ext);

        if (ext->type == SERVER_NAME_EXTENSION)
        {
            struct server_name sn;

            if (data_end < (cursor + sizeof(struct sni_extension)))
            {
                goto end;
            }

            struct sni_extension *sni = (struct sni_extension *)cursor;

            cursor += sizeof(struct sni_extension);

            __u16 server_name_len = sni->len;

            for (int sn_idx = 0; sn_idx < server_name_len; sn_idx++)
            {
                if (data_end < cursor + sn_idx)
                {
                    goto end;
                }

                if (sn.server_name + sizeof(struct server_name) < sn.server_name + sn_idx)
                {
                    goto end;
                }

                sn.server_name[sn_idx] = ((char *)cursor)[sn_idx];
            }

            sn.server_name[server_name_len] = 0;
            goto end;
        }

        if (ext->len > 2048)
        {
            goto end;
        }

        if (data_end < cursor + ext->len)
        {
            goto end;
        }

        cursor += ext->len;
    }

end:
    return XDP_PASS;
}

这里的局限性很明显,即使我们有31个只有几个字节的扩展,第32位也永远不会大于2048字节。可能有一种方法可以跟踪到目前为止所有扩展的总和,并检查这个总和是否不超过 65536,从而允许我们摆脱这些“最坏情况”常量并检查实际的 umax_value,但我将把它作为其他人的研究课题。

除了 Dylan 的出色回答之外,从 SNI 复制服务器名称的内部 for 循环的替代方法是:

                        struct server_name sn = {"a"};
                        bpf_core_read(sn.server_name, server_name_len, data);

这从 data 复制到 sn.server_name

这避免了边界检查。需要将结构初始化为默认值。否则,如果您调用任何 BPF 辅助函数,例如 bpf_map_update_elem.

,您将得到 invalid indirect read from stack ... 错误