[Linux Kernel Bug] KASAN: null-ptr-deref Read in generic_hwtstamp_ioctl_lower

11 views
Skip to first unread message

Jiaming Zhang

unread,
Oct 29, 2025, 4:46:22 AM (12 days ago) Oct 29
to da...@davemloft.net, edum...@google.com, ku...@kernel.org, net...@vger.kernel.org, pab...@redhat.com, ho...@kernel.org, kory.m...@bootlin.com, kun...@google.com, linux-...@vger.kernel.org, s...@fomichev.me, syzk...@googlegroups.com
Dear Linux kernel developers and maintainers,

We are writing to report a null pointer dereference bug discovered in
the net subsystem. This bug is reproducible on the latest version
(v6.18-rc3, commit dcb6fa37fd7bc9c3d2b066329b0d27dedf8becaa).

The root cause is in tsconfig_prepare_data(), where a local
kernel_hwtstamp_config struct (cfg) is initialized using {}, setting
all its members to zero. Consequently, cfg.ifr becomes NULL.

cfg is then passed as: tsconfig_prepare_data() ->
dev_get_hwtstamp_phylib() -> vlan_hwtstamp_get() (via
dev->netdev_ops->ndo_hwtstamp_get) -> generic_hwtstamp_get_lower() ->
generic_hwtstamp_ioctl_lower().

The function generic_hwtstamp_ioctl_lower() assumes cfg->ifr is a
valid pointer and attempts to access cfg->ifr->ifr_ifru. This access
dereferences the NULL pointer, triggering the bug.

As a potential fix, we can declare a local struct ifreq variable in
tsconfig_prepare_data(), zero-initializing it, and then assigning its
address to cfg.ifr before calling dev_get_hwtstamp_phylib(). This
ensures that functions down the call chain receive a valid pointer.

If this solution is acceptable, we are happy to prepare and submit a
patch immediately.

The kernel console output, kernel config, syzkaller reproducer, and C
reproducer are attached to help with analysis. The KASAN report from
v6.18-rc3, formatted by syz-symbolize, is listed below:

---

chnl_net:caif_netlink_parms(): no params data found
bridge0: port 1(bridge_slave_0) entered blocking state
bridge0: port 1(bridge_slave_0) entered disabled state
bridge_slave_0: entered allmulticast mode
bridge_slave_0: entered promiscuous mode
bridge0: port 2(bridge_slave_1) entered blocking state
bridge0: port 2(bridge_slave_1) entered disabled state
bridge_slave_1: entered allmulticast mode
bridge_slave_1: entered promiscuous mode
bond0: (slave bond_slave_0): Enslaving as an active interface with an up link
bond0: (slave bond_slave_1): Enslaving as an active interface with an up link
team0: Port device team_slave_0 added
team0: Port device team_slave_1 added
batman_adv: batadv0: Adding interface: batadv_slave_0
batman_adv: batadv0: The MTU of interface batadv_slave_0 is too small
(1500) to handle the transport of batman-adv packets. Packets going
over this interface will be fragmented on layer2 which could impact
the performance. Setting the MTU to 1532 would solve the problem.
batman_adv: batadv0: Not using interface batadv_slave_0 (retrying
later): interface not active
batman_adv: batadv0: Adding interface: batadv_slave_1
batman_adv: batadv0: The MTU of interface batadv_slave_1 is too small
(1500) to handle the transport of batman-adv packets. Packets going
over this interface will be fragmented on layer2 which could impact
the performance. Setting the MTU to 1532 would solve the problem.
batman_adv: batadv0: Not using interface batadv_slave_1 (retrying
later): interface not active
hsr_slave_0: entered promiscuous mode
hsr_slave_1: entered promiscuous mode
netdevsim netdevsim0 netdevsim0: renamed from eth0
netdevsim netdevsim0 netdevsim1: renamed from eth1
netdevsim netdevsim0 netdevsim2: renamed from eth2
netdevsim netdevsim0 netdevsim3: renamed from eth3
bridge0: port 2(bridge_slave_1) entered blocking state
bridge0: port 2(bridge_slave_1) entered forwarding state
bridge0: port 1(bridge_slave_0) entered blocking state
bridge0: port 1(bridge_slave_0) entered forwarding state
8021q: adding VLAN 0 to HW filter on device bond0
8021q: adding VLAN 0 to HW filter on device team0
8021q: adding VLAN 0 to HW filter on device batadv0
veth0_vlan: entered promiscuous mode
veth1_vlan: entered promiscuous mode
veth0_macvtap: entered promiscuous mode
veth1_macvtap: entered promiscuous mode
batman_adv: batadv0: Interface activated: batadv_slave_0
batman_adv: batadv0: Interface activated: batadv_slave_1
==================================================================
BUG: KASAN: null-ptr-deref in generic_hwtstamp_ioctl_lower+0x190/0x2d0
net/core/dev_ioctl.c:447
Read of size 24 at addr 0000000000000010 by task repro.out/9702

