Skip to content

Commit c746845

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 85e6633 commit c746845

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
@@ -364,13 +364,34 @@ static const struct inode_operations proc_dir_inode_operations = {
364364
.setattr = proc_notify_change,
365365
};
366366

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

393+
pde_set_flags(dp);
394+
374395
write_lock(&proc_subdir_lock);
375396
dp->parent = dir;
376397
if (pde_subdir_insert(dir, dp) == false) {
@@ -559,20 +580,6 @@ struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
559580
return p;
560581
}
561582

562-
static void pde_set_flags(struct proc_dir_entry *pde)
563-
{
564-
if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
565-
pde->flags |= PROC_ENTRY_PERMANENT;
566-
if (pde->proc_ops->proc_read_iter)
567-
pde->flags |= PROC_ENTRY_proc_read_iter;
568-
#ifdef CONFIG_COMPAT
569-
if (pde->proc_ops->proc_compat_ioctl)
570-
pde->flags |= PROC_ENTRY_proc_compat_ioctl;
571-
#endif
572-
if (pde->proc_ops->proc_lseek)
573-
pde->flags |= PROC_ENTRY_proc_lseek;
574-
}
575-
576583
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
577584
struct proc_dir_entry *parent,
578585
const struct proc_ops *proc_ops, void *data)
@@ -583,7 +590,6 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
583590
if (!p)
584591
return NULL;
585592
p->proc_ops = proc_ops;
586-
pde_set_flags(p);
587593
return proc_register(parent, p);
588594
}
589595
EXPORT_SYMBOL(proc_create_data);
@@ -634,7 +640,6 @@ struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
634640
p->proc_ops = &proc_seq_ops;
635641
p->seq_ops = ops;
636642
p->state_size = state_size;
637-
pde_set_flags(p);
638643
return proc_register(parent, p);
639644
}
640645
EXPORT_SYMBOL(proc_create_seq_private);
@@ -665,7 +670,6 @@ struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
665670
return NULL;
666671
p->proc_ops = &proc_single_ops;
667672
p->single_show = show;
668-
pde_set_flags(p);
669673
return proc_register(parent, p);
670674
}
671675
EXPORT_SYMBOL(proc_create_single_data);

0 commit comments

Comments
 (0)