Documentation: Describe bpf reference tracking
Document the new pointer types in the verifier and how the pointer ID tracking works to ensure that references which are taken are later released. Signed-off-by: Joe Stringer <joe@wand.net.nz> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
parent
de375f4e91
commit
a610b665ec
|
@ -1125,6 +1125,14 @@ pointer type. The types of pointers describe their base, as follows:
|
|||
PTR_TO_STACK Frame pointer.
|
||||
PTR_TO_PACKET skb->data.
|
||||
PTR_TO_PACKET_END skb->data + headlen; arithmetic forbidden.
|
||||
PTR_TO_SOCKET Pointer to struct bpf_sock_ops, implicitly refcounted.
|
||||
PTR_TO_SOCKET_OR_NULL
|
||||
Either a pointer to a socket, or NULL; socket lookup
|
||||
returns this type, which becomes a PTR_TO_SOCKET when
|
||||
checked != NULL. PTR_TO_SOCKET is reference-counted,
|
||||
so programs must release the reference through the
|
||||
socket release function before the end of the program.
|
||||
Arithmetic on these pointers is forbidden.
|
||||
However, a pointer may be offset from this base (as a result of pointer
|
||||
arithmetic), and this is tracked in two parts: the 'fixed offset' and 'variable
|
||||
offset'. The former is used when an exactly-known value (e.g. an immediate
|
||||
|
@ -1171,6 +1179,13 @@ over the Ethernet header, then reads IHL and addes (IHL * 4), the resulting
|
|||
pointer will have a variable offset known to be 4n+2 for some n, so adding the 2
|
||||
bytes (NET_IP_ALIGN) gives a 4-byte alignment and so word-sized accesses through
|
||||
that pointer are safe.
|
||||
The 'id' field is also used on PTR_TO_SOCKET and PTR_TO_SOCKET_OR_NULL, common
|
||||
to all copies of the pointer returned from a socket lookup. This has similar
|
||||
behaviour to the handling for PTR_TO_MAP_VALUE_OR_NULL->PTR_TO_MAP_VALUE, but
|
||||
it also handles reference tracking for the pointer. PTR_TO_SOCKET implicitly
|
||||
represents a reference to the corresponding 'struct sock'. To ensure that the
|
||||
reference is not leaked, it is imperative to NULL-check the reference and in
|
||||
the non-NULL case, and pass the valid reference to the socket release function.
|
||||
|
||||
Direct packet access
|
||||
--------------------
|
||||
|
@ -1444,6 +1459,55 @@ Error:
|
|||
8: (7a) *(u64 *)(r0 +0) = 1
|
||||
R0 invalid mem access 'imm'
|
||||
|
||||
Program that performs a socket lookup then sets the pointer to NULL without
|
||||
checking it:
|
||||
value:
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 4),
|
||||
BPF_MOV64_IMM(BPF_REG_4, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_5, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (b7) r2 = 0
|
||||
1: (63) *(u32 *)(r10 -8) = r2
|
||||
2: (bf) r2 = r10
|
||||
3: (07) r2 += -8
|
||||
4: (b7) r3 = 4
|
||||
5: (b7) r4 = 0
|
||||
6: (b7) r5 = 0
|
||||
7: (85) call bpf_sk_lookup_tcp#65
|
||||
8: (b7) r0 = 0
|
||||
9: (95) exit
|
||||
Unreleased reference id=1, alloc_insn=7
|
||||
|
||||
Program that performs a socket lookup but does not NULL-check the returned
|
||||
value:
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 4),
|
||||
BPF_MOV64_IMM(BPF_REG_4, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_5, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (b7) r2 = 0
|
||||
1: (63) *(u32 *)(r10 -8) = r2
|
||||
2: (bf) r2 = r10
|
||||
3: (07) r2 += -8
|
||||
4: (b7) r3 = 4
|
||||
5: (b7) r4 = 0
|
||||
6: (b7) r5 = 0
|
||||
7: (85) call bpf_sk_lookup_tcp#65
|
||||
8: (95) exit
|
||||
Unreleased reference id=1, alloc_insn=7
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
|
|
Loading…
Reference in New Issue