Re: [PATCH bpf] bpf: Fix out-of-bounds read of rtt_min in sock_ops
From: Emil Tsalapatis
Date: Wed Sep 09 2026 - 15:53:38 EST
On Thu, Sep 3, 2026 at 6:10 AM Jiayuan Chen <jiayuan.chen@xxxxxxxxx> wrote:
>
> A sockops prog reading skops->rtt_min never checks the sk type: on the
> tcp_conn_request() path sock_ops->sk is a request_sock (non-full), and the
> ctx rewrite casts it to a tcp_sock (full) and reads rtt_min past the end of
> the request_sock, returning dirty adjacent memory.
>
> SEC("sockops")
> int prog(struct bpf_sock_ops *skops)
> {
> switch (skops->op) {
> case BPF_SOCK_OPS_RWND_INIT:
> leak = skops->rtt_min; /* reads the request_sock OOB */
> ...
> }
> }
>
> For instance one such read returned rtt_min=0xffff8881, the high half of a
> leaked kernel pointer.
>
> Guarding that cast is exactly what SOCK_OPS_GET_FIELD() does -- it checks
> is_locked_tcp_sock and returns 0 when sock_ops->sk is not a locked full
> socket. Every other tcp_sock field in sock_ops goes through it; rtt_min is
> the only one open-coded, so it skips the check.
>
> Read rtt_min through SOCK_OPS_GET_FIELD() too. rtt_min is a bit special:
> it is a struct minmax and we only want the current min, so pass
> rtt_min.s[0].v. That is equivalent to the old hand-computed offset
>
> offsetof(struct tcp_sock, rtt_min) + sizeof_field(struct minmax_sample, t)
>
> (s[0] sits at rtt_min + 0 and .v at + sizeof(.t), i.e. what minmax_get()
> returns), so the loaded field is unchanged and only the full-sock guard is
> added. The two BUILD_BUG_ON()s that protected the hand-computed offset
> are no longer needed.
>
> Before patch:
>
> 0: r1 = *(u64 *)(r1 +0) ; r1 = skops->sk
> 1: r1 = *(u32 *)(r1 +2324) ; ((tcp_sock *)sk)->rtt_min.s[0].v
>
> After patch:
>
> 0: *(u64 *)(r1 +56) = r9
> 1: r9 = *(u8 *)(r1 +50) ; is_locked_tcp_sock
> 2: if r9 == 0 goto pc+4 ; not a locked full sock -> 0
> 3: r9 = *(u64 *)(r1 +56)
> 4: r1 = *(u64 *)(r1 +0) ; r1 = skops->sk
> 5: r1 = *(u32 *)(r1 +2324) ; rtt_min.s[0].v
> 6: goto pc+2
> 7: r9 = *(u64 *)(r1 +56)
> 8: r1 = 0
>
> Fixes: 44f0e43037d3 ("bpf: Add support for reading sk_state and more")
> Reported-by: VEGA <vega@xxxxxxxxxx>
> Signed-off-by: Jiayuan Chen <jiayuan.chen@xxxxxxxxx>
> ---
Reviewed-by: Emil Tsalapatis <emil@xxxxxxxxxxxxxxx>
> net/core/filter.c | 13 +------------
> 1 file changed, 1 insertion(+), 12 deletions(-)
>
> diff --git a/net/core/filter.c b/net/core/filter.c
> index 61940e753552..a043d179ff1a 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -11105,18 +11105,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
> break;
>
> case offsetof(struct bpf_sock_ops, rtt_min):
> - BUILD_BUG_ON(sizeof_field(struct tcp_sock, rtt_min) !=
> - sizeof(struct minmax));
> - BUILD_BUG_ON(sizeof(struct minmax) <
> - sizeof(struct minmax_sample));
> -
> - *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(
> - struct bpf_sock_ops_kern, sk),
> - si->dst_reg, si->src_reg,
> - offsetof(struct bpf_sock_ops_kern, sk));
> - *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg,
> - offsetof(struct tcp_sock, rtt_min) +
> - sizeof_field(struct minmax_sample, t));
> + SOCK_OPS_GET_FIELD(rtt_min, rtt_min.s[0].v, struct tcp_sock);
> break;
>
> case offsetof(struct bpf_sock_ops, bpf_sock_ops_cb_flags):
> --
> 2.43.0
>