bpf: allow BPF read access to qdisc pkt_len
Subsystems (6)
PATCH v2 1/1 bpf: allow BPF read access to qdisc pkt_len
From: Petar Penkov <[email protected]>
The pkt_len field in qdisc_skb_cb stores the skb length as it will appear on the wire after segmentation. For byte accounting, this value is more accurate than skb->len. It is computed on entry to the TC layer, so only valid there.
Allow read access to this field from BPF tc classifier and action programs. The implementation is analogous to tc_classid, aside from restricting to read access.
To distinguish it from skb->len and self-describe export as wire_len.
Changes v1->v2
- Rename pkt_len to wire_len
--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 16 +++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 32 +++++++++++++++++++++ 4 files changed, 50 insertions(+)
include/uapi/linux/bpf.h +1 -0
@@ -2497,6 +2497,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys;__u64 tstamp; +__u32 wire_len; };struct bpf_tunnel_key {
net/core/filter.c +16 -0
@@ -5773,6 +5773,7 @@ static bool sk_filter_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range_till(struct __sk_buff, family, local_port): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }
@@ -5797,6 +5798,7 @@ static bool cg_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; case bpf_ctx_range(struct __sk_buff, data): case bpf_ctx_range(struct __sk_buff, data_end):
@@ -5843,6 +5845,7 @@ static bool lwt_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }
@@ -6273,6 +6276,7 @@ static bool sk_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }
@@ -6360,6 +6364,7 @@ static bool flow_dissector_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range_till(struct __sk_buff, family, local_port): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }
@@ -6685,6 +6690,17 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, bpf_target_off(struct sk_buff, tstamp,8 , target_size)); +break ; + +case offsetof(struct __sk_buff , wire_len): + BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, pkt_len) != 4); + + off = si->off; + off -= offsetof(struct __sk_buff , wire_len); + off += offsetof(struct sk_buff , cb); + off += offsetof(struct qdisc_skb_cb , pkt_len); + *target_size =4 ; + *insn++ =BPF_LDX_MEM (BPF_W , si->dst_reg, si->src_reg, off); }return insn - insn_buf;
tools/include/uapi/linux/bpf.h +1 -0
@@ -2497,6 +2497,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys;__u64 tstamp; +__u32 wire_len; };struct bpf_tunnel_key {
tools/testing/selftests/bpf/test_verifier.c +32 -0
@@ -14033,6 +14033,38 @@ static struct bpf_test tests[] = { .result_unpriv = REJECT, .result = ACCEPT, }, + { + "check wire_len is not readable by sockets", + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_1 , + offsetof(struct __sk_buff , wire_len)), + BPF_EXIT_INSN(), + }, + .errstr ="invalid bpf_context access" , + .result = REJECT, + }, + { +"check wire_len is readable by tc classifier" , + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_1 , + offsetof(struct __sk_buff , wire_len)), + BPF_EXIT_INSN(), + }, + .prog_type =BPF_PROG_TYPE_SCHED_CLS , + .result = ACCEPT, + }, + { +"check wire_len is not writable by tc classifier" , + .insns = { +BPF_STX_MEM (BPF_W ,BPF_REG_1 ,BPF_REG_1 , + offsetof(struct __sk_buff , wire_len)), + BPF_EXIT_INSN(), + }, + .prog_type =BPF_PROG_TYPE_SCHED_CLS , + .errstr = "invalid bpf_context access", + .errstr_unpriv = "R1 leaks addr", + .result = REJECT, + }, };static int probe_filter_length (const struct bpf_insn *fp)
Signature · 1 line
2.20.0.rc1.387.gf8505762e3-goog
Quoted 12 lines · Willem de Bruijn
From: Petar Penkov <[email protected]>
The
pkt_lenfield inqdisc_skb_cbstores the skb length as it will appear on the wire after segmentation. For byte accounting, this value is more accurate thanskb->len. It is computed on entry to the TC layer, so only valid there.Allow read access to this field from BPF tc classifier and action programs. The implementation is analogous to
tc_classid, aside from restricting to read access.
Quoted 8 lines
To distinguish it from
skb->lenand self-describe export aswire_len.Changes
v1->v2
- Renamepkt_lentowire_lenSigned-off-by Petar Penkov <[email protected]>Signed-off-by Vlad Dumitrescu <[email protected]>Signed-off-by Willem de Bruijn <[email protected]>
Quoted 139 lines
--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 16 +++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 32 +++++++++++++++++++++ 4 files changed, 50 insertions(+)include/uapi/linux/bpf.h +1
-0@@ -2497,6 +2497,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys;__u64 tstamp; +__u32 wire_len; };struct bpf_tunnel_key {net/core/filter.c +16
-0@@ -5773,6 +5773,7 @@ static bool sk_filter_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range_till(struct __sk_buff, family, local_port): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }@@ -5797,6 +5798,7 @@ static bool cg_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; case bpf_ctx_range(struct __sk_buff, data): case bpf_ctx_range(struct __sk_buff, data_end):@@ -5843,6 +5845,7 @@ static bool lwt_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }@@ -6273,6 +6276,7 @@ static bool sk_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }@@ -6360,6 +6364,7 @@ static bool flow_dissector_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range_till(struct __sk_buff, family, local_port): case bpf_ctx_range(struct __sk_buff, tstamp): + case bpf_ctx_range(struct __sk_buff, wire_len):return false; }@@ -6685,6 +6690,17 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, bpf_target_off(struct sk_buff, tstamp,8 , target_size)); +break ; + +case offsetof(struct __sk_buff , wire_len): + BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, pkt_len) != 4); + + off = si->off; + off -= offsetof(struct __sk_buff , wire_len); + off += offsetof(struct sk_buff , cb); + off += offsetof(struct qdisc_skb_cb , pkt_len); + *target_size =4 ; + *insn++ =BPF_LDX_MEM (BPF_W , si->dst_reg, si->src_reg, off); }return insn - insn_buf;tools/include/uapi/linux/bpf.h +1
-0@@ -2497,6 +2497,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys;__u64 tstamp; +__u32 wire_len; };struct bpf_tunnel_key {tools/testing/selftests/bpf/test_verifier.c +32
-1@@ -14033,6 +14033,38 @@ static struct bpf_test tests[] = { .result_unpriv = REJECT, .result = ACCEPT, }, + { + "check wire_len is not readable by sockets", + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_1 , + offsetof(struct __sk_buff , wire_len)), + BPF_EXIT_INSN(), + }, + .errstr ="invalid bpf_context access" , + .result = REJECT, + }, + { +"check wire_len is readable by tc classifier" , + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_1 , + offsetof(struct __sk_buff , wire_len)), + BPF_EXIT_INSN(), + }, + .prog_type =BPF_PROG_TYPE_SCHED_CLS , + .result = ACCEPT, + }, + { +"check wire_len is not writable by tc classifier" , + .insns = { +BPF_STX_MEM (BPF_W ,BPF_REG_1 ,BPF_REG_1 , + offsetof(struct __sk_buff , wire_len)), + BPF_EXIT_INSN(), + }, + .prog_type =BPF_PROG_TYPE_SCHED_CLS , + .errstr = "invalid bpf_context access", + .errstr_unpriv = "R1 leaks addr", + .result = REJECT, + }, };static int probe_filter_length (const struct bpf_insn *fp) --2.20.0.rc1.387.gf8505762e3-goog
Quoted 11 lines · Willem de Bruijn
From: Petar Penkov <[email protected]>
The
pkt_lenfield inqdisc_skb_cbstores the skb length as it will appear on the wire after segmentation. For byte accounting, this value is more accurate thanskb->len. It is computed on entry to the TC layer, so only valid there.Allow read access to this field from BPF tc classifier and action programs. The implementation is analogous to
tc_classid, aside from restricting to read access.
Quoted 8 lines
To distinguish it from
skb->lenand self-describe export aswire_len.Changes
v1->v2
- Renamepkt_lentowire_lenSigned-off-by Petar Penkov <[email protected]>Signed-off-by Vlad Dumitrescu <[email protected]>Signed-off-by Willem de Bruijn <[email protected]>
Applied to bpf-next, thanks!