For avoiding pde->proc_ops->... dereference(which may cause UAF in rmmod race scene), we call pde_set_flags() to save this kind of information in PDE itself before proc_register() and call pde_has_proc_XXX() to replace pde->proc_ops->... dereference. But there has omission of pde_set_flags() in net related proc file create, which cause the wroing behavior of FMODE_LSEEK clearing in proc_reg_open() for net related proc file after commit ff7ec8dc1b64("proc: use the same treatment to check proc_lseek as ones for proc_read_iter et.al"). Lars reported it in this link[1]. So call pde_set_flags() when create net related proc file to fix this bug.
[1]: https://lore.kernel.org/all/20250815195616.64497967@chagall.paradoxon.rec/
Fixes: ff7ec8dc1b64 ("proc: use the same treatment to check proc_lseek as ones for proc_read_iter et.al) Signed-off-by: wangzijie wangzijie1@honor.com --- fs/proc/generic.c | 2 +- fs/proc/internal.h | 1 + fs/proc/proc_net.c | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 76e800e38..57ec5e385 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -561,7 +561,7 @@ struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode, return p; }
-static void pde_set_flags(struct proc_dir_entry *pde) +void pde_set_flags(struct proc_dir_entry *pde) { if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT) pde->flags |= PROC_ENTRY_PERMANENT; diff --git a/fs/proc/internal.h b/fs/proc/internal.h index e737401d7..c4f7cbc7d 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -284,6 +284,7 @@ extern struct dentry *proc_lookup(struct inode *, struct dentry *, unsigned int) struct dentry *proc_lookup_de(struct inode *, struct dentry *, struct proc_dir_entry *); extern int proc_readdir(struct file *, struct dir_context *); int proc_readdir_de(struct file *, struct dir_context *, struct proc_dir_entry *); +void pde_set_flags(struct proc_dir_entry *);
static inline void pde_get(struct proc_dir_entry *pde) { diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 52f0b75cb..20bc7481b 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -124,6 +124,7 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode, p->proc_ops = &proc_net_seq_ops; p->seq_ops = ops; p->state_size = state_size; + pde_set_flags(p); return proc_register(parent, p); } EXPORT_SYMBOL_GPL(proc_create_net_data); @@ -170,6 +171,7 @@ struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode p->seq_ops = ops; p->state_size = state_size; p->write = write; + pde_set_flags(p); return proc_register(parent, p); } EXPORT_SYMBOL_GPL(proc_create_net_data_write); @@ -217,6 +219,7 @@ struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode, pde_force_lookup(p); p->proc_ops = &proc_net_single_ops; p->single_show = show; + pde_set_flags(p); return proc_register(parent, p); } EXPORT_SYMBOL_GPL(proc_create_net_single); @@ -261,6 +264,7 @@ struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mo p->proc_ops = &proc_net_single_ops; p->single_show = show; p->write = write; + pde_set_flags(p); return proc_register(parent, p); } EXPORT_SYMBOL_GPL(proc_create_net_single_write);