Skip to content

Commit 66e3a13

Browse files
joannekoongAlexei Starovoitov
authored andcommitted
bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr
Two new kfuncs are added, bpf_dynptr_slice and bpf_dynptr_slice_rdwr. The user must pass in a buffer to store the contents of the data slice if a direct pointer to the data cannot be obtained. For skb and xdp type dynptrs, these two APIs are the only way to obtain a data slice. However, for other types of dynptrs, there is no difference between bpf_dynptr_slice(_rdwr) and bpf_dynptr_data. For skb type dynptrs, the data is copied into the user provided buffer if any of the data is not in the linear portion of the skb. For xdp type dynptrs, the data is copied into the user provided buffer if the data is between xdp frags. If the skb is cloned and a call to bpf_dynptr_data_rdwr is made, then the skb will be uncloned (see bpf_unclone_prologue()). Please note that any bpf_dynptr_write() automatically invalidates any prior data slices of the skb dynptr. This is because the skb may be cloned or may need to pull its paged buffer into the head. As such, any bpf_dynptr_write() will automatically have its prior data slices invalidated, even if the write is to data in the skb head of an uncloned skb. Please note as well that any other helper calls that change the underlying packet buffer (eg bpf_skb_pull_data()) invalidates any data slices of the skb dynptr as well, for the same reasons. Signed-off-by: Joanne Koong <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 05421ae commit 66e3a13

File tree

6 files changed

+288
-7
lines changed

6 files changed

+288
-7
lines changed

include/linux/filter.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,9 @@ int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
15481548
u32 len, u64 flags);
15491549
int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len);
15501550
int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len);
1551+
void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len);
1552+
void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
1553+
void *buf, unsigned long len, bool flush);
15511554
#else /* CONFIG_NET */
15521555
static inline int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset,
15531556
void *to, u32 len)
@@ -1572,6 +1575,17 @@ static inline int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset,
15721575
{
15731576
return -EOPNOTSUPP;
15741577
}
1578+
1579+
static inline void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
1580+
{
1581+
return NULL;
1582+
}
1583+
1584+
static inline void *bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off, void *buf,
1585+
unsigned long len, bool flush)
1586+
{
1587+
return NULL;
1588+
}
15751589
#endif /* CONFIG_NET */
15761590

15771591
#endif /* __LINUX_FILTER_H__ */

include/uapi/linux/bpf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5329,6 +5329,11 @@ union bpf_attr {
53295329
* *flags* must be 0 except for skb-type dynptrs.
53305330
*
53315331
* For skb-type dynptrs:
5332+
* * All data slices of the dynptr are automatically
5333+
* invalidated after **bpf_dynptr_write**\ (). This is
5334+
* because writing may pull the skb and change the
5335+
* underlying packet buffer.
5336+
*
53325337
* * For *flags*, please see the flags accepted by
53335338
* **bpf_skb_store_bytes**\ ().
53345339
* Return

kernel/bpf/helpers.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,142 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
21932193
return p;
21942194
}
21952195

