Skip to content

Commit ce098da

Browse files
keeskuba-moo
authored andcommitted
skbuff: Introduce slab_build_skb()
syzkaller reported: BUG: KASAN: slab-out-of-bounds in __build_skb_around+0x235/0x340 net/core/skbuff.c:294 Write of size 32 at addr ffff88802aa172c0 by task syz-executor413/5295 For bpf_prog_test_run_skb(), which uses a kmalloc()ed buffer passed to build_skb(). When build_skb() is passed a frag_size of 0, it means the buffer came from kmalloc. In these cases, ksize() is used to find its actual size, but since the allocation may not have been made to that size, actually perform the krealloc() call so that all the associated buffer size checking will be correctly notified (and use the "new" pointer so that compiler hinting works correctly). Split this logic out into a new interface, slab_build_skb(), but leave the original 0 checking for now to catch any stragglers. Reported-by: [email protected] Link: https://groups.google.com/g/syzkaller-bugs/c/UnIKxTtU5-0/m/-wbXinkgAQAJ Fixes: 38931d8 ("mm: Make ksize() a reporting-only function") Cc: Pavel Begunkov <[email protected]> Cc: pepsipu <[email protected]> Cc: [email protected] Cc: Vlastimil Babka <[email protected]> Cc: kasan-dev <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: [email protected] Cc: Daniel Borkmann <[email protected]> Cc: Hao Luo <[email protected]> Cc: Jesper Dangaard Brouer <[email protected]> Cc: John Fastabend <[email protected]> Cc: [email protected] Cc: KP Singh <[email protected]> Cc: [email protected] Cc: Stanislav Fomichev <[email protected]> Cc: [email protected] Cc: Yonghong Song <[email protected]> Signed-off-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 28d3950 commit ce098da

File tree

5 files changed

+66
-11
lines changed

5 files changed

+66
-11
lines changed

drivers/net/ethernet/broadcom/bnx2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3045,7 +3045,7 @@ bnx2_rx_skb(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, u8 *data,
30453045

30463046
dma_unmap_single(&bp->pdev->dev, dma_addr, bp->rx_buf_use_size,
30473047
DMA_FROM_DEVICE);
3048-
skb = build_skb(data, 0);
3048+
skb = slab_build_skb(data);
30493049
if (!skb) {
30503050
kfree(data);
30513051
goto error;

drivers/net/ethernet/qlogic/qed/qed_ll2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ static void qed_ll2b_complete_rx_packet(void *cxt,
200200
dma_unmap_single(&cdev->pdev->dev, buffer->phys_addr,
201201
cdev->ll2->rx_size, DMA_FROM_DEVICE);
202202

203-
skb = build_skb(buffer->data, 0);
203+
skb = slab_build_skb(buffer->data);
204204
if (!skb) {
205205
DP_INFO(cdev, "Failed to build SKB\n");
206206
kfree(buffer->data);

include/linux/skbuff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,7 @@ struct sk_buff *build_skb_around(struct sk_buff *skb,
12551255
void skb_attempt_defer_free(struct sk_buff *skb);
12561256

12571257
struct sk_buff *napi_build_skb(void *data, unsigned int frag_size);
1258+
struct sk_buff *slab_build_skb(void *data);
12581259

12591260
/**
12601261
* alloc_skb - allocate a network buffer

net/bpf/test_run.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1128,7 +1128,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
11281128
}
11291129
sock_init_data(NULL, sk);
11301130

1131-
skb = build_skb(data, 0);
1131+
skb = slab_build_skb(data);
11321132
if (!skb) {
11331133
kfree(data);
11341134
kfree(ctx);

net/core/skbuff.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,10 @@ static struct sk_buff *napi_skb_cache_get(void)
270270
return skb;
271271
}
272272

273-
/* Caller must provide SKB that is memset cleared */
274-
static void __build_skb_around(struct sk_buff *skb, void *data,
275-
unsigned int frag_size)
273+
static inline void __finalize_skb_around(struct sk_buff *skb, void *data,
274+
unsigned int size)
276275
{
277276
struct skb_shared_info *shinfo;
278-
unsigned int size = frag_size ? : ksize(data);
279277

280278
size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
281279

@@ -297,15 +295,71 @@ static void __build_skb_around(struct sk_buff *skb, void *data,
297295
skb_set_kcov_handle(skb, kcov_common_handle());
298296
}
299297

298+
static inline void *__slab_build_skb(struct sk_buff *skb, void *data,
299+
unsigned int *size)
300+
{
301+
void *resized;
302+
303+
/* Must find the allocation size (and grow it to match). */
304+
*size = ksize(data);
305+
/* krealloc() will immediately return "data" when
306+
* "ksize(data)" is requested: it is the existing upper
307+
* bounds. As a result, GFP_ATOMIC will be ignored. Note
308+
* that this "new" pointer needs to be passed back to the
309+
* caller for use so the __alloc_size hinting will be
310+
* tracked correctly.
311+
*/
312+
resized = krealloc(data, *size, GFP_ATOMIC);
313+
WARN_ON_ONCE(resized != data);
314+
return resized;
315+
}
316+
317+
/* build_skb() variant which can operate on slab buffers.
318+
* Note that this should be used sparingly as slab buffers
319+
* cannot be combined efficiently by GRO!
320+
*/
321+
struct sk_buff *slab_build_skb(void *data)
322+
{
323+
struct sk_buff *skb;
324+
unsigned int size;
325+
326+
skb = kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC);
327+
if (unlikely(!skb))
328+
return NULL;
329+
330+
memset(skb, 0, offsetof(struct sk_buff, tail));
331+
data = __slab_build_skb(skb, data, &size);
332+
__finalize_skb_around(skb, data, size);
333+
334+
return skb;
335+
}
336+
EXPORT_SYMBOL(slab_build_skb);
337+
338+
/* Caller must provide SKB that is memset cleared */
339+
static void __build_skb_around(struct sk_buff *skb, void *data,
340+
unsigned int frag_size)
341+
{
342+
unsigned int size = frag_size;
343+
344+
/* frag_size == 0 is considered deprecated now. Callers
345+
* using slab buffer should use slab_build_skb() instead.
346+
*/
347+
if (WARN_ONCE(size == 0, "Use slab_build_skb() instead"))
348+
data = __slab_build_skb(skb, data, &size);
349+
350+
__finalize_skb_around(skb, data, size);
351+
}
352+
300353
/**
301354
* __build_skb - build a network buffer
302355
* @data: data buffer provided by caller
303-
* @frag_size: size of data, or 0 if head was kmalloced
356+
* @frag_size: size of data (must not be 0)
304357
*
305358
* Allocate a new &sk_buff. Caller provides space holding head and
306-
* skb_shared_info. @data must have been allocated by kmalloc() only if
307-
* @frag_size is 0, otherwise data should come from the page allocator
308-
* or vmalloc()
359+
* skb_shared_info. @data must have been allocated from the page
360+
* allocator or vmalloc(). (A @frag_size of 0 to indicate a kmalloc()
361+
* allocation is deprecated, and callers should use slab_build_skb()
362+
* instead.)
309363
* The return is the new skb buffer.
310364
* On a failure the return is %NULL, and @data is not freed.
311365
* Notes :

0 commit comments

Comments
 (0)