Skip to content

Commit 2ce3d28

Browse files
wangzijieakpm00
authored andcommitted
proc: fix missing pde_set_flags() for net proc files
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>
1 parent c357688 commit 2ce3d28

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
@@ -367,13 +367,34 @@ static const struct inode_operations proc_dir_inode_operations = {
367367
.setattr = proc_notify_change,
368368
};
369369

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

396+
pde_set_flags(dp);
397+
377398
write_lock(&proc_subdir_lock);
378399
dp->parent = dir;
379400
if (pde_subdir_insert(dir, dp) == false) {
@@ -561,20 +582,6 @@ struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
561582
return p;
562583
}
563584

564-
static void pde_set_flags(struct proc_dir_entry *pde)
565-
{
566-
if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
567-
pde->flags |= PROC_ENTRY_PERMANENT;
568-
if (pde->proc_ops->proc_read_iter)
569-
pde->flags |= PROC_ENTRY_proc_read_iter;
570-
#ifdef CONFIG_COMPAT
571-
if (pde->proc_ops->proc_compat_ioctl)
572-
pde->flags |= PROC_ENTRY_proc_compat_ioctl;
573-
#endif
574-
if (pde->proc_ops->proc_lseek)
575-
pde->flags |= PROC_ENTRY_proc_lseek;
576-
}
577-
578585
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
579586
struct proc_dir_entry *parent,
580587
const struct proc_ops *proc_ops, void *data)
@@ -585,7 +592,6 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
585592
if (!p)
586593
return NULL;
587594
p->proc_ops = proc_ops;
588-
pde_set_flags(p);
589595
return proc_register(parent, p);
590596
}
591597
EXPORT_SYMBOL(proc_create_data);
@@ -636,7 +642,6 @@ struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
636642
p->proc_ops = &proc_seq_ops;
637643
p->seq_ops = ops;
638644
p->state_size = state_size;
639-
pde_set_flags(p);
640645
return proc_register(parent, p);
641646
}
642647
EXPORT_SYMBOL(proc_create_seq_private);
@@ -667,7 +672,6 @@ struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
667672
return NULL;
668673
p->proc_ops = &proc_single_ops;
669674
p->single_show = show;
670-
pde_set_flags(p);
671675
return proc_register(parent, p);
672676
}
673677
EXPORT_SYMBOL(proc_create_single_data);

0 commit comments

Comments
 (0)