CPU: 1 UID: 0 PID: 9702 Comm: repro.out Not tainted 6.18.0-rc3 #1 PREEMPT(full)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x1c1/0x2a0 lib/dump_stack.c:120
print_report+0x101/0x810 mm/kasan/report.c:485
kasan_report+0x147/0x180 mm/kasan/report.c:595
kasan_check_range+0x2b0/0x2c0 mm/kasan/generic.c:200
__asan_memcpy+0x29/0x70 mm/kasan/shadow.c:105
generic_hwtstamp_ioctl_lower+0x190/0x2d0 net/core/dev_ioctl.c:447
tsconfig_prepare_data+0x12c/0x600 net/ethtool/tsconfig.c:51
ethnl_default_dump_one+0x2f3/0x7e0 net/ethtool/netlink.c:591
ethnl_default_dumpit+0x30c/0x600 net/ethtool/netlink.c:628
genl_dumpit+0x10b/0x1b0 net/netlink/genetlink.c:1027
netlink_dump+0x6e4/0xe90 net/netlink/af_netlink.c:2327
__netlink_dump_start+0x5cb/0x7e0 net/netlink/af_netlink.c:2442
genl_family_rcv_msg_dumpit+0x1e7/0x2c0 net/netlink/genetlink.c:1076
genl_family_rcv_msg net/netlink/genetlink.c:1192 [inline]
genl_rcv_msg+0x5cd/0x7a0 net/netlink/genetlink.c:1210
netlink_rcv_skb+0x208/0x470 net/netlink/af_netlink.c:2552
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1219
netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
netlink_unicast+0x82f/0x9e0 net/netlink/af_netlink.c:1346
netlink_sendmsg+0x805/0xb30 net/netlink/af_netlink.c:1896
sock_sendmsg_nosec net/socket.c:727 [inline]
__sock_sendmsg+0x21c/0x270 net/socket.c:742
____sys_sendmsg+0x507/0x840 net/socket.c:2630
___sys_sendmsg+0x21f/0x2a0 net/socket.c:2684
__sys_sendmsg net/socket.c:2716 [inline]
__do_sys_sendmsg net/socket.c:2721 [inline]
__se_sys_sendmsg net/socket.c:2719 [inline]
__x64_sys_sendmsg+0x19b/0x260 net/socket.c:2719
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf3/0xfa0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x44dec9
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48
89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d
01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffdcb83e678 EFLAGS: 00000206 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00000000004004b8 RCX: 000000000044dec9
RDX: 0000000000000000 RSI: 0000000020000540 RDI: 0000000000000003
RBP: 00007ffdcb83e690 R08: 000000000049443b R09: 000000000049443b
R10: 0000000000000000 R11: 0000000000000206 R12: 000000000040c030
R13: 0000000000000000 R14: 00000000004c1018 R15: 00000000004004b8
</TASK>
==================================================================

---

Please let me know if any further information is required.

Best Regards,
Jiaming Zhang
repro.c
repro.syz
.config
report
kernel.log

Jiaming Zhang

unread,
Oct 29, 2025, 8:23:06 AM (12 days ago) Oct 29
to Kory Maincent, da...@davemloft.net, edum...@google.com, ku...@kernel.org, net...@vger.kernel.org, pab...@redhat.com, ho...@kernel.org, kun...@google.com, linux-...@vger.kernel.org, s...@fomichev.me, syzk...@googlegroups.com, Vladimir Oltean
Hi Kory,

Thank you for the suggestions!

I will prepare a patch and submit it shortly :)

Best regards,
Jiaming Zhang

