VYPR
Unrated severityNVD Advisory· Published Jun 9, 2026

CVE-2026-46320

CVE-2026-46320

Description

In the Linux kernel, the following vulnerability has been resolved:

tap: free page on error paths in tap_get_user_xdp()

tap_get_user_xdp() rejects a frame shorter than ETH_HLEN with -EINVAL, and returns -ENOMEM when build_skb() fails. Both paths jump to the err label without freeing the page that vhost_net_build_xdp() allocated for the frame. tap_sendmsg() discards the per-buffer return value and always returns 0, so vhost_tx_batch() takes the success path and never frees the page; each rejected frame in a batch leaks one page-frag chunk.

Free the page on both error paths, before the skb is built. This is the tap counterpart of the same leak in tun_xdp_one().

Affected products

1

Patches

4
18a84c35842e

tap: free page on error paths in tap_get_user_xdp()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitWeiming ShiMay 21, 2026Fixed in 7.0.12via kernel-cna
1 file changed · +2 1
  • drivers/net/tap.c+2 1 modified
    diff --git a/drivers/net/tap.c b/drivers/net/tap.c
    index a590e07ce0a98..fae115915c8ef 100644
    --- a/drivers/net/tap.c
    +++ b/drivers/net/tap.c
    @@ -1052,6 +1052,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     	int err, depth;
     
     	if (unlikely(xdp->data_end - xdp->data < ETH_HLEN)) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -EINVAL;
     		goto err;
     	}
    @@ -1061,6 +1062,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     
     	skb = build_skb(xdp->data_hard_start, buflen);
     	if (!skb) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -ENOMEM;
     		goto err;
     	}
    -- 
    cgit 1.3-korg
    
    
    
3bcf7aec6a9d

tap: free page on error paths in tap_get_user_xdp()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitWeiming ShiMay 21, 2026Fixed in 7.1-rc6via kernel-cna
1 file changed · +2 1
  • drivers/net/tap.c+2 1 modified
    diff --git a/drivers/net/tap.c b/drivers/net/tap.c
    index a590e07ce0a98..fae115915c8ef 100644
    --- a/drivers/net/tap.c
    +++ b/drivers/net/tap.c
    @@ -1052,6 +1052,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     	int err, depth;
     
     	if (unlikely(xdp->data_end - xdp->data < ETH_HLEN)) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -EINVAL;
     		goto err;
     	}
    @@ -1061,6 +1062,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     
     	skb = build_skb(xdp->data_hard_start, buflen);
     	if (!skb) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -ENOMEM;
     		goto err;
     	}
    -- 
    cgit 1.3-korg
    
    
    
18a84c35842e

tap: free page on error paths in tap_get_user_xdp()

1 file changed · +2 1
  • drivers/net/tap.c+2 1 modified
    diff --git a/drivers/net/tap.c b/drivers/net/tap.c
    index a590e07ce0a98..fae115915c8ef 100644
    --- a/drivers/net/tap.c
    +++ b/drivers/net/tap.c
    @@ -1052,6 +1052,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     	int err, depth;
     
     	if (unlikely(xdp->data_end - xdp->data < ETH_HLEN)) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -EINVAL;
     		goto err;
     	}
    @@ -1061,6 +1062,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     
     	skb = build_skb(xdp->data_hard_start, buflen);
     	if (!skb) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -ENOMEM;
     		goto err;
     	}
    -- 
    cgit 1.3-korg
    
    
    
3bcf7aec6a9d

tap: free page on error paths in tap_get_user_xdp()

1 file changed · +2 1
  • drivers/net/tap.c+2 1 modified
    diff --git a/drivers/net/tap.c b/drivers/net/tap.c
    index a590e07ce0a98..fae115915c8ef 100644
    --- a/drivers/net/tap.c
    +++ b/drivers/net/tap.c
    @@ -1052,6 +1052,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     	int err, depth;
     
     	if (unlikely(xdp->data_end - xdp->data < ETH_HLEN)) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -EINVAL;
     		goto err;
     	}
    @@ -1061,6 +1062,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
     
     	skb = build_skb(xdp->data_hard_start, buflen);
     	if (!skb) {
    +		put_page(virt_to_head_page(xdp->data));
     		err = -ENOMEM;
     		goto err;
     	}
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Error paths in tap_get_user_xdp() fail to free allocated pages, leading to memory leaks."

Attack vector

An attacker can trigger this vulnerability by sending frames shorter than ETH_HLEN or by causing build_skb() to fail within the tap_get_user_xdp() function. When these error conditions occur, the allocated page is not freed. If this happens repeatedly within a batch operation handled by tap_sendmsg() and vhost_tx_batch(), it results in a memory leak, consuming page-frag chunks.

Affected code

The vulnerability exists in the `tap_get_user_xdp()` function within the `drivers/net/tap.c` file. Specifically, the error paths leading to `-EINVAL` (frame too short) and `-ENOMEM` (build_skb() failure) did not include logic to free the allocated page.

What the fix does

The patch adds calls to `put_page(virt_to_head_page(xdp->data))` on both error paths within the tap_get_user_xdp() function [patch_id=5354535]. This ensures that the page allocated by vhost_net_build_xdp() is freed when a frame is shorter than ETH_HLEN or when build_skb() fails. By freeing the page before the skb is built, the memory leak is prevented.

Preconditions

  • inputFrames shorter than ETH_HLEN or conditions causing build_skb() to fail.

Generated on Jun 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

1