Skip to content

Commit 3eebe85

Browse files
wangzijiegregkh
authored andcommitted
proc: fix missing pde_set_flags() for net proc files
commit 2ce3d28 upstream. To avoid potential UAF issues during module removal races, we use pde_set_flags() to save proc_ops flags in PDE itself before proc_register(), and then use pde_has_proc_*() helpers instead of directly dereferencing pde->proc_ops->*. However, the pde_set_flags() call was missing when creating net related proc files. This omission caused incorrect behavior which FMODE_LSEEK was being cleared inappropriately in proc_reg_open() for net proc files. Lars reported it in this link[1]. Fix this by ensuring pde_set_flags() is called when register proc entry, and add NULL check for proc_ops in pde_set_flags(). [wangzijie1@honor.com: stash pde->proc_ops in a local const variable, per Christian] Link: https://lkml.kernel.org/r/20250821105806.1453833-1-wangzijie1@honor.com Link: https://lkml.kernel.org/r/20250818123102.959595-1-wangzijie1@honor.com Link: https://lore.kernel.org/all/20250815195616.64497967@chagall.paradoxon.rec/ [1] Fixes: ff7ec8d ("proc: use the same treatment to check proc_lseek as ones for proc_read_iter et.al") Signed-off-by: wangzijie <wangzijie1@honor.com> Reported-by: Lars Wendler <polynomial-c@gmx.de> Tested-by: Stefano Brivio <sbrivio@redhat.com> Tested-by: Petr Vaněk <pv@excello.cz> Tested by: Lars Wendler <polynomial-c@gmx.de> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: "Edgecombe, Rick P" <rick.p.edgecombe@intel.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Cc: Kirill A. Shutemov <k.shutemov@gmail.com> Cc: wangzijie <wangzijie1@honor.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent f4a917e commit 3eebe85

File tree

1 file changed

+21
-17
lines changed

1 file changed

+21
-17
lines changed

fs/proc/generic.c

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -362,13 +362,34 @@ static const struct inode_operations proc_dir_inode_operations = {
362362
.setattr = proc_notify_change,
363363
};
364364

365+
static void pde_set_flags(struct proc_dir_entry *pde)
366+
{
367+
const struct proc_ops *proc_ops = pde->proc_ops;
368+
369+
if (!proc_ops)
370+
return;
371+
372+
if (proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
373+
pde->flags |= PROC_ENTRY_PERMANENT;
374+
if (proc_ops->proc_read_iter)
375+
pde->flags |= PROC_ENTRY_proc_read_iter;
376+
#ifdef CONFIG_COMPAT
377+
if (proc_ops->proc_compat_ioctl)
378+
pde->flags |= PROC_ENTRY_proc_compat_ioctl;
379+
#endif
380+
if (proc_ops->proc_lseek)
381+
pde->flags |= PROC_ENTRY_proc_lseek;
382+
}
383+
365384
/* returns the registered entry, or frees dp and returns NULL on failure */
366385
struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
367386
struct proc_dir_entry *dp)
368387
{
369388
if (proc_alloc_inum(&dp->low_ino))
370389
goto out_free_entry;
371390

391+
pde_set_flags(dp);
392+
372393
write_lock(&proc_subdir_lock);
373394
dp->parent = dir;
374395
if (pde_subdir_insert(dir, dp) == false) {
@@ -557,20 +578,6 @@ struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
557578
return p;
558579
}
559580

560-
static void pde_set_flags(struct proc_dir_entry *pde)
561-
{
562-
if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
563-
pde->flags |= PROC_ENTRY_PERMANENT;
564-
if (pde->proc_ops->proc_read_iter)
565-
pde->flags |= PROC_ENTRY_proc_read_iter;
566-
#ifdef CONFIG_COMPAT
567-
if (pde->proc_ops->proc_compat_ioctl)
568-
pde->flags |= PROC_ENTRY_proc_compat_ioctl;
569-
#endif
570-
if (pde->proc_ops->proc_lseek)
571-
pde->flags |= PROC_ENTRY_proc_lseek;
572-
}
573-
574581
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
575582
struct proc_dir_entry *parent,
576583
const struct proc_ops *proc_ops, void *data)
@@ -581,7 +588,6 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
581588
if (!p)
582589
return NULL;
583590
p->proc_ops = proc_ops;
584-
pde_set_flags(p);
585591
return proc_register(parent, p);
586592
}
587593
EXPORT_SYMBOL(proc_create_data);
@@ -632,7 +638,6 @@ struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
632638
p->proc_ops = &proc_seq_ops;
633639
p->seq_ops = ops;
634640
p->state_size = state_size;
635-
pde_set_flags(p);
636641
return proc_register(parent, p);
637642
}
638643
EXPORT_SYMBOL(proc_create_seq_private);
@@ -663,7 +668,6 @@ struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
663668
return NULL;
664669
p->proc_ops = &proc_single_ops;
665670
p->single_show = show;
666-
pde_set_flags(p);
667671
return proc_register(parent, p);
668672
}
669673
EXPORT_SYMBOL(proc_create_single_data);

0 commit comments

Comments
 (0)