Kory Maincent <kory.m...@bootlin.com> 于2025年10月29日周三 18:06写道:
>
> Hello Jiaming,
>
> +Vlad
>
> On Wed, 29 Oct 2025 16:45:37 +0800
> Jiaming Zhang <r7725...@gmail.com> wrote:
>
> > Dear Linux kernel developers and maintainers,
> >
> > We are writing to report a null pointer dereference bug discovered in
> > the net subsystem. This bug is reproducible on the latest version
> > (v6.18-rc3, commit dcb6fa37fd7bc9c3d2b066329b0d27dedf8becaa).
> >
> > The root cause is in tsconfig_prepare_data(), where a local
> > kernel_hwtstamp_config struct (cfg) is initialized using {}, setting
> > all its members to zero. Consequently, cfg.ifr becomes NULL.
> >
> > cfg is then passed as: tsconfig_prepare_data() ->
> > dev_get_hwtstamp_phylib() -> vlan_hwtstamp_get() (via
> > dev->netdev_ops->ndo_hwtstamp_get) -> generic_hwtstamp_get_lower() ->
> > generic_hwtstamp_ioctl_lower().
> >
> > The function generic_hwtstamp_ioctl_lower() assumes cfg->ifr is a
> > valid pointer and attempts to access cfg->ifr->ifr_ifru. This access
> > dereferences the NULL pointer, triggering the bug.
>
> Thanks for spotting this issue!
>
> In the ideal world we would have all Ethernet driver supporting the
> hwtstamp_get/set NDOs but that not currently the case.
> Vladimir Oltean was working on this but it is not done yet.
> $ git grep SIOCGHWTSTAMP drivers/net/ethernet | wc -l
> 16
>
> > As a potential fix, we can declare a local struct ifreq variable in
> > tsconfig_prepare_data(), zero-initializing it, and then assigning its
> > address to cfg.ifr before calling dev_get_hwtstamp_phylib(). This
> > ensures that functions down the call chain receive a valid pointer.
>
> If we do that we will have legacy IOCTL path inside the Netlink path and that's
> not something we want.
> In fact it is possible because the drivers calling
> generic_hwtstamp_get/set_lower functions are already converted to hwtstamp NDOs
> therefore the NDO check in tsconfig_prepare_data is not working on these case.
>
> IMO the solution is to add a check on the ifr value in the
> generic_hwtstamp_set/get_lower functions like that:
>
> int generic_hwtstamp_set_lower(struct net_device *dev,
> struct kernel_hwtstamp_config *kernel_cfg,
> struct netlink_ext_ack *extack)
> {
> ...
>
> /* Netlink path with unconverted lower driver */
> if (!kernel_cfg->ifr)
> return -EOPNOTSUPP;
>
> /* Legacy path: unconverted lower driver */
> return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);
> }
>
> Regards,
> --
> Köry Maincent, Bootlin
> Embedded Linux and kernel engineering
> https://bootlin.com

Vladimir Oltean

unread,
Oct 29, 2025, 12:19:42 PM (12 days ago) Oct 29
to Kory Maincent, Jiaming Zhang, da...@davemloft.net, edum...@google.com, ku...@kernel.org, net...@vger.kernel.org, pab...@redhat.com, ho...@kernel.org, kun...@google.com, linux-...@vger.kernel.org, s...@fomichev.me, syzk...@googlegroups.com, Vadim Fedorenko
On Wed, Oct 29, 2025 at 11:06:51AM +0100, Kory Maincent wrote:
> Hello Jiaming,
>
> +Vlad
>
> On Wed, 29 Oct 2025 16:45:37 +0800
> Jiaming Zhang <r7725...@gmail.com> wrote:
>
> > Dear Linux kernel developers and maintainers,
> >
> > We are writing to report a null pointer dereference bug discovered in
> > the net subsystem. This bug is reproducible on the latest version
> > (v6.18-rc3, commit dcb6fa37fd7bc9c3d2b066329b0d27dedf8becaa).
> >
> > The root cause is in tsconfig_prepare_data(), where a local
> > kernel_hwtstamp_config struct (cfg) is initialized using {}, setting
> > all its members to zero. Consequently, cfg.ifr becomes NULL.
> >
> > cfg is then passed as: tsconfig_prepare_data() ->
> > dev_get_hwtstamp_phylib() -> vlan_hwtstamp_get() (via
> > dev->netdev_ops->ndo_hwtstamp_get) -> generic_hwtstamp_get_lower() ->
> > generic_hwtstamp_ioctl_lower().
> >
> > The function generic_hwtstamp_ioctl_lower() assumes cfg->ifr is a
> > valid pointer and attempts to access cfg->ifr->ifr_ifru. This access
> > dereferences the NULL pointer, triggering the bug.
>
> Thanks for spotting this issue!
>
> In the ideal world we would have all Ethernet driver supporting the
> hwtstamp_get/set NDOs but that not currently the case.
> Vladimir Oltean was working on this but it is not done yet.
> $ git grep SIOCGHWTSTAMP drivers/net/ethernet | wc -l
> 16

