Skip to content

Commit

Permalink
bpf: fix cb access in socket filter programs
Browse files Browse the repository at this point in the history
eBPF socket filter programs may see junk in 'u32 cb[5]' area,
since it could have been used by protocol layers earlier.

For socket filter programs used in af_packet we need to clean
20 bytes of skb->cb area if it could be used by the program.
For programs attached to TCP/UDP sockets we need to save/restore
these 20 bytes, since it's used by protocol layers.

Remove SK_RUN_FILTER macro, since it's no longer used.

Long term we may move this bpf cb area to per-cpu scratch, but that
requires addition of new 'per-cpu load/store' instructions,
so not suitable as a short term fix.

Fixes: d691f9e ("bpf: allow programs to write to certain skb fields")
Reported-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Alexei Starovoitov authored and David S. Miller committed Oct 11, 2015
1 parent d49ae37 commit ff936a0
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 18 deletions.
6 changes: 3 additions & 3 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ enum bpf_access_type {
BPF_WRITE = 2
};

struct bpf_prog;

struct bpf_verifier_ops {
/* return eBPF function prototype for verification */
const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id);
Expand All @@ -111,7 +113,7 @@ struct bpf_verifier_ops {

u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg,
int src_reg, int ctx_off,
struct bpf_insn *insn);
struct bpf_insn *insn, struct bpf_prog *prog);
};

struct bpf_prog_type_list {
Expand All @@ -120,8 +122,6 @@ struct bpf_prog_type_list {
enum bpf_prog_type type;
};

struct bpf_prog;

struct bpf_prog_aux {
atomic_t refcnt;
u32 used_map_cnt;
Expand Down
39 changes: 35 additions & 4 deletions include/linux/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/printk.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <net/sch_generic.h>

#include <asm/cacheflush.h>

Expand Down Expand Up @@ -302,10 +303,6 @@ struct bpf_prog_aux;
bpf_size; \
})

/* Macro to invoke filter function. */
#define SK_RUN_FILTER(filter, ctx) \
(*filter->prog->bpf_func)(ctx, filter->prog->insnsi)

#ifdef CONFIG_COMPAT
/* A struct sock_filter is architecture independent. */
struct compat_sock_fprog {
Expand All @@ -329,6 +326,7 @@ struct bpf_prog {
kmemcheck_bitfield_begin(meta);
u16 jited:1, /* Is our filter JIT'ed? */
gpl_compatible:1, /* Is filter GPL compatible? */
cb_access:1, /* Is control block accessed? */
dst_needed:1; /* Do we need dst entry? */
kmemcheck_bitfield_end(meta);
u32 len; /* Number of filter blocks */
Expand All @@ -352,6 +350,39 @@ struct sk_filter {

#define BPF_PROG_RUN(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi)

static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
struct sk_buff *skb)
{
u8 *cb_data = qdisc_skb_cb(skb)->data;
u8 saved_cb[QDISC_CB_PRIV_LEN];
u32 res;

BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) !=
QDISC_CB_PRIV_LEN);

if (unlikely(prog->cb_access)) {
memcpy(saved_cb, cb_data, sizeof(saved_cb));
memset(cb_data, 0, sizeof(saved_cb));
}

res = BPF_PROG_RUN(prog, skb);

if (unlikely(prog->cb_access))
memcpy(cb_data, saved_cb, sizeof(saved_cb));

return res;
}

static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog,
struct sk_buff *skb)
{
u8 *cb_data = qdisc_skb_cb(skb)->data;

if (unlikely(prog->cb_access))
memset(cb_data, 0, QDISC_CB_PRIV_LEN);
return BPF_PROG_RUN(prog, skb);
}

static inline unsigned int bpf_prog_size(unsigned int proglen)
{
return max(sizeof(struct bpf_prog),
Expand Down
2 changes: 1 addition & 1 deletion kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -2024,7 +2024,7 @@ static int convert_ctx_accesses(struct verifier_env *env)

cnt = env->prog->aux->ops->
convert_ctx_access(type, insn->dst_reg, insn->src_reg,
insn->off, insn_buf);
insn->off, insn_buf, env->prog);
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
verbose("bpf verifier is misconfigured\n");
return -EINVAL;
Expand Down
12 changes: 7 additions & 5 deletions net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@
* @sk: sock associated with &sk_buff
* @skb: buffer to filter
*
* Run the filter code and then cut skb->data to correct size returned by
* SK_RUN_FILTER. If pkt_len is 0 we toss packet. If skb->len is smaller
* Run the eBPF program and then cut skb->data to correct size returned by
* the program. If pkt_len is 0 we toss packet. If skb->len is smaller
* than pkt_len we keep whole skb->data. This is the socket level
* wrapper to SK_RUN_FILTER. It returns 0 if the packet should
* wrapper to BPF_PROG_RUN. It returns 0 if the packet should
* be accepted or -EPERM if the packet should be tossed.
*
*/
Expand All @@ -83,7 +83,7 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
rcu_read_lock();
filter = rcu_dereference(sk->sk_filter);
if (filter) {
unsigned int pkt_len = SK_RUN_FILTER(filter, skb);
unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb);

err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
}
Expand Down Expand Up @@ -1736,7 +1736,8 @@ static bool tc_cls_act_is_valid_access(int off, int size,

static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
int src_reg, int ctx_off,
struct bpf_insn *insn_buf)
struct bpf_insn *insn_buf,
struct bpf_prog *prog)
{
struct bpf_insn *insn = insn_buf;

Expand Down Expand Up @@ -1827,6 +1828,7 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
offsetof(struct __sk_buff, cb[4]):
BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20);

prog->cb_access = 1;
ctx_off -= offsetof(struct __sk_buff, cb[0]);
ctx_off += offsetof(struct sk_buff, cb);
ctx_off += offsetof(struct qdisc_skb_cb, data);
Expand Down
10 changes: 5 additions & 5 deletions net/packet/af_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1423,7 +1423,7 @@ static unsigned int fanout_demux_bpf(struct packet_fanout *f,
rcu_read_lock();
prog = rcu_dereference(f->bpf_prog);
if (prog)
ret = BPF_PROG_RUN(prog, skb) % num;
ret = bpf_prog_run_clear_cb(prog, skb) % num;
rcu_read_unlock();

return ret;
Expand Down Expand Up @@ -1939,16 +1939,16 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
return err;
}

static unsigned int run_filter(const struct sk_buff *skb,
const struct sock *sk,
unsigned int res)
static unsigned int run_filter(struct sk_buff *skb,
const struct sock *sk,
unsigned int res)
{
struct sk_filter *filter;

rcu_read_lock();
filter = rcu_dereference(sk->sk_filter);
if (filter != NULL)
res = SK_RUN_FILTER(filter, skb);
res = bpf_prog_run_clear_cb(filter->prog, skb);
rcu_read_unlock();

return res;
Expand Down

0 comments on commit ff936a0

Please sign in to comment.