fixed4
This commit is contained in:
1 parent
6cfca41b07
commit
727dd2e210
1 file changed
+23
-22
+23
-22
@@ -43,15 +43,6 @@ struct {
|
|||||||
__type(value, __u16); // MTU conhecido
|
__type(value, __u16); // MTU conhecido
|
||||||
} path_mtu_map SEC(".maps");
|
} path_mtu_map SEC(".maps");
|
||||||
|
|
||||||
// Barreira de compilador: impede o clang de "provar" (via -O2) que um
|
|
||||||
// ponteiro já está implicitamente dentro de um range válido por
|
|
||||||
// transitividade com outro ponteiro checado antes, e por isso eliminar do
|
|
||||||
// bytecode um check de bounds que na prática é redundante *matematicamente*
|
|
||||||
// mas que o verificador do kernel exige ver explicitamente antes de cada
|
|
||||||
// dereferência. Gotcha clássico de eBPF — aparece até nos selftests do
|
|
||||||
// próprio kernel para parsing de opções TCP.
|
|
||||||
#define barrier_var(var) asm volatile("" : "+r"(var))
|
|
||||||
|
|
||||||
// Atualização incremental de checksum de 16 bits (RFC 1071/1624) — a mesma
|
// Atualização incremental de checksum de 16 bits (RFC 1071/1624) — a mesma
|
||||||
// técnica usada pelo csum_replace2() do kernel. Necessária porque XDP não
|
// técnica usada pelo csum_replace2() do kernel. Necessária porque XDP não
|
||||||
// tem acesso a helpers de checksum baseados em skb (bpf_l4_csum_replace só
|
// tem acesso a helpers de checksum baseados em skb (bpf_l4_csum_replace só
|
||||||
@@ -84,19 +75,27 @@ static __always_inline int clamp_tcp_mss(struct tcphdr *tcp, void *data_end, __u
|
|||||||
if (tcp_hdr_len <= sizeof(struct tcphdr))
|
if (tcp_hdr_len <= sizeof(struct tcphdr))
|
||||||
return 0; // Sem opções TCP
|
return 0; // Sem opções TCP
|
||||||
|
|
||||||
|
// Bytes de opções restantes como CONTADOR ESCALAR, não como um segundo
|
||||||
|
// ponteiro (opt_end) comparado contra data_end. Isso é de propósito:
|
||||||
|
// se o limite do loop fosse outro ponteiro também verificado contra
|
||||||
|
// data_end, o clang consegue provar por transitividade que checar
|
||||||
|
// "opt+N > data_end" é redundante (já que opt < opt_end <= data_end) e
|
||||||
|
// REMOVE esse check do bytecode — o verificador do kernel então rejeita
|
||||||
|
// o programa porque, sem o check explícito no bytecode, não há prova
|
||||||
|
// de que a leitura é segura (viu-se isso na prática: nem barrier_var
|
||||||
|
// impediu, porque a eliminação é por análise de range de valores, não
|
||||||
|
// por CSE simples). Um contador inteiro não carrega esse tipo de
|
||||||
|
// relação com data_end, então cada dereferência só fica "provada seguro"
|
||||||
|
// pelo check explícito que a precede — que é exatamente o que o
|
||||||
|
// verificador exige ver.
|
||||||
|
__u8 opt_bytes_left = tcp_hdr_len - sizeof(struct tcphdr);
|
||||||
__u8 *opt = (__u8 *)(tcp + 1);
|
__u8 *opt = (__u8 *)(tcp + 1);
|
||||||
__u8 *opt_end = (__u8 *)tcp + tcp_hdr_len;
|
|
||||||
if ((void *)opt_end > data_end)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Loop limitado (verificador do BPF exige bound estático): cabeçalho
|
|
||||||
// TCP tem no máximo 40 bytes de opções, então 10 iterações cobrem até
|
|
||||||
// opções de 4 bytes cada — mais que suficiente para chegar no MSS,
|
|
||||||
// que por convenção vem logo no início das opções.
|
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
barrier_var(opt);
|
if (opt_bytes_left < 1)
|
||||||
if ((void *)(opt + 1) > data_end || opt >= opt_end)
|
break;
|
||||||
|
if ((void *)(opt + 1) > data_end)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
__u8 kind = opt[0];
|
__u8 kind = opt[0];
|
||||||
@@ -104,18 +103,19 @@ static __always_inline int clamp_tcp_mss(struct tcphdr *tcp, void *data_end, __u
|
|||||||
break;
|
break;
|
||||||
if (kind == 1) { // NOP
|
if (kind == 1) { // NOP
|
||||||
opt += 1;
|
opt += 1;
|
||||||
|
opt_bytes_left -= 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
barrier_var(opt);
|
if (opt_bytes_left < 2)
|
||||||
if ((void *)(opt + 2) > data_end || opt + 1 >= opt_end)
|
break;
|
||||||
|
if ((void *)(opt + 2) > data_end)
|
||||||
break;
|
break;
|
||||||
__u8 opt_len = opt[1];
|
__u8 opt_len = opt[1];
|
||||||
if (opt_len < 2 || (void *)(opt + opt_len) > data_end || opt + opt_len > opt_end)
|
if (opt_len < 2 || opt_len > opt_bytes_left)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (kind == 2 && opt_len == 4) { // Maximum Segment Size
|
if (kind == 2 && opt_len == 4) { // Maximum Segment Size
|
||||||
barrier_var(opt);
|
|
||||||
if ((void *)(opt + 4) > data_end)
|
if ((void *)(opt + 4) > data_end)
|
||||||
break;
|
break;
|
||||||
__be16 *mss_ptr = (__be16 *)(opt + 2);
|
__be16 *mss_ptr = (__be16 *)(opt + 2);
|
||||||
@@ -131,6 +131,7 @@ static __always_inline int clamp_tcp_mss(struct tcphdr *tcp, void *data_end, __u
|
|||||||
}
|
}
|
||||||
|
|
||||||
opt += opt_len;
|
opt += opt_len;
|
||||||
|
opt_bytes_left -= opt_len;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user