[PATCH] ipv4: guard inetdev_event() against a NULL from inetdev_init()
From: Yunseong Kim
Date: Mon Sep 21 2026 - 13:06:06 EST
On NETDEV_REGISTER, inetdev_event() only checks the inetdev_init()
return value with IS_ERR():
in_dev = inetdev_init(dev);
if (IS_ERR(in_dev))
return notifier_from_errno(PTR_ERR(in_dev));
if (dev->flags & IFF_LOOPBACK) {
IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
...
Because IS_ERR(NULL) is false, a NULL return flows straight into
IN_DEV_CONF_SET() -> ipv4_devconf_set(), whose
set_bit(index, in_dev->cnf.state) dereferences NULL.
inetdev_init() is only kept from returning NULL by its trailing
out:
return in_dev ?: ERR_PTR(err);
which relies on every failure path that leaves in_dev NULL having also
set a non-zero err. That invariant is fragile and lives in the producer,
while the consumer's IS_ERR()-only check silently depends on it: any
future inetdev_init() failure path that returns NULL (directly, or by
leaving err == 0) becomes a NULL dereference at this call site rather
than a clean error return.
Before commit 20e61da7ffcf ("ipv4: fail early when creating netdev
named all or default") this call site used "if (!in_dev)", which caught
a NULL return; that commit converted inetdev_init() to the ERR_PTR()
convention and switched the check to IS_ERR(), dropping the NULL
handling here.
Decouple the caller from that invariant by using IS_ERR_OR_NULL() and
translating a NULL return to -ENOMEM, so a NULL can no longer be
dereferenced regardless of how inetdev_init() signals failure.
Cc: stable@xxxxxxxxxxxxxxx
Cc: syzkaller-bugs@xxxxxxxxxxxxxxxx
Signed-off-by: Yunseong Kim <yunseong.kim@xxxxxxxx>
---
Syzkaller reproducer:
# {Threaded:true Repeat:true RepeatTimes:0 Procs:8 Slowdown:1 Sandbox:none SandboxArg:0 Leak:false NetInjection:false NetDevices:true NetReset:true Cgroups:true BinfmtMisc:true CloseFDs:true KCSAN:false DevlinkP
CI:false NicVF:false USB:false VhciInjection:false Wifi:false IEEE802154:false Sysctl:true Swap:true UseTmpDir:true HandleSegv:true Trace:false CallComments:true LegacyOptions:{Collide:false Fault:false FaultCal
l:0 FaultNth:0}}
r0 = creat(&(0x7f00000000c0)='./file0\x00', 0x26)
ioctl$RNDADDTOENTCNT(r0, 0x40045201, 0x0)
r1 = socket$inet6_udplite(0xa, 0x2, 0x88)
setsockopt$sock_int(r1, 0x1, 0x1d, &(0x7f0000000380), 0x4) (async, rerun: 64)
unshare(0x42020000) (async, rerun: 64)
truncate(&(0x7f0000000040)='./file0\x00', 0x9) (rerun: 64)
ioctl$sock_SIOCBRADDBR(0xffffffffffffffff, 0x89a0, &(0x7f0000000000)='syzkaller0\x00')
socket$inet_udplite(0x2, 0x2, 0x88)
rename(&(0x7f0000000640)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x00', &(0x7f0000000780)='./file1\x00') (async)
ioctl$BTRFS_IOC_BALANCE_PROGRESS(0xffffffffffffffff, 0x84009422, &(0x7f00000004c0)={0x0, 0x0, {0x0, @struct, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @struct}, {}, {0x0, @struct}})
net/ipv4/devinet.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index a90be57c63be..353d7c584463 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1588,8 +1588,9 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
if (!in_dev) {
if (event == NETDEV_REGISTER) {
in_dev = inetdev_init(dev);
- if (IS_ERR(in_dev))
- return notifier_from_errno(PTR_ERR(in_dev));
+ if (IS_ERR_OR_NULL(in_dev))
+ return notifier_from_errno(in_dev ?
+ PTR_ERR(in_dev) : -ENOMEM);
if (dev->flags & IFF_LOOPBACK) {
IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
--
2.55.0