Vadim also took the initiative and submitted (is still submitting?) some
more conversions, whereas I lost all steam.

> > As a potential fix, we can declare a local struct ifreq variable in
> > tsconfig_prepare_data(), zero-initializing it, and then assigning its
> > address to cfg.ifr before calling dev_get_hwtstamp_phylib(). This
> > ensures that functions down the call chain receive a valid pointer.
>
> If we do that we will have legacy IOCTL path inside the Netlink path and that's
> not something we want.
> In fact it is possible because the drivers calling
> generic_hwtstamp_get/set_lower functions are already converted to hwtstamp NDOs
> therefore the NDO check in tsconfig_prepare_data is not working on these case.

I remember we had this discussion before.

| This is why I mentioned by ndo_hwtstamp_set() conversion, because
| suddenly it is a prerequisite for any further progress to be done.
| You can't convert SIOCSHWTSTAMP to netlink if there are some driver
| implementations which still use ndo_eth_ioctl(). They need to be
| UAPI-agnostic.

https://lore.kernel.org/netdev/20231122140850.li2mvf6tpo3f2fhh@skbuf/

I'm not sure what was your agreement with the netdev maintainer
accepting the tsconfig netlink work with unconverted device drivers left
in the tree.

> IMO the solution is to add a check on the ifr value in the
> generic_hwtstamp_set/get_lower functions like that:
>
> int generic_hwtstamp_set_lower(struct net_device *dev,
> struct kernel_hwtstamp_config *kernel_cfg,
> struct netlink_ext_ack *extack)
> {
> ...
>
> /* Netlink path with unconverted lower driver */
> if (!kernel_cfg->ifr)
> return -EOPNOTSUPP;
>
> /* Legacy path: unconverted lower driver */
> return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);
> }

This plugs one hole (two including _get). How many more are there? If
this is an oversight, the entire tree needs to be reviewed for
ndo_hwtstamp_get() / ndo_hwtstamp_test() pointer tests which were used
as an indication that this net device is netlink ready. Stacked
virtual interfaces are netlink-ready only when the entire chain down to
the physical interface is netlink-ready.

Kory Maincent

unread,
Oct 30, 2025, 6:13:46 AM (11 days ago) Oct 30
to Vladimir Oltean, Jiaming Zhang, da...@davemloft.net, edum...@google.com, ku...@kernel.org, net...@vger.kernel.org, pab...@redhat.com, ho...@kernel.org, kun...@google.com, linux-...@vger.kernel.org, s...@fomichev.me, syzk...@googlegroups.com, Vadim Fedorenko
Ok no worry I was simply pointing this out, people will convert it when they
want to use the new netlink API.

> > > As a potential fix, we can declare a local struct ifreq variable in
> > > tsconfig_prepare_data(), zero-initializing it, and then assigning its
> > > address to cfg.ifr before calling dev_get_hwtstamp_phylib(). This
> > > ensures that functions down the call chain receive a valid pointer.
> >
> > If we do that we will have legacy IOCTL path inside the Netlink path and
> > that's not something we want.
> > In fact it is possible because the drivers calling
> > generic_hwtstamp_get/set_lower functions are already converted to hwtstamp
> > NDOs therefore the NDO check in tsconfig_prepare_data is not working on
> > these case.
>
> I remember we had this discussion before.
>
> | This is why I mentioned by ndo_hwtstamp_set() conversion, because
> | suddenly it is a prerequisite for any further progress to be done.
> | You can't convert SIOCSHWTSTAMP to netlink if there are some driver
> | implementations which still use ndo_eth_ioctl(). They need to be
> | UAPI-agnostic.
>
> https://lore.kernel.org/netdev/20231122140850.li2mvf6tpo3f2fhh@skbuf/
>
> I'm not sure what was your agreement with the netdev maintainer
> accepting the tsconfig netlink work with unconverted device drivers left
> in the tree.

I did like 21th versions and there was not many people active in the reviews.
No one stand against this work.

