Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 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>
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>

authored by

wangzijie and committed by
Andrew Morton
2ce3d282 c3576889

+21 -17
+21 -17
fs/proc/generic.c
··· 367 367 .setattr = proc_notify_change, 368 368 }; 369 369 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 + 370 389 /* returns the registered entry, or frees dp and returns NULL on failure */ 371 390 struct proc_dir_entry *proc_register(struct proc_dir_entry *dir, 372 391 struct proc_dir_entry *dp) 373 392 { 374 393 if (proc_alloc_inum(&dp->low_ino)) 375 394 goto out_free_entry; 395 + 396 + pde_set_flags(dp); 376 397 377 398 write_lock(&proc_subdir_lock); 378 399 dp->parent = dir; ··· 582 561 return p; 583 562 } 584 563 585 - static void pde_set_flags(struct proc_dir_entry *pde) 586 - { 587 - if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT) 588 - pde->flags |= PROC_ENTRY_PERMANENT; 589 - if (pde->proc_ops->proc_read_iter) 590 - pde->flags |= PROC_ENTRY_proc_read_iter; 591 - #ifdef CONFIG_COMPAT 592 - if (pde->proc_ops->proc_compat_ioctl) 593 - pde->flags |= PROC_ENTRY_proc_compat_ioctl; 594 - #endif 595 - if (pde->proc_ops->proc_lseek) 596 - pde->flags |= PROC_ENTRY_proc_lseek; 597 - } 598 - 599 564 struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, 600 565 struct proc_dir_entry *parent, 601 566 const struct proc_ops *proc_ops, void *data) ··· 592 585 if (!p) 593 586 return NULL; 594 587 p->proc_ops = proc_ops; 595 - pde_set_flags(p); 596 588 return proc_register(parent, p); 597 589 } 598 590 EXPORT_SYMBOL(proc_create_data); ··· 642 636 p->proc_ops = &proc_seq_ops; 643 637 p->seq_ops = ops; 644 638 p->state_size = state_size; 645 - pde_set_flags(p); 646 639 return proc_register(parent, p); 647 640 } 648 641 EXPORT_SYMBOL(proc_create_seq_private); ··· 672 667 return NULL; 673 668 p->proc_ops = &proc_single_ops; 674 669 p->single_show = show; 675 - pde_set_flags(p); 676 670 return proc_register(parent, p); 677 671 } 678 672 EXPORT_SYMBOL(proc_create_single_data);