2196+
/**
2197+
* bpf_dynptr_slice - Obtain a read-only pointer to the dynptr data.
2198+
*
2199+
* For non-skb and non-xdp type dynptrs, there is no difference between
2200+
* bpf_dynptr_slice and bpf_dynptr_data.
2201+
*
2202+
* If the intention is to write to the data slice, please use
2203+
* bpf_dynptr_slice_rdwr.
2204+
*
2205+
* The user must check that the returned pointer is not null before using it.
2206+
*
2207+
* Please note that in the case of skb and xdp dynptrs, bpf_dynptr_slice
2208+
* does not change the underlying packet data pointers, so a call to
2209+
* bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
2210+
* the bpf program.
2211+
*
2212+
* @ptr: The dynptr whose data slice to retrieve
2213+
* @offset: Offset into the dynptr
2214+
* @buffer: User-provided buffer to copy contents into
2215+
* @buffer__szk: Size (in bytes) of the buffer. This is the length of the
2216+
* requested slice. This must be a constant.
2217+
*
2218+
* @returns: NULL if the call failed (eg invalid dynptr), pointer to a read-only
2219+
* data slice (can be either direct pointer to the data or a pointer to the user
2220+
* provided buffer, with its contents containing the data, if unable to obtain
2221+
* direct pointer)
2222+
*/
2223+
__bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
2224+
void *buffer, u32 buffer__szk)
2225+
{
2226+
enum bpf_dynptr_type type;
2227+
u32 len = buffer__szk;
2228+
int err;
2229+
2230+
if (!ptr->data)
2231+
return 0;
2232+
2233+
err = bpf_dynptr_check_off_len(ptr, offset, len);
2234+
if (err)
2235+
return 0;
2236+
2237+
type = bpf_dynptr_get_type(ptr);
2238+
2239+
switch (type) {
2240+
case BPF_DYNPTR_TYPE_LOCAL:
2241+
case BPF_DYNPTR_TYPE_RINGBUF:
2242+
return ptr->data + ptr->offset + offset;
2243+
case BPF_DYNPTR_TYPE_SKB:
2244+
return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer);
2245+
case BPF_DYNPTR_TYPE_XDP:
2246+
{
2247+
void *xdp_ptr = bpf_xdp_pointer(ptr->data, ptr->offset + offset, len);
2248+
if (xdp_ptr)
2249+
return xdp_ptr;
2250+
2251+
bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer, len, false);
2252+
return buffer;
2253+
}
2254+
default:
2255+
WARN_ONCE(true, "unknown dynptr type %d\n", type);
2256+
return 0;
2257+
}
2258+
}
2259+
2260+
/**
2261+
* bpf_dynptr_slice_rdwr - Obtain a writable pointer to the dynptr data.
2262+
*
2263+
* For non-skb and non-xdp type dynptrs, there is no difference between
2264+
* bpf_dynptr_slice and bpf_dynptr_data.
2265+
*
2266+
* The returned pointer is writable and may point to either directly the dynptr
2267+
* data at the requested offset or to the buffer if unable to obtain a direct
2268+
* data pointer to (example: the requested slice is to the paged area of an skb
2269+
* packet). In the case where the returned pointer is to the buffer, the user
2270+
* is responsible for persisting writes through calling bpf_dynptr_write(). This
2271+
* usually looks something like this pattern:
2272+
*
2273+
* struct eth_hdr *eth = bpf_dynptr_slice_rdwr(&dynptr, 0, buffer, sizeof(buffer));
2274+
* if (!eth)
2275+
* return TC_ACT_SHOT;
2276+
*
2277+
* // mutate eth header //
2278+
*
2279+
* if (eth == buffer)
2280+
* bpf_dynptr_write(&ptr, 0, buffer, sizeof(buffer), 0);
2281+
*
2282+
* Please note that, as in the example above, the user must check that the
2283+
* returned pointer is not null before using it.
2284+
*
2285+
* Please also note that in the case of skb and xdp dynptrs, bpf_dynptr_slice_rdwr
2286+
* does not change the underlying packet data pointers, so a call to
2287+
* bpf_dynptr_slice_rdwr will not invalidate any ctx->data/data_end pointers in
2288+
* the bpf program.
2289+
*
2290+
* @ptr: The dynptr whose data slice to retrieve
2291+
* @offset: Offset into the dynptr
2292+
* @buffer: User-provided buffer to copy contents into
2293+
* @buffer__szk: Size (in bytes) of the buffer. This is the length of the
2294+
* requested slice. This must be a constant.
2295+
*
2296+
* @returns: NULL if the call failed (eg invalid dynptr), pointer to a
2297+
* data slice (can be either direct pointer to the data or a pointer to the user
2298+
* provided buffer, with its contents containing the data, if unable to obtain
2299+
* direct pointer)
2300+
*/
2301+
__bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
2302+
void *buffer, u32 buffer__szk)
2303+
{
2304+
if (!ptr->data || bpf_dynptr_is_rdonly(ptr))
2305+
return 0;
2306+
2307+
/* bpf_dynptr_slice_rdwr is the same logic as bpf_dynptr_slice.
2308+
*
2309+
* For skb-type dynptrs, it is safe to write into the returned pointer
2310+
* if the bpf program allows skb data writes. There are two possiblities
2311+
* that may occur when calling bpf_dynptr_slice_rdwr:
2312+
*
2313+
* 1) The requested slice is in the head of the skb. In this case, the
2314+
* returned pointer is directly to skb data, and if the skb is cloned, the
2315+
* verifier will have uncloned it (see bpf_unclone_prologue()) already.
2316+
* The pointer can be directly written into.
2317+
*
2318+
* 2) Some portion of the requested slice is in the paged buffer area.
2319+
* In this case, the requested data will be copied out into the buffer
2320+
* and the returned pointer will be a pointer to the buffer. The skb
2321+
* will not be pulled. To persist the write, the user will need to call
2322+
* bpf_dynptr_write(), which will pull the skb and commit the write.
2323+
*
2324+
* Similarly for xdp programs, if the requested slice is not across xdp
2325+
* fragments, then a direct pointer will be returned, otherwise the data
2326+
* will be copied out into the buffer and the user will need to call
2327+
* bpf_dynptr_write() to commit changes.
2328+
*/
2329+
return bpf_dynptr_slice(ptr, offset, buffer, buffer__szk);
2330+
}
2331+
21962332
__bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj)
21972333
{
21982334
return obj;
@@ -2262,6 +2398,8 @@ BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx)
22622398
BTF_ID_FLAGS(func, bpf_rdonly_cast)
22632399
BTF_ID_FLAGS(func, bpf_rcu_read_lock)
22642400
BTF_ID_FLAGS(func, bpf_rcu_read_unlock)
2401+
BTF_ID_FLAGS(func, bpf_dynptr_slice, KF_RET_NULL)
2402+
BTF_ID_FLAGS(func, bpf_dynptr_slice_rdwr, KF_RET_NULL)
22652403
BTF_SET8_END(common_btf_ids)
22662404

22672405
static const struct btf_kfunc_id_set common_kfunc_set = {

0 commit comments

Comments
 (0)