aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2018-02-15 17:47:15 -0500
committerDavid S. Miller <davem@davemloft.net>2018-02-16 16:20:46 -0500
commit43a08e0f58b3f236165029710a4e3b303815253b (patch)
tree2aaccadbb3aef32878c6d8f08f88e4534d2e4c79
parent15f35d49c93f4fa9875235e7bf3e3783d2dd7a1b (diff)
tun: fix tun_napi_alloc_frags() frag allocator
<Mark Rutland reported> While fuzzing arm64 v4.16-rc1 with Syzkaller, I've been hitting a misaligned atomic in __skb_clone:         atomic_inc(&(skb_shinfo(skb)->dataref)); where dataref doesn't have the required natural alignment, and the atomic operation faults. e.g. i often see it aligned to a single byte boundary rather than a four byte boundary. AFAICT, the skb_shared_info is misaligned at the instant it's allocated in __napi_alloc_skb() __napi_alloc_skb() </end of report> Problem is caused by tun_napi_alloc_frags() using napi_alloc_frag() with user provided seg sizes, leading to other users of this API getting unaligned page fragments. Since we would like to not necessarily add paddings or alignments to the frags that tun_napi_alloc_frags() attaches to the skb, switch to another page frag allocator. As a bonus skb_page_frag_refill() can use GFP_KERNEL allocations, meaning that we can not deplete memory reserves as easily. Fixes: 90e33d459407 ("tun: enable napi_gro_frags() for TUN/TAP driver") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Mark Rutland <mark.rutland@arm.com> Tested-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/tun.c16
1 files changed, 6 insertions, 10 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 81e6cc951e7f..b52258c327d2 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1489,27 +1489,23 @@ static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,
1489 skb->truesize += skb->data_len; 1489 skb->truesize += skb->data_len;
1490 1490
1491 for (i = 1; i < it->nr_segs; i++) { 1491 for (i = 1; i < it->nr_segs; i++) {
1492 struct page_frag *pfrag = &current->task_frag;
1492 size_t fragsz = it->iov[i].iov_len; 1493 size_t fragsz = it->iov[i].iov_len;
1493 unsigned long offset;
1494 struct page *page;
1495 void *data;
1496 1494
1497 if (fragsz == 0 || fragsz > PAGE_SIZE) { 1495 if (fragsz == 0 || fragsz > PAGE_SIZE) {
1498 err = -EINVAL; 1496 err = -EINVAL;
1499 goto free; 1497 goto free;
1500 } 1498 }
1501 1499
1502 local_bh_disable(); 1500 if (!skb_page_frag_refill(fragsz, pfrag, GFP_KERNEL)) {
1503 data = napi_alloc_frag(fragsz);
1504 local_bh_enable();
1505 if (!data) {
1506 err = -ENOMEM; 1501 err = -ENOMEM;
1507 goto free; 1502 goto free;
1508 } 1503 }
1509 1504
1510 page = virt_to_head_page(data); 1505 skb_fill_page_desc(skb, i - 1, pfrag->page,
1511 offset = data - page_address(page); 1506 pfrag->offset, fragsz);
1512 skb_fill_page_desc(skb, i - 1, page, offset, fragsz); 1507 page_ref_inc(pfrag->page);
1508 pfrag->offset += fragsz;
1513 } 1509 }
1514 1510
1515 return skb; 1511 return skb;