* [PATCH v2 net] l2tp: fix tunnel and session refcount leak on seq_file release
@ 2026-08-10 14:11 Eric Dumazet
2026-08-11 10:20 ` Paolo Abeni
0 siblings, 1 reply; 3+ messages in thread
From: Eric Dumazet @ 2026-08-10 14:11 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, netdev, eric.dumazet, Eric Dumazet,
syzbot+d6fa74e3f19d6ee01e3a, James Chapman, Guillaume Nault
In pppol2tp_proc_open() and l2tp_dfs_seq_open(), iteration state
(pd->tunnel and pd->session) is kept in seq_file private data to allow
iteration across multiple read() system calls.
However, if userspace closes /proc/net/pppol2tp or /sys/kernel/debug/l2tp/tunnels
before reading to end-of-file (EOF), any tunnel or session reference stored in
pd->tunnel / pd->session is left un-dropped when seq_file private data is freed.
Fix this by dropping any remaining pd->tunnel and pd->session references in
pppol2tp_proc_release() and l2tp_dfs_seq_release() when closing the file.
Fixes: 0e0c3fee3a59 ("l2tp: hold reference on tunnels printed in pppol2tp proc file")
Fixes: f726214d9b23 ("l2tp: hold reference on tunnels printed in l2tp/tunnels debugfs file")
Reported-by: syzbot+d6fa74e3f19d6ee01e3a@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/6a760f32.01d0871a.3a0d52.004f.GAE@google.com/T/#u
Assisted-by: Jetski:Gemini-3.1-Pro
Cc: James Chapman <jchapman@katalix.com>
Cc: Guillaume Nault <gnault@redhat.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/l2tp/l2tp_debugfs.c | 4 ++++
net/l2tp/l2tp_ppp.c | 51 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c
index b26986fda9d6cf096519d56032fe3deb5034c7c8..c12d5e1ee550ccc2608b6128cb653ca373280373 100644
--- a/net/l2tp/l2tp_debugfs.c
+++ b/net/l2tp/l2tp_debugfs.c
@@ -306,6 +306,10 @@ static int l2tp_dfs_seq_release(struct inode *inode, struct file *file)
seq = file->private_data;
pd = seq->private;
+ if (pd->session)
+ l2tp_session_put(pd->session);
+ if (pd->tunnel)
+ l2tp_tunnel_put(pd->tunnel);
if (pd->net)
put_net_track(pd->net, &pd->ns_tracker);
kfree(pd);
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index f8881699e1cad88e6f312e6ca47ae0af94c6137c..1bcf447f2f8b08235fdec747d33fd188e869a382 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -1597,6 +1597,53 @@ static const struct seq_operations pppol2tp_seq_ops = {
.stop = pppol2tp_seq_stop,
.show = pppol2tp_seq_show,
};
+
+static int pppol2tp_proc_open(struct inode *inode, struct file *file)
+{
+ struct net *net = pde_data(inode);
+ struct pppol2tp_seq_data *pd;
+
+ net = maybe_get_net(net);
+ if (!net)
+ return -ENXIO;
+
+ pd = __seq_open_private(file, &pppol2tp_seq_ops, sizeof(*pd));
+ if (!pd) {
+ put_net(net);
+ return -ENOMEM;
+ }
+
+#ifdef CONFIG_NET_NS
+ pd->p.net = net;
+ netns_tracker_alloc(net, &pd->p.ns_tracker, GFP_KERNEL);
+#endif
+ return 0;
+}
+
+static int pppol2tp_proc_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq = file->private_data;
+ struct pppol2tp_seq_data *pd = seq->private;
+
+ if (pd->session)
+ l2tp_session_put(pd->session);
+ if (pd->tunnel)
+ l2tp_tunnel_put(pd->tunnel);
+
+#ifdef CONFIG_NET_NS
+ put_net_track(pd->p.net, &pd->p.ns_tracker);
+#else
+ put_net(&init_net);
+#endif
+ return seq_release_private(inode, file);
+}
+
+static const struct proc_ops pppol2tp_proc_ops = {
+ .proc_open = pppol2tp_proc_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = pppol2tp_proc_release,
+};
#endif /* CONFIG_PROC_FS */
/*****************************************************************************
@@ -1608,8 +1655,8 @@ static __net_init int pppol2tp_init_net(struct net *net)
struct proc_dir_entry *pde;
int err = 0;
- pde = proc_create_net("pppol2tp", 0444, net->proc_net,
- &pppol2tp_seq_ops, sizeof(struct pppol2tp_seq_data));
+ pde = proc_create_data("pppol2tp", 0444, net->proc_net,
+ &pppol2tp_proc_ops, net);
if (!pde) {
err = -ENOMEM;
goto out;
--
2.55.0.654.g21b8a5bc05-goog
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v2 net] l2tp: fix tunnel and session refcount leak on seq_file release
2026-08-10 14:11 [PATCH v2 net] l2tp: fix tunnel and session refcount leak on seq_file release Eric Dumazet
@ 2026-08-11 10:20 ` Paolo Abeni
2026-08-11 10:59 ` Eric Dumazet
0 siblings, 1 reply; 3+ messages in thread
From: Paolo Abeni @ 2026-08-11 10:20 UTC (permalink / raw)
To: Eric Dumazet, David S . Miller, Jakub Kicinski
Cc: Simon Horman, netdev, eric.dumazet, syzbot+d6fa74e3f19d6ee01e3a,
James Chapman, Guillaume Nault
On 8/10/26 4:11 PM, Eric Dumazet wrote:
> @@ -1608,8 +1655,8 @@ static __net_init int pppol2tp_init_net(struct net *net)
> struct proc_dir_entry *pde;
> int err = 0;
>
> - pde = proc_create_net("pppol2tp", 0444, net->proc_net,
> - &pppol2tp_seq_ops, sizeof(struct pppol2tp_seq_data));
> + pde = proc_create_data("pppol2tp", 0444, net->proc_net,
> + &pppol2tp_proc_ops, net);
It looks like the above does not build with CONFIG_PROC_FS=n:
net/l2tp/l2tp_ppp.c: In function ‘pppol2tp_init_net’:
net/l2tp/l2tp_ppp.c:1659:33: error: ‘pppol2tp_proc_ops’ undeclared (first use in this function); did you mean ‘pppol2tp_chan_ops’?
1659 | &pppol2tp_proc_ops, net);
| ^~~~~~~~~~~~~~~~~
| pppol2tp_chan_ops
/P
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2 net] l2tp: fix tunnel and session refcount leak on seq_file release
2026-08-11 10:20 ` Paolo Abeni
@ 2026-08-11 10:59 ` Eric Dumazet
0 siblings, 0 replies; 3+ messages in thread
From: Eric Dumazet @ 2026-08-11 10:59 UTC (permalink / raw)
To: Paolo Abeni
Cc: David S . Miller, Jakub Kicinski, Simon Horman, netdev,
eric.dumazet, syzbot+d6fa74e3f19d6ee01e3a, James Chapman,
Guillaume Nault
On Tue, Aug 11, 2026 at 12:20 PM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On 8/10/26 4:11 PM, Eric Dumazet wrote:
> > @@ -1608,8 +1655,8 @@ static __net_init int pppol2tp_init_net(struct net *net)
> > struct proc_dir_entry *pde;
> > int err = 0;
> >
> > - pde = proc_create_net("pppol2tp", 0444, net->proc_net,
> > - &pppol2tp_seq_ops, sizeof(struct pppol2tp_seq_data));
> > + pde = proc_create_data("pppol2tp", 0444, net->proc_net,
> > + &pppol2tp_proc_ops, net);
>
> It looks like the above does not build with CONFIG_PROC_FS=n:
>
> net/l2tp/l2tp_ppp.c: In function ‘pppol2tp_init_net’:
> net/l2tp/l2tp_ppp.c:1659:33: error: ‘pppol2tp_proc_ops’ undeclared (first use in this function); did you mean ‘pppol2tp_chan_ops’?
> 1659 | &pppol2tp_proc_ops, net);
> | ^~~~~~~~~~~~~~~~~
> | pppol2tp_chan_ops
>
For some reason, proc_create_net() is a NOP for CONFIG_PROC_FS=n, but
proc_create_data() is not.
Oh well.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-11 10:59 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-10 14:11 [PATCH v2 net] l2tp: fix tunnel and session refcount leak on seq_file release Eric Dumazet
2026-08-11 10:20 ` Paolo Abeni
2026-08-11 10:59 ` Eric Dumazet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox