Re: [PATCH net v2] tun: disallow setting IEEE 802.15.4 link types via TUNSETLINK
From: Willem de Bruijn
Date: Fri Nov 06 2026 - 11:59:18 EST
On Fri, Nov 6, 2026 at 9:32 AM Kalpesh Solanki <skalpesh942@xxxxxxxxx> wrote:
>
> A userspace application can set the link type of a TUN/TAP interface
> to ARPHRD_IEEE802154 or ARPHRD_IEEE802154_MONITOR via ioctl(fd,
> TUNSETLINK, arg) because tun_get_addr_len() explicitly recognized
> these types.
>
> However, TUN devices cannot function as IEEE 802.15.4 radios (they
> lack wpan_phy, channels, PAN IDs, and MLME operations), and tun
> never initializes dev->ieee802154_ptr or dev->ml_priv.
>
> The IEEE 802.15.4 subsystem operates on the invariant that any
> net_device with dev->type == ARPHRD_IEEE802154 is a genuine WPAN device
> with a valid dev->ieee802154_ptr context. When an unprivileged user
> creates a TUN interface in a user+net namespace, changes its link type
> to ARPHRD_IEEE802154, and issues a netlink dump request (such as
> IEEE802154_LLSEC_LIST_KEY), ieee802154_llsec_dump_table()
> unconditionally dereferences dev->ieee802154_ptr /
> ieee802154_mlme_ops(dev), triggering a supervisor null-pointer
> dereference / general protection fault. Similar dereferences occur
> across net/ieee802154/nl-phy.c (ieee802154_del_iface) and
> net/ieee802154/socket.c.
>
> Rather than scattering NULL checks across every site in net/ieee802154/,
> fix the issue at the source: validate that arg fits within unsigned short
> (rejecting arg > USHRT_MAX to prevent upper-bit truncation), disallow
> setting ARPHRD_IEEE802154 and ARPHRD_IEEE802154_MONITOR via TUNSETLINK
> by returning -EINVAL, and remove these unsupported types from
> tun_get_addr_len() along with the unused <linux/ieee802154.h> include.
I don't think we can do this.
TUNSETLINK is a weird quirk. It never intended to fully change the link
layer. IFF_TAP still only supports Ethernet (calls eth_type_trans).
It was added in commit ff4cc3ac93e1 ("[TUNTAP]: Allow setting the linktype
of the tap device from userspace"), seemingly exactly to be able to process
other protocols.
Kismet would write captured 802.11 frames into a tap. The device would be
labelled ARPHRD_IEEE80211* so that tcpdump or wireshark interface would
see the "right" link type for processing.
> Fixes: cca8ea3b05c9 ("net: tun: set tun->dev->addr_len during TUNSETLINK processing")
> Reported-by: syzbot+9ea34da5ca6100593c90@xxxxxxxxxxxxxxxxxxxxxxxxx
> Closes: https://syzkaller.appspot.com/bug?extid=9ea34da5ca6100593c90
> Signed-off-by: Kalpesh Solanki <skalpesh942@xxxxxxxxx>
> ---
> v1 -> v2:
> - Instead of adding NULL checks in net/ieee802154/nl-mac.c, disallow setting
> ARPHRD_IEEE802154 and ARPHRD_IEEE802154_MONITOR link types via TUNSETLINK in
> drivers/net/tun.c, as discussed with Andrew Lunn and identified in Sashiko
> bot review. Virtual IEEE 802.15.4 interfaces are properly provided by
> mac802154_hwsim and fakelb; TUN devices cannot function as genuine 802.15.4
> radios and lack private 802.15.4 contexts.
> - Add an explicit bounds check (arg > USHRT_MAX) to prevent integer truncation
> bypasses when casting 64-bit ioctl arg to 16-bit dev->type.
> - Remove ARPHRD_IEEE802154 and ARPHRD_IEEE802154_MONITOR from tun_get_addr_len()
> and drop unused <linux/ieee802154.h> header.
>
> drivers/net/tun.c | 45 ++++++++++++++++++++++++++-------------------
> 1 file changed, 26 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index dcec7311e60..f2915e929e3 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -70,7 +70,6 @@
> #include <linux/bpf.h>
> #include <linux/bpf_trace.h>
> #include <linux/mutex.h>
> -#include <linux/ieee802154.h>
> #include <uapi/linux/if_ltalk.h>
> #include <uapi/linux/if_fddi.h>
> #include <uapi/linux/if_hippi.h>
> @@ -3183,9 +3182,6 @@ static unsigned char tun_get_addr_len(unsigned short type)
> return 4;
> case ARPHRD_ETHER:
> return ETH_ALEN;
> - case ARPHRD_IEEE802154:
> - case ARPHRD_IEEE802154_MONITOR:
> - return IEEE802154_EXTENDED_ADDR_LEN;
> case ARPHRD_PHONET_PIPE:
> case ARPHRD_PPP:
> case ARPHRD_NONE:
> @@ -3362,22 +3358,33 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
> netif_info(tun, drv, tun->dev,
> "Linktype set failed because interface is up\n");
> ret = -EBUSY;
> - } else {
> - ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE,
> - tun->dev);
> - ret = notifier_to_errno(ret);
> - if (ret) {
> - netif_info(tun, drv, tun->dev,
> - "Refused to change device type\n");
> - break;
> - }
> - tun->dev->type = (int) arg;
> - tun->dev->addr_len = tun_get_addr_len(tun->dev->type);
> - netif_info(tun, drv, tun->dev, "linktype set to %d\n",
> - tun->dev->type);
> - call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE,
> - tun->dev);
> + break;
> + }
> +
> + if (arg > USHRT_MAX) {
> + ret = -EINVAL;
> + break;
> + }
> +
> + if (arg == ARPHRD_IEEE802154 || arg == ARPHRD_IEEE802154_MONITOR) {
> + ret = -EINVAL;
> + break;
> + }
> +
> + ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE,
> + tun->dev);
> + ret = notifier_to_errno(ret);
> + if (ret) {
> + netif_info(tun, drv, tun->dev,
> + "Refused to change device type\n");
> + break;
> }
> + tun->dev->type = (unsigned short)arg;
> + tun->dev->addr_len = tun_get_addr_len(tun->dev->type);
> + netif_info(tun, drv, tun->dev, "linktype set to %d\n",
> + tun->dev->type);
> + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE,
> + tun->dev);
> break;
>
> case TUNSETDEBUG:
> --
> 2.53.0
>
>