mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 08:15:07 -04:00
l2tp: fix tunnel and session refcount leak on seq_file release
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:0e0c3fee3a("l2tp: hold reference on tunnels printed in pppol2tp proc file") Fixes:f726214d9b("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> Link: https://patch.msgid.link/20260811144651.2733424-1-edumazet@google.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
committed by
Paolo Abeni
parent
120977e2c0
commit
9006c116dd
@@ -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);
|
||||
|
||||
@@ -1597,7 +1597,53 @@ static const struct seq_operations pppol2tp_seq_ops = {
|
||||
.stop = pppol2tp_seq_stop,
|
||||
.show = pppol2tp_seq_show,
|
||||
};
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* Network namespace
|
||||
@@ -1608,8 +1654,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;
|
||||
@@ -1624,9 +1670,13 @@ static __net_exit void pppol2tp_exit_net(struct net *net)
|
||||
remove_proc_entry("pppol2tp", net->proc_net);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
static struct pernet_operations pppol2tp_net_ops = {
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.init = pppol2tp_init_net,
|
||||
.exit = pppol2tp_exit_net,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user