> > IMO the solution is to add a check on the ifr value in the
> > generic_hwtstamp_set/get_lower functions like that:
> >
> > int generic_hwtstamp_set_lower(struct net_device *dev,
> > struct kernel_hwtstamp_config *kernel_cfg,
> > struct netlink_ext_ack *extack)
> > {
> > ...
> >
> > /* Netlink path with unconverted lower driver */
> > if (!kernel_cfg->ifr)
> > return -EOPNOTSUPP;
> >
> > /* Legacy path: unconverted lower driver */
> > return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);
> > }
>
> This plugs one hole (two including _get). How many more are there? If
> this is an oversight, the entire tree needs to be reviewed for
> ndo_hwtstamp_get() / ndo_hwtstamp_test() pointer tests which were used
> as an indication that this net device is netlink ready. Stacked
> virtual interfaces are netlink-ready only when the entire chain down to
> the physical interface is netlink-ready.

I don't see this as a hole. The legacy ioctl path still works.
If people want to use the new Netlink path on their board, yes they need to
convert all the parts of the chain to hwtstamp NDOs. If they don't they will
get now a EOPNOTSUPP error instead of a null pointer dereference koops.

Kory Maincent

unread,
Oct 30, 2025, 6:13:46 AM (11 days ago) Oct 30
to Jiaming Zhang, da...@davemloft.net, edum...@google.com, ku...@kernel.org, net...@vger.kernel.org, pab...@redhat.com, ho...@kernel.org, kun...@google.com, linux-...@vger.kernel.org, s...@fomichev.me, syzk...@googlegroups.com, Vladimir Oltean
Hello Jiaming,

+Vlad

On Wed, 29 Oct 2025 16:45:37 +0800
Jiaming Zhang <r7725...@gmail.com> wrote:

> Dear Linux kernel developers and maintainers,
>
> We are writing to report a null pointer dereference bug discovered in
> the net subsystem. This bug is reproducible on the latest version
> (v6.18-rc3, commit dcb6fa37fd7bc9c3d2b066329b0d27dedf8becaa).
>
> The root cause is in tsconfig_prepare_data(), where a local
> kernel_hwtstamp_config struct (cfg) is initialized using {}, setting
> all its members to zero. Consequently, cfg.ifr becomes NULL.
>
> cfg is then passed as: tsconfig_prepare_data() ->
> dev_get_hwtstamp_phylib() -> vlan_hwtstamp_get() (via
> dev->netdev_ops->ndo_hwtstamp_get) -> generic_hwtstamp_get_lower() ->
> generic_hwtstamp_ioctl_lower().
>
> The function generic_hwtstamp_ioctl_lower() assumes cfg->ifr is a
> valid pointer and attempts to access cfg->ifr->ifr_ifru. This access
> dereferences the NULL pointer, triggering the bug.

Thanks for spotting this issue!

In the ideal world we would have all Ethernet driver supporting the
hwtstamp_get/set NDOs but that not currently the case.
Vladimir Oltean was working on this but it is not done yet.
$ git grep SIOCGHWTSTAMP drivers/net/ethernet | wc -l
16

> As a potential fix, we can declare a local struct ifreq variable in
> tsconfig_prepare_data(), zero-initializing it, and then assigning its
> address to cfg.ifr before calling dev_get_hwtstamp_phylib(). This
> ensures that functions down the call chain receive a valid pointer.

If we do that we will have legacy IOCTL path inside the Netlink path and that's
not something we want.
In fact it is possible because the drivers calling
generic_hwtstamp_get/set_lower functions are already converted to hwtstamp NDOs
therefore the NDO check in tsconfig_prepare_data is not working on these case.

IMO the solution is to add a check on the ifr value in the
generic_hwtstamp_set/get_lower functions like that:

int generic_hwtstamp_set_lower(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_cfg,
struct netlink_ext_ack *extack)
{
...

/* Netlink path with unconverted lower driver */
if (!kernel_cfg->ifr)
return -EOPNOTSUPP;

/* Legacy path: unconverted lower driver */
return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg);
}

Vadim Fedorenko

unread,
Oct 30, 2025, 6:13:46 AM (11 days ago) Oct 30
to Kory Maincent, Vladimir Oltean, Jiaming Zhang, da...@davemloft.net, edum...@google.com, ku...@kernel.org, net...@vger.kernel.org, pab...@redhat.com, ho...@kernel.org, kun...@google.com, linux-...@vger.kernel.org, s...@fomichev.me, syzk...@googlegroups.com
I agree with Kory - we don't have many spots in the code calling HW
timestamping configuration. The ones to check is actually phy code and
can drivers. But anyways, we have this interface exposed to UAPI, and we
have ethtool with supports it already. And there is a bug, which can be
fixed with the proposed code.

I'm working right now to finish conversion by the end of this term, both
can and phy will be switched to new API as well as mlx5/ti_netcp
ethernet drivers.
Reply all
Reply to author
Forward
0 new messages