Message ID | 20211011191647.418704-5-john.fastabend@gmail.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | BPF |
Headers | show |
Series | bpf, sockmap: fixes stress testing and regression | expand |
On Mon, Oct 11, 2021 at 09:16 PM CEST, John Fastabend wrote: > From: Jussi Maki <joamaki@gmail.com> > > The current conversion of skb->data_end reads like this, > > ; data_end = (void*)(long)skb->data_end; > 559: (79) r1 = *(u64 *)(r2 +200) ; r1 = skb->data > 560: (61) r11 = *(u32 *)(r2 +112) ; r11 = skb->len > 561: (0f) r1 += r11 > 562: (61) r11 = *(u32 *)(r2 +116) > 563: (1f) r1 -= r11 > > But similar to the case > > ("bpf: sock_ops sk access may stomp registers when dst_reg = src_reg"), > > the code will read an incorrect skb->len when src == dst. In this case we > end up generating this xlated code. > > ; data_end = (void*)(long)skb->data_end; > 559: (79) r1 = *(u64 *)(r1 +200) ; r1 = skb->data > 560: (61) r11 = *(u32 *)(r1 +112) ; r11 = (skb->data)->len > 561: (0f) r1 += r11 > 562: (61) r11 = *(u32 *)(r1 +116) > 563: (1f) r1 -= r11 > > where line 560 is the reading 4B of (skb->data + 112) instead of the > intended skb->len Here the skb pointer in r1 gets set to skb->data and > the later deref for skb->len ends up following skb->data instead of skb. > > This fixes the issue similarly to the patch mentioned above by creating > an additional temporary variable and using to store the register when > dst_reg = src_reg. We name the variable bpf_temp_reg and place it in the > cb context for sk_skb. Then we restore from the temp to ensure nothing > is lost. > > Fixes: 16137b09a66f2 ("bpf: Compute data_end dynamically with JIT code") > Signed-off-by: Jussi Maki <joamaki@gmail.com> > Signed-off-by: John Fastabend <john.fastabend@gmail.com> > --- Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
diff --git a/include/net/strparser.h b/include/net/strparser.h index bec1439bd3be..732b7097d78e 100644 --- a/include/net/strparser.h +++ b/include/net/strparser.h @@ -66,6 +66,10 @@ struct sk_skb_cb { #define SK_SKB_CB_PRIV_LEN 20 unsigned char data[SK_SKB_CB_PRIV_LEN]; struct _strp_msg strp; + /* temp_reg is a temporary register used for bpf_convert_data_end_access + * when dst_reg == src_reg. + */ + u64 temp_reg; }; static inline struct strp_msg *strp_msg(struct sk_buff *skb) diff --git a/net/core/filter.c b/net/core/filter.c index 23a9bf92b5bb..f4a63af45f00 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -9735,22 +9735,46 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, static struct bpf_insn *bpf_convert_data_end_access(const struct bpf_insn *si, struct bpf_insn *insn) { - /* si->dst_reg = skb->data */ + int reg; + int temp_reg_off = offsetof(struct sk_buff, cb) + + offsetof(struct sk_skb_cb, temp_reg); + + if (si->src_reg == si->dst_reg) { + /* We need an extra register, choose and save a register. */ + reg = BPF_REG_9; + if (si->src_reg == reg || si->dst_reg == reg) + reg--; + if (si->src_reg == reg || si->dst_reg == reg) + reg--; + *insn++ = BPF_STX_MEM(BPF_DW, si->src_reg, reg, temp_reg_off); + } else { + reg = si->dst_reg; + } + + /* reg = skb->data */ *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, data), - si->dst_reg, si->src_reg, + reg, si->src_reg, offsetof(struct sk_buff, data)); /* AX = skb->len */ *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, len), BPF_REG_AX, si->src_reg, offsetof(struct sk_buff, len)); - /* si->dst_reg = skb->data + skb->len */ - *insn++ = BPF_ALU64_REG(BPF_ADD, si->dst_reg, BPF_REG_AX); + /* reg = skb->data + skb->len */ + *insn++ = BPF_ALU64_REG(BPF_ADD, reg, BPF_REG_AX); /* AX = skb->data_len */ *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, data_len), BPF_REG_AX, si->src_reg, offsetof(struct sk_buff, data_len)); - /* si->dst_reg = skb->data + skb->len - skb->data_len */ - *insn++ = BPF_ALU64_REG(BPF_SUB, si->dst_reg, BPF_REG_AX); + + /* reg = skb->data + skb->len - skb->data_len */ + *insn++ = BPF_ALU64_REG(BPF_SUB, reg, BPF_REG_AX); + + if (si->src_reg == si->dst_reg) { + /* Restore the saved register */ + *insn++ = BPF_MOV64_REG(BPF_REG_AX, si->src_reg); + *insn++ = BPF_MOV64_REG(si->dst_reg, reg); + *insn++ = BPF_LDX_MEM(BPF_DW, reg, BPF_REG_AX, temp_reg_off); + } return insn; }