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(). [[email protected]: stash pde->proc_ops in a local const variable, per Christian] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Link: https://lore.kernel.org/all/[email protected]/ [1] Fixes: ff7ec8d ("proc: use the same treatment to check proc_lseek as ones for proc_read_iter et.al") Signed-off-by: wangzijie <[email protected]> Reported-by: Lars Wendler <[email protected]> Tested-by: Stefano Brivio <[email protected]> Tested-by: Petr Vaněk <[email protected]> Tested by: Lars Wendler <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Alexey Dobriyan <[email protected]> Cc: Al Viro <[email protected]> Cc: "Edgecombe, Rick P" <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Cc: Jiri Slaby <[email protected]> Cc: Kirill A. Shutemov <[email protected]> Cc: wangzijie <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
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)