Linux kernel -stable discussions
 help / color / mirror / Atom feed
* [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7)
       [not found] <20261104-work-procfs-v2-0-b45cd5789ac5@brauner>
@ 2026-11-04 19:05 ` Christian Brauner
  2026-11-04 19:06   ` [PATCH RESEND v2 01/51] alloc_tag: don't remove /proc/allocinfo under the codetag module lock Christian Brauner
                     ` (34 more replies)
  0 siblings, 35 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:05 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable,
	Rafael J. Wysocki (Intel)

Part 1/7 of "proc: move procfs to what kernfs does", the base of the
other six parts:

  https://patch.msgid.link/20261104-work-procfs-v2-0-b45cd5789ac5@brauner

Moving procfs to what kernfs does touches just about every
->proc_read(), ->proc_write() and ->proc_ioctl() in the tree. Reviewing
that turned up bugs that have nothing to do with the conversion and are
a lot older. They go first so they can go to stable on their own. Four
selftests come with them.

In procfs itself:

- readdir resumed a listing at a counted position, so an entry that came
  or went in between repeated or skipped one, and it ended a listing
  early when the entry it was emitting went away. It resumes after the
  last name now, like kernfs. /proc/sys had the same bug.
- /proc/sys readdir ended a listing early at an entry that was being
  unregistered, so getdents64() reported EOF with entries left.
- /proc/sys copied to and from userspace with the table held, so a read,
  write or getdents64() that faults for as long as userspace wants kept
  unregister_sysctl_table() waiting, which addrconf calls under RTNL.
- proc_sys_make_inode() read the table after it had dropped sysctl_lock.
- a symlink's entry stayed pinned while readlink() copied it out, so a
  faulting readlink() held up remove_proc_entry(). ALSA removes its card
  links with snd_card_mutex held.
- name resolution walked through regular files, and pktgen lets
  userspace pick names, so an unprivileged user could hang an entry
  below another file and get it freed. pktgen refuses a '/' now.
- proc_register() WARNed on a duplicate name, which pktgen's names can
  provoke.
- proc_remove() removed by name, so removing an entry that was already
  gone could take out a different one.
- the parent of entries a dying network namespace left in /proc/net was
  freed silently. procfs warns now.
- on NOMMU every /proc file could be mapped privately, which reads it
  with nommu_region_sem held, and /proc/maps takes that lock. Any user
  could hang the machine.
- sunrpc queued the pollers of its channel files on a wait queue in the
  per-namespace cache_detail, which is freed with the namespace. Back to
  one static queue here, the conversion makes procfs own the queues.
- vmcore_cleanup() could run twice at once.

In the handlers:

- PCI/proc and isapnp truncated the position to an int, so a pwrite()
  or pread() at a huge offset went negative and accessed config space
  far outside the file.
- dasd looked in front of its buffer on an empty write.
- toshiba_acpi's brightness and fan write checks could never fail.
- simdisk read the file name without the lock that protects it, so a
  reader could copy out memory a concurrent detach had freed.
- rtas_flash read past its validate message, and rtasd handed out what
  vmalloc() left in its log buffer.
- ALSA's info layer let a write past the end of a data entry through
  unclamped.
- BusLogic, ips and libipw returned 0 from writes, so stdio writers
  retry forever.
- the scsi host files and um's /proc/exitcode parsed writes without
  NUL-terminating them.
- ntfs3 tripped the page allocator's warning on a huge label write.
- esas2r took the lengths of its HBA, SMP and firmware ioctls from
  userspace unchecked, through a file everyone can open.
- powerpc's copy_oldmem_page() didn't check ioremap_cache() and only
  recovered from a machine check for read(), the pseries ofdt parser
  read past its input, and lparcfg reported a failed hcall as a huge
  write.
- alloc_tag removed /proc/allocinfo with the codetag module lock held
  when a module's tags couldn't be reserved, and a reader of the file
  takes that lock with the entry pinned, so the module load and the
  reader waited for each other for good.
- esas2r copied the kernel addresses of the adapter, its pci_dev and its
  Scsi_Host out through the 0444 ATTONode.
- cs46xx stored the chip in the private data of its region files and
  read it back as the region.
- isapnp went on talking to whatever device the card had selected, and
  unlocked a mutex it never took, when isapnp_cfg_begin() refused a
  logical device number.
- simdisk freed the gendisk before it removed the entry, so a write in
  between set the capacity of a freed disk.
- the pseries ofdt commands leaked a node reference per property
  command, and lockd handed out the NUL behind "Y\n".
- lparcfg and psi overwrote the last byte of a write to terminate it,
  so a write without a trailing newline lost a character.
- the sparc LED and rtas progress files consumed only the start of a
  write, so a looping writer sent the rest as a second command.
- profiling reported success when /proc/profile couldn't be created,
  and zorro sized its files like a zorro_dev instead of the ConfigDev
  they serve.

The xt_recent deadlock v1 fixed here goes in through netfilter instead.

The procfs, sysctl, readlink, pktgen, sunrpc, PCI and duplicate name
bugs reproduce on a KASAN and lockdep kernel in a VM and are gone with
the series. The rest is build tested.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Changes in v2:
- thirteen more fixes that the review of v1 turned up: alloc_tag,
  esas2r, cs46xx, isapnp, simdisk, pseries ofdt, lockd, lparcfg, psi,
  the sparc LED, rtas progress, profiling and zorro
- the NOMMU, crash_dump and lparcfg patches say more about the bug
- new patch: sysctl readdir ended a listing early when an entry was
  being unregistered, pre-existing
- the xt_recent patch is dropped, Cen Zhang's fix for the same lock is
  on its way through netfilter
- the readdir churn selftests skip instead of failing without
  /dev/net/tun, the sysctl removal selftest seeks past the dots first so
  its fault lands in an entry, the selftests' Makefile, .gitignore and
  config lines sit in the series that adds each test
- struct sysctl_dirent loses its unused mode
- Reviewed-by and Acked-by from Rafael, Takashi and Chuck
- rebased onto v7.4-rc1
- Link to v1: https://patch.msgid.link/20261102-work-procfs-bugfixes-v1-0-59422ed3e465@kernel.org

---
Christian Brauner (51):
      alloc_tag: don't remove /proc/allocinfo under the codetag module lock
      scsi: esas2r: don't copy kernel pointers out in EXPRESS_IOCTL_GET_MOD_INFO
      ALSA: cs46xx: hand the region to the region proc files
      PNP: isapnp: don't go on when isapnp_cfg_begin() fails
      xtensa: simdisk: remove the proc entry before the disk
      powerpc/pseries: put the node that the ofdt property commands look up
      lockd: don't hand out the terminator of nlm_end_grace
      powerpc/pseries/lparcfg: don't drop the last byte of a write
      profiling: report it when /proc/profile can't be created
      zorro: size the proc files like what they serve
      sparc: led: consume the whole write
      powerpc/rtas: consume the whole write to /proc/powerpc/rtas/progress
      sched/psi: don't drop the last byte of a trigger write
      proc: sysctl: don't stop readdir at an entry that is being unregistered
      PCI/proc: don't truncate the position of config space accesses
      PNP: isapnp: don't read past the end of the buffer on a large pread()
      s390/dasd: don't look in front of the buffer on an empty write
      platform/x86: toshiba_acpi: reject malformed brightness and fan writes
      xtensa: simdisk: don't read the file name without the lock
      powerpc/rtas_flash: don't read past the validate message
      powerpc/rtasd: zero the error log buffer
      ALSA: info: don't write past the end of a data entry
      scsi: BusLogic: don't return 0 from writes to the proc file
      scsi: ips: don't return 0 from writes to the proc file
      wifi: libipw: don't return 0 from writes to debug_level
      scsi: core: NUL-terminate what is written to the host proc files
      um: NUL-terminate what is written to /proc/exitcode
      ntfs3: don't warn when a huge label is written
      proc: don't walk through a non-directory when resolving a name
      net: pktgen: reject device names with a '/'
      proc: don't WARN about a name that is already registered
      proc: resume readdir after the last name it returned
      selftests/proc: check a /proc listing while entries come and go
      proc: let proc_remove() remove the entry it was given
      proc: warn about entries left in /proc/net of a dying namespace
      proc: don't let NOMMU map procfs files
      proc/vmcore: don't let vmcore_cleanup() run twice at once
      powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page()
      powerpc/crash_dump: recover from a machine check in copy_oldmem_page()
      sunrpc: make queue_wait a global wait queue again
      scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls
      powerpc/pseries: bound the lengths in the ofdt property parser
      powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long
      proc: sysctl: resume readdir after the last name it returned
      selftests/proc: check a sysctl listing while entries come and go
      proc: sysctl: copy to and from userspace without holding the table
      proc: sysctl: look at the table under sysctl_lock when creating an inode
      proc: sysctl: don't hold the tables while readdir copies to userspace
      selftests/proc: check that faulting sysctl accesses don't hold up removal
      proc: don't pin a symlink's entry while readlink() copies it out
      selftests/proc: check that a faulting readlink() doesn't hold up removal

 arch/powerpc/kernel/crash_dump.c                   |   6 +-
 arch/powerpc/kernel/rtas-proc.c                    |   3 +-
 arch/powerpc/kernel/rtas_flash.c                   |   2 +-
 arch/powerpc/kernel/rtasd.c                        |   2 +-
 arch/powerpc/platforms/pseries/lparcfg.c           |   7 +-
 arch/powerpc/platforms/pseries/reconfig.c          |  24 +-
 arch/sparc/kernel/led.c                            |   3 +-
 arch/um/kernel/exitcode.c                          |   3 +-
 arch/xtensa/platforms/iss/simdisk.c                |  28 +-
 drivers/net/wireless/intel/ipw2x00/libipw_module.c |   2 +-
 drivers/pci/proc.c                                 |  15 +-
 drivers/platform/x86/toshiba_acpi.c                |   8 +-
 drivers/pnp/isapnp/core.c                          |  16 +-
 drivers/pnp/isapnp/proc.c                          |  11 +-
 drivers/s390/block/dasd.c                          |   2 +-
 drivers/scsi/BusLogic.c                            |   2 +-
 drivers/scsi/esas2r/esas2r.h                       |   2 +-
 drivers/scsi/esas2r/esas2r_ioctl.c                 |  48 ++-
 drivers/scsi/esas2r/esas2r_main.c                  |   3 +-
 drivers/scsi/ips.c                                 |   2 +-
 drivers/scsi/scsi_proc.c                           |   1 +
 drivers/zorro/proc.c                               |   2 +-
 fs/lockd/procfs.c                                  |   2 +-
 fs/ntfs3/super.c                                   |  11 +-
 fs/proc/generic.c                                  | 174 ++++++---
 fs/proc/inode.c                                    |  38 +-
 fs/proc/internal.h                                 |   1 +
 fs/proc/proc_net.c                                 |   7 +
 fs/proc/proc_sysctl.c                              | 388 ++++++++++++++-------
 fs/proc/root.c                                     |   1 +
 fs/proc/vmcore.c                                   |   5 +
 include/linux/sunrpc/cache.h                       |   1 -
 kernel/profile.c                                   |   8 +-
 kernel/sched/psi.c                                 |   4 +-
 mm/alloc_tag.c                                     |  27 +-
 net/core/pktgen.c                                  |   4 +
 net/sunrpc/cache.c                                 |  12 +-
 sound/core/info.c                                  |   2 +
 sound/isa/es18xx.c                                 |   6 +-
 sound/pci/cs46xx/cs46xx_lib.c                      |   2 +-
 tools/testing/selftests/proc/.gitignore            |   4 +
 tools/testing/selftests/proc/Makefile              |   4 +
 tools/testing/selftests/proc/config                |   9 +
 tools/testing/selftests/proc/proc-readdir-churn.c  | 240 +++++++++++++
 .../selftests/proc/proc-remove-slow-readlink.c     | 191 ++++++++++
 .../selftests/proc/proc-remove-slow-sysctl.c       | 215 ++++++++++++
 .../selftests/proc/proc-sysctl-readdir-churn.c     | 152 ++++++++
 47 files changed, 1420 insertions(+), 280 deletions(-)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261101-work-procfs-bugfixes-054d00028491


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 01/51] alloc_tag: don't remove /proc/allocinfo under the codetag module lock
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
@ 2026-11-04 19:06   ` Christian Brauner
  2026-11-05  2:15     ` Hao Ge
  2026-11-04 19:16   ` [PATCH RESEND v2 02/51] scsi: esas2r: don't copy kernel pointers out in EXPRESS_IOCTL_GET_MOD_INFO Christian Brauner
                     ` (33 subsequent siblings)
  34 siblings, 1 reply; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:06 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

codetag_alloc_module_section() takes the codetag type's mod_lock for
writing and calls reserve_module_tags() with it held. When that can't
reserve the tags, because the section's alignment doesn't fit, because
there are too many tags for the page flag bits or because the memory
for them can't be allocated, it shuts memory allocation profiling down
and removes /proc/allocinfo, still with mod_lock held.

remove_proc_entry() waits until every reader of the entry has let go of
it, and a reader of /proc/allocinfo takes mod_lock for reading in
allocinfo_start() while it holds the entry. So a module load that hits
one of those paths while /proc/allocinfo is being read waits for the
reader and the reader waits for the module load, for good.

Let a work item remove the entry in that case. The profiling is shut
down right away like before, the file just goes a moment later.

Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 mm/alloc_tag.c | 27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
index 82e2c3448dcf..7d7ad6704953 100644
--- a/mm/alloc_tag.c
+++ b/mm/alloc_tag.c
@@ -15,6 +15,7 @@
 #include <linux/seq_file.h>
 #include <linux/string_choices.h>
 #include <linux/vmalloc.h>
+#include <linux/workqueue.h>
 #include <linux/kmemleak.h>
 #include <uapi/linux/alloc_tag.h>
 
@@ -604,6 +605,26 @@ static void shutdown_mem_profiling(bool remove_file)
 	mem_profiling_support = false;
 }
 
+static void remove_allocinfo_file(struct work_struct *work)
+{
+	remove_proc_entry(ALLOCINFO_FILE_NAME, NULL);
+}
+static DECLARE_WORK(remove_allocinfo_work, remove_allocinfo_file);
+
+/*
+ * A module's tags are reserved with the codetag module lock held for
+ * writing, and a reader of /proc/allocinfo takes that lock with the entry
+ * pinned. Removing the entry there would wait for that reader for good, so
+ * let a work item do it.
+ */
+static void shutdown_mem_profiling_async(void)
+{
+	if (!mem_profiling_support)
+		return;
+	shutdown_mem_profiling(false);
+	schedule_work(&remove_allocinfo_work);
+}
+
 void __init alloc_tag_sec_init(void)
 {
 	struct alloc_tag *last_codetag;
@@ -855,7 +876,7 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
 		align = 1;
 
 	if (!ensure_alignment(align, &prepend)) {
-		shutdown_mem_profiling(true);
+		shutdown_mem_profiling_async();
 		pr_err("%s: alignment %lu is incompatible with allocation tag indexing. Memory allocation profiling is disabled!\n",
 			mod->name, align);
 		return ERR_PTR(-EINVAL);
@@ -905,14 +926,14 @@ unlock:
 
 		module_tags.size = offset + size;
 		if (mem_alloc_profiling_enabled() && !tags_addressable()) {
-			shutdown_mem_profiling(true);
+			shutdown_mem_profiling_async();
 			pr_warn("With module %s there are too many tags to fit in %d page flag bits. Memory allocation profiling is disabled!\n",
 				mod->name, NR_UNUSED_PAGEFLAG_BITS);
 		}
 
 		grow_res = vm_module_tags_populate();
 		if (grow_res) {
-			shutdown_mem_profiling(true);
+			shutdown_mem_profiling_async();
 			pr_err("Failed to allocate memory for allocation tags in the module %s. Memory allocation profiling is disabled!\n",
 			       mod->name);
 			return ERR_PTR(grow_res);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 02/51] scsi: esas2r: don't copy kernel pointers out in EXPRESS_IOCTL_GET_MOD_INFO
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
  2026-11-04 19:06   ` [PATCH RESEND v2 01/51] alloc_tag: don't remove /proc/allocinfo under the codetag module lock Christian Brauner
@ 2026-11-04 19:16   ` Christian Brauner
  2026-11-04 19:16   ` [PATCH RESEND v2 03/51] ALSA: cs46xx: hand the region to the region proc files Christian Brauner
                     ` (32 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:16 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

EXPRESS_IOCTL_GET_MOD_INFO fills in the addresses of the adapter, its
pci_dev and its Scsi_Host and copies the whole struct back to userspace.
Nothing in userspace can do anything with those addresses, and the
ioctl is reachable through /proc/scsi/esas2r/ATTONode, which is created
with mode 0444. Only pass the host number.

Fixes: 26780d9e12ed ("[SCSI] esas2r: ATTO Technology ExpressSAS 6G SAS/SATA RAID Adapter Driver")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/esas2r/esas2r_ioctl.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c
index 3f7c1d131ec3..c93d9265e100 100644
--- a/drivers/scsi/esas2r/esas2r_ioctl.c
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -1468,9 +1468,10 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
 
 	case EXPRESS_IOCTL_GET_MOD_INFO:
 
-		ioctl->data.modinfo.adapter = a;
-		ioctl->data.modinfo.pci_dev = a->pcid;
-		ioctl->data.modinfo.scsi_host = a->host;
+		/* these are kernel pointers, userspace has no use for them */
+		ioctl->data.modinfo.adapter = NULL;
+		ioctl->data.modinfo.pci_dev = NULL;
+		ioctl->data.modinfo.scsi_host = NULL;
 		ioctl->data.modinfo.host_no = a->host->host_no;
 
 		break;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 03/51] ALSA: cs46xx: hand the region to the region proc files
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
  2026-11-04 19:06   ` [PATCH RESEND v2 01/51] alloc_tag: don't remove /proc/allocinfo under the codetag module lock Christian Brauner
  2026-11-04 19:16   ` [PATCH RESEND v2 02/51] scsi: esas2r: don't copy kernel pointers out in EXPRESS_IOCTL_GET_MOD_INFO Christian Brauner
@ 2026-11-04 19:16   ` Christian Brauner
  2026-11-07  9:00     ` Takashi Iwai
  2026-11-04 19:16   ` [PATCH RESEND v2 04/51] PNP: isapnp: don't go on when isapnp_cfg_begin() fails Christian Brauner
                     ` (31 subsequent siblings)
  34 siblings, 1 reply; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:16 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

snd_cs46xx_proc_init() stores the chip in the private data of the five
region entries, but snd_cs46xx_io_read() reads that back as the region,
so a read of /proc/asound/cardN/CS46xx_BA0 and friends copies from
whatever sits at the region's offset in struct snd_cs46xx instead of
the mapped registers. Store the region.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 sound/pci/cs46xx/cs46xx_lib.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 797a5f09cade..efc7df444cb5 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2764,7 +2764,7 @@ static int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
 		struct snd_cs46xx_region *region = &chip->region.idx[idx];
 		if (! snd_card_proc_new(card, region->name, &entry)) {
 			entry->content = SNDRV_INFO_CONTENT_DATA;
-			entry->private_data = chip;
+			entry->private_data = region;
 			entry->c.ops = &snd_cs46xx_proc_io_ops;
 			entry->size = region->size;
 			entry->mode = S_IFREG | 0400;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 04/51] PNP: isapnp: don't go on when isapnp_cfg_begin() fails
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-04 19:16   ` [PATCH RESEND v2 03/51] ALSA: cs46xx: hand the region to the region proc files Christian Brauner
@ 2026-11-04 19:16   ` Christian Brauner
  2026-11-04 19:17   ` [PATCH RESEND v2 05/51] xtensa: simdisk: remove the proc entry before the disk Christian Brauner
                     ` (30 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:16 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

isapnp_cfg_begin() refuses a logical device number above 10 and a bad
card number with -EINVAL before it takes isapnp_cfg_mutex. The callers
that read and write the device's configuration, the proc file included,
don't look at that: they go on talking to whatever device the card
currently has selected and then unlock a mutex they never took in
isapnp_cfg_end(). isapnp_create_device() hands out logical device
numbers without a bound, so a card with twelve or more logical devices
gets there. Return the error instead.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pnp/isapnp/core.c | 16 ++++++++++++----
 drivers/pnp/isapnp/proc.c |  5 ++++-
 sound/isa/es18xx.c        |  6 +++++-
 3 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c
index 219f96f2aaaf..3591a2791c1a 100644
--- a/drivers/pnp/isapnp/core.c
+++ b/drivers/pnp/isapnp/core.c
@@ -853,7 +853,9 @@ static int isapnp_get_resources(struct pnp_dev *dev)
 
 	pnp_dbg(&dev->dev, "get resources\n");
 	pnp_init_resources(dev);
-	isapnp_cfg_begin(dev->card->number, dev->number);
+	ret = isapnp_cfg_begin(dev->card->number, dev->number);
+	if (ret)
+		return ret;
 	dev->active = isapnp_read_byte(ISAPNP_CFG_ACTIVATE);
 	if (!dev->active)
 		goto __end;
@@ -887,10 +889,12 @@ __end:
 static int isapnp_set_resources(struct pnp_dev *dev)
 {
 	struct resource *res;
-	int tmp;
+	int tmp, ret;
 
 	pnp_dbg(&dev->dev, "set resources\n");
-	isapnp_cfg_begin(dev->card->number, dev->number);
+	ret = isapnp_cfg_begin(dev->card->number, dev->number);
+	if (ret)
+		return ret;
 	dev->active = 1;
 	for (tmp = 0; tmp < ISAPNP_MAX_PORT; tmp++) {
 		res = pnp_get_resource(dev, IORESOURCE_IO, tmp);
@@ -936,9 +940,13 @@ static int isapnp_set_resources(struct pnp_dev *dev)
 
 static int isapnp_disable_resources(struct pnp_dev *dev)
 {
+	int ret;
+
 	if (!dev->active)
 		return -EINVAL;
-	isapnp_cfg_begin(dev->card->number, dev->number);
+	ret = isapnp_cfg_begin(dev->card->number, dev->number);
+	if (ret)
+		return ret;
 	isapnp_deactivate(dev->number);
 	dev->active = 0;
 	isapnp_cfg_end();
diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c
index c1f6e6bd0eb2..b3abab24050b 100644
--- a/drivers/pnp/isapnp/proc.c
+++ b/drivers/pnp/isapnp/proc.c
@@ -25,6 +25,7 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf,
 	struct pnp_dev *dev = pde_data(file_inode(file));
 	int pos = *ppos;
 	int cnt, size = 256;
+	int ret;
 
 	if (pos >= size)
 		return 0;
@@ -37,7 +38,9 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf,
 	if (!access_ok(buf, cnt))
 		return -EINVAL;
 
-	isapnp_cfg_begin(dev->card->number, dev->number);
+	ret = isapnp_cfg_begin(dev->card->number, dev->number);
+	if (ret)
+		return ret;
 	for (; pos < 256 && cnt > 0; pos++, buf++, cnt--) {
 		unsigned char val;
 		val = isapnp_read_byte(pos);
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index 3cadbf7cf238..de1eda5a0656 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -1950,7 +1950,11 @@ static int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
 	/* ok. hack using Vendor-Defined Card-Level registers */
 	/* skip csn and logdev initialization - already done in isapnp_configure */
 	if (pnp_device_is_isapnp(pdev)) {
-		isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
+		int ret = isapnp_cfg_begin(isapnp_card_number(pdev),
+					   isapnp_csn_number(pdev));
+
+		if (ret)
+			return ret;
 		isapnp_write_byte(0x27, pnp_irq(pdev, 0));	/* Hardware Volume IRQ Number */
 		if (mpu_port[dev] != SNDRV_AUTO_PORT)
 			isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 05/51] xtensa: simdisk: remove the proc entry before the disk
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-04 19:16   ` [PATCH RESEND v2 04/51] PNP: isapnp: don't go on when isapnp_cfg_begin() fails Christian Brauner
@ 2026-11-04 19:17   ` Christian Brauner
  2026-11-04 19:17   ` [PATCH RESEND v2 08/51] powerpc/pseries/lparcfg: don't drop the last byte of a write Christian Brauner
                     ` (29 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:17 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

simdisk_teardown() frees the gendisk and only then removes
/proc/simdisk/<n>. A write to that entry in between attaches the file
that was written to the disk, and simdisk_attach() sets the capacity of
the gendisk that is gone. Remove the entry first, remove_proc_entry()
waits for a writer that is in the middle of that.

Fixes: b6c7e873daf7 ("xtensa: ISS: add host file-based simulated disk")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/xtensa/platforms/iss/simdisk.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index a2ddb49c269d..e51edb8cf872 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -379,12 +379,13 @@ static void simdisk_teardown(struct simdisk *dev, int which,
 {
 	char tmp[2] = { '0' + which, 0 };
 
+	/* a write to the entry attaches a file to the disk, so it goes first */
+	remove_proc_entry(tmp, procdir);
 	simdisk_detach(dev);
 	if (dev->gd) {
 		del_gendisk(dev->gd);
 		put_disk(dev->gd);
 	}
-	remove_proc_entry(tmp, procdir);
 }
 
 static void __exit simdisk_exit(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 08/51] powerpc/pseries/lparcfg: don't drop the last byte of a write
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (4 preceding siblings ...)
  2026-11-04 19:17   ` [PATCH RESEND v2 05/51] xtensa: simdisk: remove the proc entry before the disk Christian Brauner
@ 2026-11-04 19:17   ` Christian Brauner
  2026-11-04 19:19   ` [PATCH RESEND v2 13/51] sched/psi: don't drop the last byte of a trigger write Christian Brauner
                     ` (28 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:17 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

lparcfg_write() terminates its copy of the write by overwriting the
last byte, so a write without a trailing newline loses its last
character: "partition_entitled_capacity=5" is parsed as an empty value
and fails with -EINVAL. Terminate behind the copy instead, and refuse a
write that fills the whole buffer, which can't be terminated.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/platforms/pseries/lparcfg.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c
index 3280b7968cab..9882f98c0efb 100644
--- a/arch/powerpc/platforms/pseries/lparcfg.c
+++ b/arch/powerpc/platforms/pseries/lparcfg.c
@@ -699,13 +699,12 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf,
 	if (!firmware_has_feature(FW_FEATURE_SPLPAR))
 		return -EINVAL;
 
-	if (count == 0 || count > sizeof(kbuf))
+	if (count == 0 || count >= sizeof(kbuf))
 		return -EINVAL;
 
 	if (copy_from_user(kbuf, buf, count))
 		return -EFAULT;
-
-	kbuf[count - 1] = '\0';
+	kbuf[count] = '\0';
 	tmp = strchr(kbuf, '=');
 	if (!tmp)
 		return -EINVAL;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 13/51] sched/psi: don't drop the last byte of a trigger write
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (5 preceding siblings ...)
  2026-11-04 19:17   ` [PATCH RESEND v2 08/51] powerpc/pseries/lparcfg: don't drop the last byte of a write Christian Brauner
@ 2026-11-04 19:19   ` Christian Brauner
  2026-11-04 19:19   ` [PATCH RESEND v2 14/51] proc: sysctl: don't stop readdir at an entry that is being unregistered Christian Brauner
                     ` (27 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:19 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

psi_write() copies at most 32 bytes of the write into its buffer and
terminates the buffer by overwriting its last byte. For a write shorter
than 32 bytes that is the last byte the writer sent. A trigger written
without a trailing newline loses its last digit then, "some 150000
1000000" turns into "some 150000 100000", and a shorter one like
"some 150 10000" fails with -EINVAL because the window doesn't fit
anymore. Only writes that end in a newline parse as intended.

Copy at most 31 bytes and terminate behind them.

Fixes: 0e94682b73bf ("psi: introduce psi monitor")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/sched/psi.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index 7c5381423334..8b59635e95d4 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1566,11 +1566,11 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
 	if (!nbytes)
 		return -EINVAL;
 
-	buf_size = min(nbytes, sizeof(buf));
+	buf_size = min(nbytes, sizeof(buf) - 1);
 	if (copy_from_user(buf, user_buf, buf_size))
 		return -EFAULT;
 
-	buf[buf_size - 1] = '\0';
+	buf[buf_size] = '\0';
 
 	seq = file->private_data;
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 14/51] proc: sysctl: don't stop readdir at an entry that is being unregistered
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (6 preceding siblings ...)
  2026-11-04 19:19   ` [PATCH RESEND v2 13/51] sched/psi: don't drop the last byte of a trigger write Christian Brauner
@ 2026-11-04 19:19   ` Christian Brauner
  2026-11-04 19:19   ` [PATCH RESEND v2 15/51] PCI/proc: don't truncate the position of config space accesses Christian Brauner
                     ` (26 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:19 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_sys_fill_cache() creates the inode of an entry that has no dentry
yet. Since ace0c791e6c3 ("proc/sysctl: Don't grab i_lock under
sysctl_lock.") proc_sys_make_inode() refuses to do that once the table
is being unregistered and fails with -ENOENT. readdir takes every table
it lists into use and unregister_sysctl_table() marks the table as
being unregistered before it waits for readdir to let go of it. So an
entry that is unregistered right then makes proc_sys_fill_cache() fail,
proc_sys_readdir() stops there and returns 0. If that getdents64() call
hadn't emitted anything before that entry userspace sees the end of the
directory and never gets the entries behind it. A listing of
/proc/sys/net/ipv6/conf can end early that way while a device goes
away.

So skip an entry whose table is being unregistered, like readdir
already skips a link it can't follow. Lookup treats such an entry as
gone too since ea5751ccd665 ("proc/sysctl: don't return ENOMEM on
lookup when a table is unregistering").

Fixes: ace0c791e6c3 ("proc/sysctl: Don't grab i_lock under sysctl_lock.")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 6c0859c66563..b4a309d49698 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -704,6 +704,9 @@ static bool proc_sys_fill_cache(struct file *file,
 			if (unlikely(res)) {
 				dput(child);
 
+				/* skip an entry whose table is being unregistered */
+				if (res == ERR_PTR(-ENOENT))
+					return true;
 				if (IS_ERR(res))
 					return false;
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 15/51] PCI/proc: don't truncate the position of config space accesses
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (7 preceding siblings ...)
  2026-11-04 19:19   ` [PATCH RESEND v2 14/51] proc: sysctl: don't stop readdir at an entry that is being unregistered Christian Brauner
@ 2026-11-04 19:19   ` Christian Brauner
  2026-11-04 19:19   ` [PATCH RESEND v2 16/51] PNP: isapnp: don't read past the end of the buffer on a large pread() Christian Brauner
                     ` (25 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:19 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_bus_pci_write() truncates the file position to an int. A pwrite()
at an offset like 0xfffff000 turns it negative, the check against the
size of the config space passes and the count it clamps to ends up
larger than what was written. The loop then reads past the end of the
caller's buffer and writes all of it into config space at negative
offsets, which pci_user_write_config_*() pass on to the config
accessors unchecked. The write also returns more than it was asked to
write.

An offset above 4G that truncates to a small positive value writes to
the start of config space instead. proc_bus_pci_read() keeps the
position in an unsigned int, so a read at such an offset returns the
start of config space instead of hitting EOF.

Check the position against the size of the config space before
narrowing it.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pci/proc.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 3d7a9ed5fc22..e0dc70123f82 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -32,8 +32,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 				 size_t nbytes, loff_t *ppos)
 {
 	struct pci_dev *dev = pde_data(file_inode(file));
-	unsigned int pos = *ppos;
-	unsigned int cnt, size;
+	unsigned int pos, cnt, size;
 
 	/*
 	 * Normal users can read only the standardized portion of the
@@ -51,8 +50,9 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 	if (!nbytes)
 		return 0;
 
-	if (pos >= size)
+	if (*ppos >= size)
 		return 0;
+	pos = *ppos;
 	if (nbytes >= size)
 		nbytes = size;
 	if (pos + nbytes > size)
@@ -119,9 +119,8 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
 {
 	struct inode *ino = file_inode(file);
 	struct pci_dev *dev = pde_data(ino);
-	int pos = *ppos;
 	int size = dev->cfg_size;
-	int cnt, ret;
+	int pos, cnt, ret;
 
 	ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
 	if (ret)
@@ -130,14 +129,16 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
 	if (!nbytes)
 		return 0;
 
+	if (*ppos >= size)
+		return 0;
+	pos = *ppos;
+
 	if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) {
 		pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x",
 			      current->comm, pos);
 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
 	}
 
-	if (pos >= size)
-		return 0;
 	if (nbytes >= size)
 		nbytes = size;
 	if (pos + nbytes > size)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 16/51] PNP: isapnp: don't read past the end of the buffer on a large pread()
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (8 preceding siblings ...)
  2026-11-04 19:19   ` [PATCH RESEND v2 15/51] PCI/proc: don't truncate the position of config space accesses Christian Brauner
@ 2026-11-04 19:19   ` Christian Brauner
  2026-11-04 19:20   ` [PATCH RESEND v2 17/51] s390/dasd: don't look in front of the buffer on an empty write Christian Brauner
                     ` (24 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:19 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever,
	Rafael J. Wysocki (Intel), stable

isapnp_proc_bus_read() truncates the position to an int. A pread() at
an offset like 0xfffffc00 turns it negative, the check against the size
of the configuration space passes, and the loop then puts everything
from that position up to byte 256 into the reader's buffer, no matter
how few bytes were asked for.

Keep the position as a loff_t and clamp the count against what is left
of the configuration space.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pnp/isapnp/proc.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c
index b3abab24050b..1829c20d5446 100644
--- a/drivers/pnp/isapnp/proc.c
+++ b/drivers/pnp/isapnp/proc.c
@@ -23,15 +23,13 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf,
 				    size_t nbytes, loff_t * ppos)
 {
 	struct pnp_dev *dev = pde_data(file_inode(file));
-	int pos = *ppos;
+	loff_t pos = *ppos;
 	int cnt, size = 256;
 	int ret;
 
 	if (pos >= size)
 		return 0;
-	if (nbytes >= size)
-		nbytes = size;
-	if (pos + nbytes > size)
+	if (nbytes > size - pos)
 		nbytes = size - pos;
 	cnt = nbytes;
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 17/51] s390/dasd: don't look in front of the buffer on an empty write
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (9 preceding siblings ...)
  2026-11-04 19:19   ` [PATCH RESEND v2 16/51] PNP: isapnp: don't read past the end of the buffer on a large pread() Christian Brauner
@ 2026-11-04 19:20   ` Christian Brauner
  2026-11-04 19:20   ` [PATCH RESEND v2 18/51] platform/x86: toshiba_acpi: reject malformed brightness and fan writes Christian Brauner
                     ` (23 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:20 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

dasd_get_user_string() strips a trailing newline by looking at the last
byte that was written. For an empty write that's the byte in front of
the buffer it just allocated. Only look if something was written.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/s390/block/dasd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 2ffe404b890f..e97891e58957 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -933,7 +933,7 @@ char *dasd_get_user_string(const char __user *user_buf, size_t user_len)
 		return ERR_PTR(-EFAULT);
 	}
 	/* got the string, now strip linefeed. */
-	if (buffer[user_len - 1] == '\n')
+	if (user_len && buffer[user_len - 1] == '\n')
 		buffer[user_len - 1] = 0;
 	else
 		buffer[user_len] = 0;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 18/51] platform/x86: toshiba_acpi: reject malformed brightness and fan writes
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (10 preceding siblings ...)
  2026-11-04 19:20   ` [PATCH RESEND v2 17/51] s390/dasd: don't look in front of the buffer on an empty write Christian Brauner
@ 2026-11-04 19:20   ` Christian Brauner
  2026-11-04 19:20   ` [PATCH RESEND v2 19/51] xtensa: simdisk: don't read the file name without the lock Christian Brauner
                     ` (22 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:20 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

When lcd_proc_write() and fan_proc_write() were refactored their checks
were inverted but the && stayed. They only fail if sscanf() didn't
parse anything and the value is out of range at the same time, and for
the brightness value < 0 && value > levels can't be true at all. So an
out of range brightness or fan setting is passed on to the hardware,
and so is the uninitialized value if nothing could be parsed.

Restore the checks as they were before the refactoring.

Fixes: e1a949c1b988 ("toshiba_acpi: Refactor *{get, set} functions return value")
Fixes: 3e07e5baa2af ("toshiba_acpi: Add set_fan_status function")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/platform/x86/toshiba_acpi.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index a0b8060836d0..9eb8f1e5aeda 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1498,8 +1498,8 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
 	cmd[len] = '\0';
 
 	levels = dev->backlight_dev->props.max_brightness + 1;
-	if (sscanf(cmd, " brightness : %i", &value) != 1 &&
-	    value < 0 && value > levels)
+	if (sscanf(cmd, " brightness : %i", &value) != 1 ||
+	    value < 0 || value >= levels)
 		return -EINVAL;
 
 	if (set_lcd_brightness(dev, value))
@@ -1678,8 +1678,8 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf,
 		return -EFAULT;
 	cmd[len] = '\0';
 
-	if (sscanf(cmd, " force_on : %i", &value) != 1 &&
-	    value != 0 && value != 1)
+	if (sscanf(cmd, " force_on : %i", &value) != 1 ||
+	    value < 0 || value > 1)
 		return -EINVAL;
 
 	if (set_fan_status(dev, value))

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 19/51] xtensa: simdisk: don't read the file name without the lock
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (11 preceding siblings ...)
  2026-11-04 19:20   ` [PATCH RESEND v2 18/51] platform/x86: toshiba_acpi: reject malformed brightness and fan writes Christian Brauner
@ 2026-11-04 19:20   ` Christian Brauner
  2026-11-04 19:20   ` [PATCH RESEND v2 20/51] powerpc/rtas_flash: don't read past the validate message Christian Brauner
                     ` (21 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:20 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_read_simdisk() looks at dev->filename without holding dev->lock. A
write to the same file detaches the disk and frees the name under the
lock, so a concurrent reader can copy out freed memory. Copy the name
with the lock held.

Fixes: b6c7e873daf7 ("xtensa: ISS: add host file-based simulated disk")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/xtensa/platforms/iss/simdisk.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index e51edb8cf872..1248d960e5d3 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -238,22 +238,21 @@ static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
 			size_t size, loff_t *ppos)
 {
 	struct simdisk *dev = pde_data(file_inode(file));
-	const char *s = dev->filename;
-	if (s) {
-		ssize_t len = strlen(s);
-		char *temp = kmalloc(len + 2, GFP_KERNEL);
+	ssize_t len;
+	char *temp;
 
-		if (!temp)
-			return -ENOMEM;
+	/* a write sets at most a page, see proc_write_simdisk() */
+	temp = kmalloc(PAGE_SIZE + 2, GFP_KERNEL);
+	if (!temp)
+		return -ENOMEM;
 
-		len = scnprintf(temp, len + 2, "%s\n", s);
-		len = simple_read_from_buffer(buf, size, ppos,
-					      temp, len);
+	spin_lock(&dev->lock);
+	len = scnprintf(temp, PAGE_SIZE + 2, "%s\n", dev->filename ?: "");
+	spin_unlock(&dev->lock);
 
-		kfree(temp);
-		return len;
-	}
-	return simple_read_from_buffer(buf, size, ppos, "\n", 1);
+	len = simple_read_from_buffer(buf, size, ppos, temp, len);
+	kfree(temp);
+	return len;
 }
 
 static ssize_t proc_write_simdisk(struct file *file, const char __user *buf,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 20/51] powerpc/rtas_flash: don't read past the validate message
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (12 preceding siblings ...)
  2026-11-04 19:20   ` [PATCH RESEND v2 19/51] xtensa: simdisk: don't read the file name without the lock Christian Brauner
@ 2026-11-04 19:20   ` Christian Brauner
  2026-11-04 19:21   ` [PATCH RESEND v2 21/51] powerpc/rtasd: zero the error log buffer Christian Brauner
                     ` (20 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:20 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

get_validate_flash_msg() adds up what snprintf() returns, which is the
length of the whole string and not what fit into msg. If the update
results the firmware hands back don't fit into what is left of the 256
bytes, the length ends up larger than msg and validate_flash_read()
copies whatever follows it on the stack to userspace. Use scnprintf().

Fixes: a94a14720eaf ("powerpc/rtas_flash: Fix validate_flash buffer overflow issue")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/rtas_flash.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
index 722dbfb6fbf8..f1cd105f7426 100644
--- a/arch/powerpc/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -448,7 +448,7 @@ static int get_validate_flash_msg(struct rtas_validate_flash_t *args_buf,
 		n = sprintf(msg, "%d\n", args_buf->update_results);
 		if ((args_buf->update_results >= VALIDATE_CUR_UNKNOWN) ||
 		    (args_buf->update_results == VALIDATE_TMP_UPDATE))
-			n += snprintf(msg + n, msglen - n, "%s\n",
+			n += scnprintf(msg + n, msglen - n, "%s\n",
 					args_buf->buf);
 	} else {
 		n = sprintf(msg, "%d\n", args_buf->status);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 21/51] powerpc/rtasd: zero the error log buffer
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (13 preceding siblings ...)
  2026-11-04 19:20   ` [PATCH RESEND v2 20/51] powerpc/rtas_flash: don't read past the validate message Christian Brauner
@ 2026-11-04 19:21   ` Christian Brauner
  2026-11-04 19:21   ` [PATCH RESEND v2 22/51] ALSA: info: don't write past the end of a data entry Christian Brauner
                     ` (19 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

rtas_log_read() hands out a whole slot of the error log but
pSeries_log_error() only fills in the sequence number and the event,
which is usually a lot shorter. The rest of the slot is whatever
vmalloc() left there. Use vzalloc().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/rtasd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index fd40864bdb70..dd0df5e164a3 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -523,7 +523,7 @@ static int __init rtas_event_scan_init(void)
 	rtas_error_log_max = rtas_get_error_log_max();
 	rtas_error_log_buffer_max = rtas_error_log_max + sizeof(int);
 
-	rtas_log_buf = vmalloc(array_size(LOG_NUMBER,
+	rtas_log_buf = vzalloc(array_size(LOG_NUMBER,
 					  rtas_error_log_buffer_max));
 	if (!rtas_log_buf) {
 		printk(KERN_ERR "rtasd: no memory\n");

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 22/51] ALSA: info: don't write past the end of a data entry
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (14 preceding siblings ...)
  2026-11-04 19:21   ` [PATCH RESEND v2 21/51] powerpc/rtasd: zero the error log buffer Christian Brauner
@ 2026-11-04 19:21   ` Christian Brauner
  2026-11-04 19:22   ` [PATCH RESEND v2 26/51] scsi: core: NUL-terminate what is written to the host proc files Christian Brauner
                     ` (18 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

snd_info_entry_write() clamps the count to what is left of the entry
by computing entry->size - pos as a size_t. For a position past the end
that wraps and the write goes to the driver unclamped, for opl4 into
sample memory outside of what the entry covers. At the end of the entry
the driver gets a count of 0 and vmalloc(0) warns. Before the clamp
moved into the common code the driver ignored such writes. Fail them
with -ENOSPC instead, like a block device does at the end of the
device. Returning 0 would make writers that use stdio retry forever.

Fixes: d97e1b78239c ("ALSA: info - Check file position validity in common layer")
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 sound/core/info.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/core/info.c b/sound/core/info.c
index 023b4736aa6d..f608d335b6c9 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -171,6 +171,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
 	pos = *offset;
 	if (!valid_pos(pos, count))
 		return -EIO;
+	if (pos >= entry->size)
+		return count ? -ENOSPC : 0;
 	if (count > 0) {
 		size_t maxsize = entry->size - pos;
 		count = min(count, maxsize);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 26/51] scsi: core: NUL-terminate what is written to the host proc files
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (15 preceding siblings ...)
  2026-11-04 19:21   ` [PATCH RESEND v2 22/51] ALSA: info: don't write past the end of a data entry Christian Brauner
@ 2026-11-04 19:22   ` Christian Brauner
  2026-11-04 19:22   ` [PATCH RESEND v2 27/51] um: NUL-terminate what is written to /proc/exitcode Christian Brauner
                     ` (17 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:22 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_scsi_host_write() copies what is written into a page and hands it
to ->write_info() without terminating it. ppa and imm parse it with
strncmp() and simple_strtoul(), which read on past what was written into
whatever the page held before. Terminate the copy. A write is at most
PROC_BLOCK_SIZE bytes, so the page always has room for that.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/scsi_proc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index fdc355d783da..dbd03360b5a4 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -80,6 +80,7 @@ static ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
 		ret = -EFAULT;
 		if (copy_from_user(page, buf, count))
 			goto out;
+		page[count] = '\0';
 		ret = shost->hostt->write_info(shost, page, count);
 	}
 out:

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 27/51] um: NUL-terminate what is written to /proc/exitcode
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (16 preceding siblings ...)
  2026-11-04 19:22   ` [PATCH RESEND v2 26/51] scsi: core: NUL-terminate what is written to the host proc files Christian Brauner
@ 2026-11-04 19:22   ` Christian Brauner
  2026-11-04 19:22   ` [PATCH RESEND v2 28/51] ntfs3: don't warn when a huge label is written Christian Brauner
                     ` (16 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:22 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

exitcode_proc_write() copies up to sizeof(buf) bytes of the write into
buf and hands it to simple_strtol() without terminating it. So
"echo -n 5" parses whatever the stack held after the '5', and a write of
seven bytes or more reads past the end of buf. Terminate the copy.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/um/kernel/exitcode.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c
index 43edc2aa57e4..0230354f4812 100644
--- a/arch/um/kernel/exitcode.c
+++ b/arch/um/kernel/exitcode.c
@@ -43,9 +43,10 @@ static ssize_t exitcode_proc_write(struct file *file,
 	size_t size;
 	int tmp;
 
-	size = min(count, sizeof(buf));
+	size = min(count, sizeof(buf) - 1);
 	if (copy_from_user(buf, buffer, size))
 		return -EFAULT;
+	buf[size] = '\0';
 
 	tmp = simple_strtol(buf, &end, 0);
 	if ((*end != '\0') && !isspace(*end))

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 28/51] ntfs3: don't warn when a huge label is written
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (17 preceding siblings ...)
  2026-11-04 19:22   ` [PATCH RESEND v2 27/51] um: NUL-terminate what is written to /proc/exitcode Christian Brauner
@ 2026-11-04 19:22   ` Christian Brauner
  2026-11-04 19:23   ` [PATCH RESEND v2 29/51] proc: don't walk through a non-directory when resolving a name Christian Brauner
                     ` (15 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:22 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

ntfs3_label_write() allocates as many bytes as are written with plain
kmalloc(). A write that is larger than what kmalloc() can hand out then
trips the page allocator's warning, which panics a panic_on_warn kernel.
Use memdup_user(), which fails such an allocation quietly with -ENOMEM
like before.

Fixes: 7832e123490a ("fs/ntfs3: Add support /proc/fs/ntfs3/<dev>/volinfo and /proc/fs/ntfs3/<dev>/label")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/ntfs3/super.c | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 29aabdc8a801..37e27b73b7a5 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -576,15 +576,10 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
 	if (sb_rdonly(sb))
 		return -EROFS;
 
-	label = kmalloc(count, GFP_NOFS);
+	label = memdup_user(buffer, count);
+	if (IS_ERR(label))
+		return PTR_ERR(label);
 
-	if (!label)
-		return -ENOMEM;
-
-	if (copy_from_user(label, buffer, ret)) {
-		ret = -EFAULT;
-		goto out;
-	}
 	while (ret > 0 && label[ret - 1] == '\n')
 		ret -= 1;
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 29/51] proc: don't walk through a non-directory when resolving a name
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (18 preceding siblings ...)
  2026-11-04 19:22   ` [PATCH RESEND v2 28/51] ntfs3: don't warn when a huge label is written Christian Brauner
@ 2026-11-04 19:23   ` Christian Brauner
  2026-11-04 19:23   ` [PATCH RESEND v2 30/51] net: pktgen: reject device names with a '/' Christian Brauner
                     ` (14 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:23 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

__xlate_proc_name() walks the components of a name like "a/b/c" and
takes whatever it finds for "a" and "b" as the directory to go on in.
It never checks that they actually are directories. So
proc_create("file/x") hangs x into the subdir tree of the regular file
"file". Nobody can look x up there, but remove_proc_subtree() on the
file removes and frees x along with it, behind the back of whoever
registered x.

pktgen passes the name the writer of "add_device" picks to
proc_create_data(), and only the part in front of the '@' has to name
a network device:

  echo "add_device lo@"   > /proc/net/pktgen/kpktgend_0
  echo "add_device lo@/x" > /proc/net/pktgen/kpktgend_1
  echo "rem_device_all"   > /proc/net/pktgen/kpktgend_0
  echo "rem_device_all"   > /proc/net/pktgen/kpktgend_1

The third write frees both entries and the fourth one makes kpktgend_1
call proc_remove() on the freed second one:

  BUG: KASAN: slab-use-after-free in proc_remove+0x8f/0xa0
  Read of size 8 at addr ff110001134c8e70 by task kpktgend_1/243
  Call Trace:
   proc_remove+0x8f/0xa0
   pktgen_remove_device.isra.0+0x118/0x500
   pktgen_rem_all_ifs+0xeb/0x1a0
   pktgen_thread_worker+0x1b42/0x7280

The files of a network namespace belong to the root of its user
namespace and these writes don't require a capability. So once pktgen
is loaded an unprivileged user can do this in namespaces of its own.

Treat a component that isn't a directory like one that doesn't exist.
All names with a '/' that the tree passes in as literals only walk
through directories. pktgen is fixed separately to not pass such names
in the first place.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 4c3fcaf68ef4..6ac9c4c13897 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -170,7 +170,7 @@ static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
 	de = *ret ?: &proc_root;
 	while ((next = strchr(cp, '/')) != NULL) {
 		de = pde_subdir_find(de, cp, next - cp);
-		if (!de) {
+		if (!de || !S_ISDIR(de->mode)) {
 			WARN(1, "name '%s'\n", name);
 			return -ENOENT;
 		}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 30/51] net: pktgen: reject device names with a '/'
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (19 preceding siblings ...)
  2026-11-04 19:23   ` [PATCH RESEND v2 29/51] proc: don't walk through a non-directory when resolving a name Christian Brauner
@ 2026-11-04 19:23   ` Christian Brauner
  2026-11-04 19:23   ` [PATCH RESEND v2 31/51] proc: don't WARN about a name that is already registered Christian Brauner
                     ` (13 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:23 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

"add_device" takes a name like eth0@1. Only the part in front of the
'@' has to name a network device. The rest is free text and all of it
becomes the name of a file in /proc/net/pktgen, where procfs takes a
'/' as a directory separator. So "add_device lo@a/b" makes procfs look
for a directory "lo@a" that doesn't exist and warn about it. Any user
can do that in a network namespace of its own:

  name 'lo@a/b'
  WARNING: fs/proc/generic.c:174 at __xlate_proc_name+0xb1/0xf0, CPU#3: bash/241
  Call Trace:
   __proc_create+0xd0/0x9b0
   proc_create_reg+0x45/0xf0
   proc_create_data+0x77/0xf0
   pktgen_thread_write+0xd8d/0x1750

With panic_on_warn that takes the machine down. And if "lo@a" exists
because somebody added a device of that name, procfs used to register
the new entry below that regular file, and removing "lo@a" freed both.

Reject names with a '/'.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: e6fce5b916cd ("pktgen: multiqueue etc.")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/core/pktgen.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index d63eeadddfcc..02af70d360bc 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3831,6 +3831,10 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
 	int err;
 	int node = cpu_to_node(t->cpu);
 
+	/* The name becomes the name of a file in /proc/net/pktgen. */
+	if (strchr(ifname, '/'))
+		return -EINVAL;
+
 	/* We don't allow a device to be on several threads */
 
 	if (__pktgen_NN_threads(t->net, ifname, FIND)) {

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 31/51] proc: don't WARN about a name that is already registered
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (20 preceding siblings ...)
  2026-11-04 19:23   ` [PATCH RESEND v2 30/51] net: pktgen: reject device names with a '/' Christian Brauner
@ 2026-11-04 19:23   ` Christian Brauner
  2026-11-04 19:23   ` [PATCH RESEND v2 32/51] proc: resume readdir after the last name it returned Christian Brauner
                     ` (12 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:23 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_register() WARNs when the directory already has an entry of the
same name and fails the registration. Callers that pick their names
themselves should never get there but some take names from userspace.
pktgen creates a file for each device a writer of "add_device" adds,
named after the device. A device can have any name, including "pgctrl"
or "kpktgend_0", the names of pktgen's own files:

  ip link set lo name pgctrl
  ip link set pgctrl up
  echo "add_device pgctrl" > /proc/net/pktgen/kpktgend_0

  proc_dir_entry 'pktgen/pgctrl' already registered
  WARNING: fs/proc/generic.c:404 at proc_register+0x555/0x8b0, CPU#0: bash/241
  Call Trace:
   proc_create_data+0x9f/0xf0
   pktgen_thread_write+0xd9b/0x1780
   proc_reg_write+0x1c2/0x3c0
   vfs_write+0x1cd/0xfa0

The files of a network namespace belong to the root of its user
namespace and none of this requires a capability. So once pktgen is
loaded an unprivileged user can taint the kernel, or crash it with
panic_on_warn, from namespaces of its own. pktgen itself copes with
the failed registration just fine.

So go back to what proc_register() did before commit 6c2f91e077f1
("proc: use WARN() rather than printk+backtrace") and what
sysfs_warn_dup() does for sysfs: report the name with pr_warn() and
dump the stack. While at it, do that after dropping proc_subdir_lock
instead of printing a backtrace while holding the lock that every
lookup and readdir in procfs needs.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 6c2f91e077f1 ("proc: use WARN() rather than printk+backtrace")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 6ac9c4c13897..9e59796349e1 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -402,9 +402,10 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
 	write_lock(&proc_subdir_lock);
 	dp->parent = dir;
 	if (pde_subdir_insert(dir, dp) == false) {
-		WARN(1, "proc_dir_entry '%s/%s' already registered\n",
-		     dir->name, dp->name);
 		write_unlock(&proc_subdir_lock);
+		pr_warn("proc_dir_entry '%s/%s' already registered\n",
+			dir->name, dp->name);
+		dump_stack();
 		goto out_free_inum;
 	}
 	write_unlock(&proc_subdir_lock);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 32/51] proc: resume readdir after the last name it returned
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (21 preceding siblings ...)
  2026-11-04 19:23   ` [PATCH RESEND v2 31/51] proc: don't WARN about a name that is already registered Christian Brauner
@ 2026-11-04 19:23   ` Christian Brauner
  2026-11-04 19:24   ` [PATCH RESEND v2 36/51] proc: don't let NOMMU map procfs files Christian Brauner
                     ` (11 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:23 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_readdir_de() counts the entries of a directory and resumes a
listing at the number getdents64() stopped at. The entries are sorted
by name, so one that is added or removed in between shifts everything
behind it by one. Listing /proc/net/dev_snmp6 with a buffer that holds
".", ".." and "lo" and adding a device whose name sorts before "lo"
makes the next call return "lo" a second time. Removing one that sorted
before the position makes the next call skip an entry instead.

Since commit 895b4c0c79b0 ("fs/proc: fix uaf in proc_readdir_de()") an
entry that is removed while it is being emitted also ends the listing
right there. pde_subdir_next() finds nothing after an erased entry, so
proc_readdir_de() reports the end of the directory and the next call
resumes by number, skipping another entry. For /proc itself
proc_root_readdir() takes that as the end of the static entries and
moves on to the pids, so the rest of the static entries is missing.
And if the position is behind the last entry when a call starts
proc_readdir_de() returns 0, so /proc then lists no pids at all.

So remember the name of the last entry a call emitted in the file and
resume at the first entry after it, like kernfs resumes after the node
it stopped at. An entry that was removed while it was emitted is
continued after by its name as well. If the position was moved by a
seek in between the listing counts from the start like before.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: 895b4c0c79b0 ("fs/proc: fix uaf in proc_readdir_de()")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c  | 89 ++++++++++++++++++++++++++++++++++++++++++++++--------
 fs/proc/internal.h |  1 +
 fs/proc/proc_net.c |  1 +
 fs/proc/root.c     |  1 +
 4 files changed, 80 insertions(+), 12 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 9e59796349e1..588999058e66 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -87,6 +87,28 @@ static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,
 	return NULL;
 }
 
+/* The first entry of @dir that sorts after @name. */
+static struct proc_dir_entry *pde_subdir_after(struct proc_dir_entry *dir,
+					       const char *name,
+					       unsigned int len)
+{
+	struct rb_node *node = dir->subdir.rb_node, *after = NULL;
+
+	while (node) {
+		struct proc_dir_entry *de = rb_entry(node,
+						     struct proc_dir_entry,
+						     subdir_node);
+
+		if (proc_match(name, de, len) < 0) {
+			after = node;
+			node = node->rb_left;
+		} else {
+			node = node->rb_right;
+		}
+	}
+	return rb_entry_safe(after, struct proc_dir_entry, subdir_node);
+}
+
 static bool pde_subdir_insert(struct proc_dir_entry *dir,
 			      struct proc_dir_entry *de)
 {
@@ -278,6 +300,40 @@ struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
 	return proc_lookup_de(dir, dentry, PDE(dir));
 }
 
+/*
+ * Where the next readdir resumes. The entries are counted, so one that is
+ * added or removed in front of the position would shift what the next call
+ * returns. The name of the last entry emitted doesn't move.
+ */
+struct proc_dir_cursor {
+	loff_t pos;			/* the position behind the entry */
+	unsigned int len;
+	char name[NAME_MAX + 1];
+};
+
+/* If this can't remember the entry the next call counts from the start. */
+static void proc_dir_cursor_set(struct file *file, struct proc_dir_entry *de,
+				loff_t pos)
+{
+	struct proc_dir_cursor *cursor = file->private_data;
+
+	if (!cursor) {
+		cursor = kmalloc(sizeof(*cursor), GFP_KERNEL);
+		if (!cursor)
+			return;
+		file->private_data = cursor;
+	}
+	cursor->pos = pos;
+	cursor->len = de->namelen;
+	memcpy(cursor->name, de->name, de->namelen);
+}
+
+int proc_dir_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+}
+
 /*
  * This returns non-zero if at EOF, so that the /proc
  * root directory can use this and check if it should
@@ -290,23 +346,26 @@ struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
 int proc_readdir_de(struct file *file, struct dir_context *ctx,
 		    struct proc_dir_entry *de)
 {
+	struct proc_dir_cursor *cursor = file->private_data;
+	struct proc_dir_entry *dir = de;
 	int i;
 
 	if (!dir_emit_dots(file, ctx))
 		return 0;
 
-	i = ctx->pos - 2;
 	read_lock(&proc_subdir_lock);
-	de = pde_subdir_first(de);
-	for (;;) {
-		if (!de) {
-			read_unlock(&proc_subdir_lock);
-			return 0;
-		}
-		if (!i)
-			break;
-		de = pde_subdir_next(de);
-		i--;
+	if (cursor && cursor->pos == ctx->pos) {
+		/* Resume after the last entry emitted, wherever it is now. */
+		de = pde_subdir_after(dir, cursor->name, cursor->len);
+	} else {
+		/* Moved by a seek, count from the start like always. */
+		de = pde_subdir_first(dir);
+		for (i = ctx->pos - 2; de && i; i--)
+			de = pde_subdir_next(de);
+	}
+	if (!de) {
+		read_unlock(&proc_subdir_lock);
+		return 1;
 	}
 
 	do {
@@ -319,8 +378,13 @@ int proc_readdir_de(struct file *file, struct dir_context *ctx,
 			return 0;
 		}
 		ctx->pos++;
+		proc_dir_cursor_set(file, de, ctx->pos);
 		read_lock(&proc_subdir_lock);
-		next = pde_subdir_next(de);
+		/* Removed in the meantime, go on after its name. */
+		if (RB_EMPTY_NODE(&de->subdir_node))
+			next = pde_subdir_after(dir, de->name, de->namelen);
+		else
+			next = pde_subdir_next(de);
 		pde_put(de);
 		de = next;
 	} while (de);
@@ -348,6 +412,7 @@ static const struct file_operations proc_dir_operations = {
 	.llseek			= generic_file_llseek,
 	.read			= generic_read_dir,
 	.iterate_shared		= proc_readdir,
+	.release		= proc_dir_release,
 };
 
 static int proc_net_d_revalidate(struct inode *dir, const struct qstr *name,
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index df873df9afc5..04f7ce2b24ed 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -287,6 +287,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 *);
+int proc_dir_release(struct inode *, struct file *);
 
 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 b1f5eafb069a..dbcf87a257e1 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -351,6 +351,7 @@ const struct file_operations proc_net_operations = {
 	.llseek		= generic_file_llseek,
 	.read		= generic_read_dir,
 	.iterate_shared	= proc_tgid_net_readdir,
+	.release	= proc_dir_release,
 };
 
 static __net_init int proc_net_ns_init(struct net *net)
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 7fbbe92bf73a..dfee1caa26f7 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -441,6 +441,7 @@ static const struct file_operations proc_root_operations = {
 	.read		 = generic_read_dir,
 	.iterate_shared	 = proc_root_readdir,
 	.llseek		= generic_file_llseek,
+	.release	= proc_dir_release,
 };
 
 /*

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 36/51] proc: don't let NOMMU map procfs files
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (22 preceding siblings ...)
  2026-11-04 19:23   ` [PATCH RESEND v2 32/51] proc: resume readdir after the last name it returned Christian Brauner
@ 2026-11-04 19:24   ` Christian Brauner
  2026-11-04 19:25   ` [PATCH RESEND v2 37/51] proc/vmcore: don't let vmcore_cleanup() run twice at once Christian Brauner
                     ` (10 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:24 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

Every procfs file has a ->mmap since commit 786d7e1612f0 ("Fix
rmmod/read/write races in /proc entries"), whether the entry has a
->proc_mmap or not. On NOMMU validate_mmap_request() takes that to mean
the file can be mapped. procfs has no ->mmap_capabilities so a regular
file gets NOMMU_MAP_COPY, and a private mapping never calls ->mmap at
all. Instead do_mmap() takes nommu_region_sem for writing and
do_mmap_private() reads the file into the copy with kernel_read().

Reading /proc/maps takes nommu_region_sem as well:

  mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, open("/proc/maps", O_RDONLY), 0)

  do_mmap()                          // down_write(&nommu_region_sem)
  -> do_mmap_private()
     -> kernel_read()
        -> seq_read_iter()
           -> nommu_region_list_start()   // down_read()

The task hangs for good, holding nommu_region_sem and its own mmap_lock,
and every mmap(), munmap() and exit after that hangs behind it.
/proc/maps is 0444 so any user can do that.

Give the procfs files a ->mmap_capabilities on NOMMU that allows
nothing. That covers every entry and not just /proc/maps, procfs can't
know which entry's read takes nommu_region_sem. Mapping a procfs file
then fails with -ENODEV, like mapping a file without ->mmap did before
786d7e1612f0. NOMMU never called ->proc_mmap() for a regular file
anyway, procfs never offered NOMMU_MAP_DIRECT.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 786d7e1612f0 ("Fix rmmod/read/write races in /proc entries")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/inode.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index b7634f975d98..9933f03ab9fb 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -466,6 +466,18 @@ proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
 	return rv;
 }
 
+#ifndef CONFIG_MMU
+/*
+ * NOMMU never calls ->proc_mmap() for a regular file. It reads the file into
+ * a private copy instead, with nommu_region_sem held for writing, which
+ * reading /proc/maps takes as well. Don't map procfs files at all.
+ */
+static unsigned int proc_reg_mmap_capabilities(struct file *file)
+{
+	return 0;
+}
+#endif
+
 static int proc_reg_open(struct inode *inode, struct file *file)
 {
 	struct proc_dir_entry *pde = PDE(inode);
@@ -559,6 +571,9 @@ static const struct file_operations proc_reg_file_ops = {
 	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };
@@ -572,6 +587,9 @@ static const struct file_operations proc_iter_file_ops = {
 	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };
@@ -586,6 +604,9 @@ static const struct file_operations proc_reg_file_ops_compat = {
 	.compat_ioctl	= proc_reg_compat_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };
@@ -600,6 +621,9 @@ static const struct file_operations proc_iter_file_ops_compat = {
 	.compat_ioctl	= proc_reg_compat_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 37/51] proc/vmcore: don't let vmcore_cleanup() run twice at once
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (23 preceding siblings ...)
  2026-11-04 19:24   ` [PATCH RESEND v2 36/51] proc: don't let NOMMU map procfs files Christian Brauner
@ 2026-11-04 19:25   ` Christian Brauner
  2026-11-04 19:25   ` [PATCH RESEND v2 38/51] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page() Christian Brauner
                     ` (9 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:25 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

vmcore_cleanup() has a single caller, fadump's release_mem_store() on
powerpc. That checks fw_dump.dump_active without a lock and calls
vmcore_cleanup() without one, and kernfs only serializes the writers of
one open file. So two tasks that write to
/sys/kernel/fadump/release_mem through different files run
vmcore_cleanup() at the same time.

The first one removes /proc/vmcore and waits for its readers in
proc_remove(). The second one finds the entry already unlinked, returns
from proc_remove() right away and frees vmcore_list, the ELF headers
and the device dumps while those readers still walk them. Or both of
them free the same lists at once.

Serialize vmcore_cleanup() with a mutex of its own. It can't be
vmcore_mutex, removing /proc/vmcore calls release_vmcore() which takes
it. The second caller then finds nothing left to free.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: b500afff11f6 ("fadump: Invalidate registration and release reserved memory for general use.")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/vmcore.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 1fe70c4ecc54..ccb795b85509 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -1764,6 +1764,11 @@ fs_initcall(vmcore_init);
 /* Cleanup function for vmcore module. */
 void vmcore_cleanup(void)
 {
+	/* Not vmcore_mutex, removing /proc/vmcore calls release_vmcore(). */
+	static DEFINE_MUTEX(vmcore_cleanup_mutex);
+
+	/* Two writers of fadump's release_mem can get here at once. */
+	guard(mutex)(&vmcore_cleanup_mutex);
 	if (proc_vmcore) {
 		proc_remove(proc_vmcore);
 		proc_vmcore = NULL;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 38/51] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page()
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (24 preceding siblings ...)
  2026-11-04 19:25   ` [PATCH RESEND v2 37/51] proc/vmcore: don't let vmcore_cleanup() run twice at once Christian Brauner
@ 2026-11-04 19:25   ` Christian Brauner
  2026-11-04 19:25   ` [PATCH RESEND v2 39/51] powerpc/crash_dump: recover from a machine check " Christian Brauner
                     ` (8 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:25 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

copy_oldmem_page() maps a page of the old kernel that isn't in the
linear mapping with ioremap_cache() and copies from it without checking
whether the mapping worked. If it didn't, the copy reads from an offset
into the NULL page. That oopses unless the copy happens to be a user
copy, whose exception table entries turn the fault into a short copy.

Return -ENOMEM like the other architectures do. read_from_oldmem()
turns that into -EFAULT like any other short copy.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 54c32021eb6f ("[PATCH] powerpc: Add arch-dependent copy_oldmem_page")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/crash_dump.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c
index 103b6605dd68..1543917bd3e1 100644
--- a/arch/powerpc/kernel/crash_dump.c
+++ b/arch/powerpc/kernel/crash_dump.c
@@ -86,6 +86,8 @@ ssize_t copy_oldmem_page(struct iov_iter *iter, unsigned long pfn,
 		csize = copy_to_iter(vaddr + offset, csize, iter);
 	} else {
 		vaddr = ioremap_cache(paddr, PAGE_SIZE);
+		if (!vaddr)
+			return -ENOMEM;
 		csize = copy_to_iter(vaddr + offset, csize, iter);
 		iounmap(vaddr);
 	}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 39/51] powerpc/crash_dump: recover from a machine check in copy_oldmem_page()
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (25 preceding siblings ...)
  2026-11-04 19:25   ` [PATCH RESEND v2 38/51] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page() Christian Brauner
@ 2026-11-04 19:25   ` Christian Brauner
  2026-11-04 19:25   ` [PATCH RESEND v2 40/51] sunrpc: make queue_wait a global wait queue again Christian Brauner
                     ` (7 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:25 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

A capture kernel reads the memory of the crashed kernel, and that memory
may well have uncorrectable errors. On powerpc a machine check in the
kernel is only recovered from if the instruction that took it has an
exception table entry (cf. mce_common_process_ue()). copy_oldmem_page()
copies with copy_to_iter(). For a read() of /proc/vmcore that is a user
copy, all of whose loads have exception table entries, so the machine
check turns into a short copy and read_from_oldmem() returns -EFAULT.

But since commit 4a22fd20379c ("vmcore: convert __read_vmcore to use an
iov_iter") /proc/vmcore can be spliced too, and splice(), sendfile()
and copy_file_range() copy into kernel pages with memcpy(), which has
no exception table entries. A machine check there panics the capture
kernel. The same goes for the reads of the ELF headers and notes that
vmcore does itself, and those have been memcpy() for as long as
copy_oldmem_page() has had its kernel buffer branch, since commit
54c32021eb6f ("[PATCH] powerpc: Add arch-dependent copy_oldmem_page").

Copy with _copy_mc_to_iter() instead. PPC64 selects ARCH_HAS_COPY_MC
and there it uses copy_mc_generic() for kernel and user destinations
alike, which has the exception table entries. Everywhere else it is
_copy_to_iter().

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 54c32021eb6f ("[PATCH] powerpc: Add arch-dependent copy_oldmem_page")
Fixes: 4a22fd20379c ("vmcore: convert __read_vmcore to use an iov_iter")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/crash_dump.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c
index 1543917bd3e1..ca7e019ae3e9 100644
--- a/arch/powerpc/kernel/crash_dump.c
+++ b/arch/powerpc/kernel/crash_dump.c
@@ -83,12 +83,12 @@ ssize_t copy_oldmem_page(struct iov_iter *iter, unsigned long pfn,
 
 	if (memblock_is_region_memory(paddr, csize)) {
 		vaddr = __va(paddr);
-		csize = copy_to_iter(vaddr + offset, csize, iter);
+		csize = _copy_mc_to_iter(vaddr + offset, csize, iter);
 	} else {
 		vaddr = ioremap_cache(paddr, PAGE_SIZE);
 		if (!vaddr)
 			return -ENOMEM;
-		csize = copy_to_iter(vaddr + offset, csize, iter);
+		csize = _copy_mc_to_iter(vaddr + offset, csize, iter);
 		iounmap(vaddr);
 	}
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 40/51] sunrpc: make queue_wait a global wait queue again
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (26 preceding siblings ...)
  2026-11-04 19:25   ` [PATCH RESEND v2 39/51] powerpc/crash_dump: recover from a machine check " Christian Brauner
@ 2026-11-04 19:25   ` Christian Brauner
  2026-11-04 19:26   ` [PATCH RESEND v2 41/51] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls Christian Brauner
                     ` (6 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:25 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

Since commit 552d0e17ea04 ("sunrpc: convert queue_wait from global to
per-cache-detail waitqueue") poll() and epoll queue their waiters for a
channel file on the queue_wait head in struct cache_detail. But the
cache_detail can be freed while its channel file is still open and
waited on.

The procfs channel files of a network namespace are removed when the
namespace goes away. Removing a procfs file waits for the callers that
are in one of its methods right now and then calls ->release on every
file that is still open (cf. close_pdeo()). The waiters that poll() and
epoll left on the head stay queued until their file is really closed.
So:

  sunrpc_exit_net()
  -> ip_map_cache_destroy()
     -> cache_unregister_net()
        -> remove_cache_proc_entries()
           -> proc_remove()      // ->release, the waiters stay queued
     -> cache_destroy_net()      // kfree(cd)

and when poll() returns remove_wait_queue() writes to the freed
cache_detail:

  BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x4b/0xa0
  Read of size 1 at addr ff110001131bb570 by task sunrpc-poll-uaf/242
  Call Trace:
   remove_wait_queue+0x22/0x1c0
   poll_freewait+0xc2/0x230
   do_sys_poll+0x9ba/0xd50
   __x64_sys_poll+0x165/0x470
  Freed by task 12:
   kfree+0x267/0x620
   sunrpc_exit_net+0x34/0x80
   ops_undo_list+0x271/0x8d0
   cleanup_net+0x3f3/0x860

Closing an epoll instance that watches the file gets there through
ep_unregister_pollwait() instead.

The channel files of a network namespace belong to the root of its
user namespace and cache_open() doesn't check for a capability. So an
unprivileged user can open the channel file of a namespace of its own
from the outside, poll it and let the namespace die.

Go back to a single static wait queue that never goes away. A wakeup
for one cache wakes the waiters of all caches again but cache_poll()
reports per cache_detail so that's just a spurious wakeup. procfs
should really own the wait queue of its files like kernfs does but
that's nothing for stable.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 552d0e17ea04 ("sunrpc: convert queue_wait from global to per-cache-detail waitqueue")
Acked-by: Chuck Lever <cel@kernel.org>
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 include/linux/sunrpc/cache.h |  1 -
 net/sunrpc/cache.c           | 12 +++++++++---
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 2735c332ddb7..bf4fc543e1e5 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -119,7 +119,6 @@ struct cache_detail {
 	struct list_head	requests;
 	struct list_head	readers;
 	spinlock_t		queue_lock;
-	wait_queue_head_t	queue_wait;
 	u64			next_seqno;
 
 	atomic_t		writers;		/* how many time is /channel open */
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 1bc04109d213..1ceaea7e01b9 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -404,7 +404,6 @@ void sunrpc_init_cache_detail(struct cache_detail *cd)
 	INIT_LIST_HEAD(&cd->requests);
 	INIT_LIST_HEAD(&cd->readers);
 	spin_lock_init(&cd->queue_lock);
-	init_waitqueue_head(&cd->queue_wait);
 	cd->next_seqno = 1;
 	spin_lock(&cache_list_lock);
 	cd->nextcheck = 0;
@@ -965,13 +964,20 @@ out:
 	return ret;
 }
 
+/*
+ * Not in struct cache_detail: poll and epoll waiters stay queued until
+ * their file is closed, but a channel file in procfs can stay open after
+ * the cache_detail of its dying network namespace has been freed.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(queue_wait);
+
 static __poll_t cache_poll(struct file *filp, poll_table *wait,
 			       struct cache_detail *cd)
 {
 	__poll_t mask;
 	struct cache_reader *rp = filp->private_data;
 
-	poll_wait(filp, &cd->queue_wait, wait);
+	poll_wait(filp, &queue_wait, wait);
 
 	/* alway allow write */
 	mask = EPOLLOUT | EPOLLWRNORM;
@@ -1236,7 +1242,7 @@ static int cache_do_upcall(struct cache_detail *detail, struct cache_head *h)
 	spin_unlock(&detail->queue_lock);
 	if (ret != -EAGAIN && detail->cache_notify)
 		detail->cache_notify(detail, h);
-	wake_up(&detail->queue_wait);
+	wake_up(&queue_wait);
 	if (ret == -EAGAIN) {
 		kfree(buf);
 		kfree(crq);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 41/51] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (27 preceding siblings ...)
  2026-11-04 19:25   ` [PATCH RESEND v2 40/51] sunrpc: make queue_wait a global wait queue again Christian Brauner
@ 2026-11-04 19:26   ` Christian Brauner
  2026-11-04 19:26   ` [PATCH RESEND v2 42/51] powerpc/pseries: bound the lengths in the ofdt property parser Christian Brauner
                     ` (5 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:26 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

esas2r_ioctl_handler() copies in a struct atto_express_ioctl, a bit over
512k, and hands the union member for the command to the handlers. For
EXPRESS_IOCTL_HBA and EXPRESS_IOCTL_SMP the length of the buffered ioctl
is the size of the member plus lengths that come straight from
userspace:

  bi.length = sizeof(struct atto_ioctl) + ioctl_hba->data_length;

  bi.length = sizeof(struct atto_ioctl_smp)
	    + le32_to_cpu(si->req_length)
	    + le32_to_cpu(si->rsp_length);

handle_buffered_ioctl() copies bi.length bytes from the member into a
DMA buffer and, once the adapter is done, copies the DMA buffer back
over the member. Nothing checks that the lengths fit into the struct
that was copied in. So a large data_length makes it read past the
struct and then write the DMA buffer back over the memory behind it.

The firmware commands pass sizeof(struct atto_express_ioctl) to
esas2r_{read,write}_{fw,fs}() as the length of data.fwrw.image, which
starts 28 bytes into the struct. So they can read and write 28 bytes
past it as well.

The handler is reachable through /proc/scsi/esas2r/ATTONode. It is
created with mode 0, which procfs turns into 0444, and esas2r doesn't
check for a capability anywhere. So any local user can do this on a
machine with such an adapter.

Bound all of these by the room from the member to the end of the
struct. read_hw() passes handle_hba_ioctl() a single struct atto_ioctl
and only ever returns that, so there the data length has to be zero.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 26780d9e12ed ("[SCSI] esas2r: ATTO Technology ExpressSAS 6G SAS/SATA RAID Adapter Driver")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/esas2r/esas2r.h       |  2 +-
 drivers/scsi/esas2r/esas2r_ioctl.c | 41 ++++++++++++++++++++++++--------------
 drivers/scsi/esas2r/esas2r_main.c  |  3 ++-
 3 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index a763edd05fe4..10101f50b282 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -967,7 +967,7 @@ int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
 int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg);
 int esas2r_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg);
 u8 handle_hba_ioctl(struct esas2r_adapter *a,
-		    struct atto_ioctl *ioctl_hba);
+		    struct atto_ioctl *ioctl_hba, size_t room);
 enum scsi_qc_status esas2r_queuecommand(struct Scsi_Host *host,
 					struct scsi_cmnd *cmd);
 int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh);
diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c
index c93d9265e100..80ae5b9d45db 100644
--- a/drivers/scsi/esas2r/esas2r_ioctl.c
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -311,17 +311,27 @@ static int smp_ioctl_callback(struct esas2r_adapter *a,
 	return true;
 }
 
+/* The room from @member to the end of a struct atto_express_ioctl. */
+#define EXPRESS_IOCTL_ROOM(member)				\
+	(sizeof(struct atto_express_ioctl) -			\
+	 offsetof(struct atto_express_ioctl, member))
+
 static u8 handle_smp_ioctl(struct esas2r_adapter *a, struct atto_ioctl_smp *si)
 {
 	struct esas2r_buffered_ioctl bi;
+	u64 length = (u64)sizeof(struct atto_ioctl_smp) +
+		     le32_to_cpu(si->req_length) +
+		     le32_to_cpu(si->rsp_length);
+
+	/* The request and the response follow @si in the same buffer. */
+	if (length > EXPRESS_IOCTL_ROOM(data.ioctl_smp))
+		return IOCTL_INVALID_PARAM;
 
 	memset(&bi, 0, sizeof(bi));
 
 	bi.a = a;
 	bi.ioctl = si;
-	bi.length = sizeof(struct atto_ioctl_smp)
-		    + le32_to_cpu(si->req_length)
-		    + le32_to_cpu(si->rsp_length);
+	bi.length = length;
 	bi.offset = 0;
 	bi.callback = smp_ioctl_callback;
 	return handle_buffered_ioctl(&bi);
@@ -1221,11 +1231,16 @@ static void hba_ioctl_done_callback(struct esas2r_adapter *a,
 	}
 }
 
+/* @room is the size of the buffer that starts with @ioctl_hba. */
 u8 handle_hba_ioctl(struct esas2r_adapter *a,
-		    struct atto_ioctl *ioctl_hba)
+		    struct atto_ioctl *ioctl_hba, size_t room)
 {
 	struct esas2r_buffered_ioctl bi;
 
+	/* The data follows @ioctl_hba in the same buffer. */
+	if (ioctl_hba->data_length > room - sizeof(struct atto_ioctl))
+		return IOCTL_INVALID_PARAM;
+
 	memset(&bi, 0, sizeof(bi));
 
 	bi.a = a;
@@ -1349,31 +1364,27 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
 			err = esas2r_write_fw(a,
 					      (char *)ioctl->data.fwrw.image,
 					      0,
-					      sizeof(struct
-						     atto_express_ioctl));
+					      EXPRESS_IOCTL_ROOM(data.fwrw.image));
 
 			if (err >= 0) {
 				err = esas2r_read_fw(a,
 						     (char *)ioctl->data.fwrw.
 						     image,
 						     0,
-						     sizeof(struct
-							    atto_express_ioctl));
+						     EXPRESS_IOCTL_ROOM(data.fwrw.image));
 			}
 		} else if (ioctl->data.fwrw.img_type == FW_IMG_FS_API) {
 			err = esas2r_write_fs(a,
 					      (char *)ioctl->data.fwrw.image,
 					      0,
-					      sizeof(struct
-						     atto_express_ioctl));
+					      EXPRESS_IOCTL_ROOM(data.fwrw.image));
 
 			if (err >= 0) {
 				err = esas2r_read_fs(a,
 						     (char *)ioctl->data.fwrw.
 						     image,
 						     0,
-						     sizeof(struct
-							    atto_express_ioctl));
+						     EXPRESS_IOCTL_ROOM(data.fwrw.image));
 			}
 		} else {
 			ioctl->header.return_code = IOCTL_BAD_FLASH_IMGTYPE;
@@ -1441,9 +1452,9 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
 		break;
 
 	case EXPRESS_IOCTL_HBA:
-		ioctl->header.return_code = handle_hba_ioctl(a,
-							     &ioctl->data.
-							     ioctl_hba);
+		ioctl->header.return_code =
+			handle_hba_ioctl(a, &ioctl->data.ioctl_hba,
+					 EXPRESS_IOCTL_ROOM(data.ioctl_hba));
 		break;
 
 	case EXPRESS_IOCTL_VDA:
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index ada278c24c51..75a70ae9441b 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -178,7 +178,8 @@ static ssize_t read_hw(struct file *file, struct kobject *kobj,
 	if (!a->local_atto_ioctl)
 		return -ENOMEM;
 
-	if (handle_hba_ioctl(a, a->local_atto_ioctl) != IOCTL_SUCCESS)
+	if (handle_hba_ioctl(a, a->local_atto_ioctl,
+			     sizeof(struct atto_ioctl)) != IOCTL_SUCCESS)
 		return -ENOMEM;
 
 	memcpy(buf, a->local_atto_ioctl, length);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 42/51] powerpc/pseries: bound the lengths in the ofdt property parser
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (28 preceding siblings ...)
  2026-11-04 19:26   ` [PATCH RESEND v2 41/51] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls Christian Brauner
@ 2026-11-04 19:26   ` Christian Brauner
  2026-11-04 19:26   ` [PATCH RESEND v2 43/51] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long Christian Brauner
                     ` (4 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:26 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

The commands for /proc/powerpc/ofdt that carry properties, "add_node",
"add_property" and "update_property", can make the parser read past the
buffer they were copied into:

(1) do_update_property() computes the end of the buffer after
    parse_node() moved buf past the phandle. So the end is off by the
    length of the phandle string, which can be padded with zeroes, and
    parse_next_property() accepts a value that reaches that far past
    the command. new_property() then copies those bytes into the
    property, which everyone can read in /proc/device-tree.

(2) parse_next_property() parses the length with simple_strtoul() into
    an int and only rejects -1. A length above INT_MAX turns negative,
    tmp += *length moves backwards and passes the check against the
    end, so *tmp is read up to 2G in front of the buffer.

(3) Updating "slb-size" or "ibm,slb-size" reads an int from the value
    whatever its length.

Compute the end before parsing the phandle, reject lengths that don't
fit into an int or into what is left of the buffer, and only take the
slb size from a value that holds one. The file is root-only and
refused under lockdown but the parser still shouldn't read past its
input.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: 610d91511f99 ("[PATCH] powerpc: Add support for changing properties from userspace")
Fixes: 46db2f86a3b2 ("powerpc/pseries: Fix to handle slb resize across migration")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/platforms/pseries/reconfig.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index 5a0c9c0b6253..b543afbc1efb 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -114,6 +114,7 @@ static void release_prop_list(const struct property *prop)
 static char * parse_next_property(char *buf, char *end, char **name, int *length,
 				  unsigned char **value)
 {
+	unsigned long len;
 	char *tmp;
 
 	*name = buf;
@@ -133,13 +134,13 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
 	}
 
 	/* now we're on the length */
-	*length = -1;
-	*length = simple_strtoul(tmp, &tmp, 10);
-	if (*length == -1) {
+	len = simple_strtoul(tmp, &tmp, 10);
+	if (len > INT_MAX) {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
 		return NULL;
 	}
+	*length = len;
 	if (*tmp != ' ' || ++tmp >= end) {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
@@ -148,13 +149,13 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
 
 	/* now we're on the value */
 	*value = tmp;
-	tmp += *length;
-	if (tmp > end) {
+	if (len > end - tmp) {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
 		return NULL;
 	}
-	else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
+	tmp += len;
+	if (tmp < end && *tmp != ' ' && *tmp != '\0') {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
 		return NULL;
@@ -320,8 +321,8 @@ static int do_update_property(char *buf, size_t bufsize)
 	char *name, *end, *next_prop;
 	int length;
 	struct property *newprop;
-	buf = parse_node(buf, bufsize, &np);
 	end = buf + bufsize;
+	buf = parse_node(buf, bufsize, &np);
 
 	if (!np)
 		return -ENODEV;
@@ -337,7 +338,8 @@ static int do_update_property(char *buf, size_t bufsize)
 	if (!newprop)
 		return -ENOMEM;
 
-	if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
+	if ((!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size")) &&
+	    length >= (int)sizeof(int))
 		slb_set_size(*(int *)value);
 
 	return of_update_property(np, newprop);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 43/51] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (29 preceding siblings ...)
  2026-11-04 19:26   ` [PATCH RESEND v2 42/51] powerpc/pseries: bound the lengths in the ofdt property parser Christian Brauner
@ 2026-11-04 19:26   ` Christian Brauner
  2026-11-04 19:26   ` [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned Christian Brauner
                     ` (3 subsequent siblings)
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:26 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

h_get_ppp() returns the status of the H_GET_PPP hcall as an unsigned
int. update_ppp() hands it on as its ssize_t result, and
lparcfg_write() turns the hcall statuses it knows into an errno and
returns anything else as it is. So when H_GET_PPP fails, say with
H_PARAMETER (-4), the status comes back as 4294967292 and a write to
/proc/powerpc/lparcfg reports that many bytes written.

Return the status as the long it is. lparcfg_write() then treats it
like the status of H_SET_PPP: an unknown error status is at least
negative, and one of the positive long-busy statuses comes back as a
byte count, as it does for H_SET_PPP already. Before commit
11529396ea31 ("powerpc/pseries: Split processor entitlement retrieval
and gathering to helper routines") a failed H_GET_PPP turned into -EIO.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 11529396ea31 ("powerpc/pseries: Split processor entitlement retrieval and gathering to helper routines")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/platforms/pseries/lparcfg.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c
index 9882f98c0efb..4f6b2975c6e9 100644
--- a/arch/powerpc/platforms/pseries/lparcfg.c
+++ b/arch/powerpc/platforms/pseries/lparcfg.c
@@ -113,7 +113,7 @@ struct hvcall_ppp_data {
  *	          XXXXXX - Entitled procs capacity % available to the
  *			   partitions pool.
  */
-static unsigned int h_get_ppp(struct hvcall_ppp_data *ppp_data)
+static long h_get_ppp(struct hvcall_ppp_data *ppp_data)
 {
 	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
 	long rc;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (30 preceding siblings ...)
  2026-11-04 19:26   ` [PATCH RESEND v2 43/51] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long Christian Brauner
@ 2026-11-04 19:26   ` Christian Brauner
  2026-11-05  0:08     ` Jann Horn
  2026-11-05 16:26     ` Jann Horn
  2026-11-04 19:27   ` [PATCH RESEND v2 46/51] proc: sysctl: copy to and from userspace without holding the table Christian Brauner
                     ` (2 subsequent siblings)
  34 siblings, 2 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:26 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_sys_readdir() counts the entries of a directory and resumes a
listing at the number getdents64() stopped at. The entries are sorted
by name, so one that is registered or unregistered in between shifts
everything behind it by one. Listing /proc/sys/net/ipv4/conf with a
buffer that holds ".", ".." and "all" and registering a device whose
name sorts before "all" makes the next call return "all" a second time.
Unregistering one that sorted before the position makes it skip an
entry instead. So a listing that takes more than one call shows a name
twice, or not at all. Even the 64k buffer of ls needs a second call to
find the end of the directory, so a device that comes up right then
repeats the last entry.

So remember the name of the last entry a call emitted in the file and
resume at the first usable entry after it, like kernfs resumes after
the node it stopped at. The entry itself can't be pinned across calls,
unregistering waits for that. If the position was moved by a seek in
between the listing counts from the start like before, seeking to an
arbitrary position has never been better than that.

That also takes one walk down the tree to resume instead of a walk over
every entry in front of the position with a round trip through
sysctl_lock for each of them.

Fixes: 77b14db502cb ("[PATCH] sysctl: reimplement the sysctl proc support")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 103 insertions(+), 3 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index b4a309d49698..ba52f13eb8f3 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -417,6 +417,54 @@ static void next_entry(struct ctl_table_header **phead, const struct ctl_table *
 	*pentry = entry;
 }
 
+/*
+ * The first usable entry of @dir after @name, or the first one at all for a
+ * NULL @name. The entries are sorted by name, so that is where a listing
+ * resumes when entries in front of it came or went in between. -ENOENT if
+ * the directory is being unregistered.
+ */
+static int entry_after(struct ctl_dir *dir, const char *name, int namelen,
+		       struct ctl_table_header **phead,
+		       const struct ctl_table **pentry)
+{
+	struct ctl_table_header *head = NULL;
+	const struct ctl_table *entry = NULL;
+	struct rb_node *node, *after = NULL;
+	struct ctl_node *ctl_node;
+
+	spin_lock(&sysctl_lock);
+	if (unlikely(dir->header.unregistering)) {
+		spin_unlock(&sysctl_lock);
+		return -ENOENT;
+	}
+	if (!name) {
+		after = rb_first(&dir->root);
+	} else for (node = dir->root.rb_node; node;) {
+		const struct ctl_table *table;
+		struct ctl_table_header *h;
+
+		ctl_node = rb_entry(node, struct ctl_node, node);
+		h = ctl_node->header;
+		table = &h->ctl_table[ctl_node - h->node];
+		if (namecmp(table->procname, strlen(table->procname),
+			    name, namelen) > 0) {
+			after = node;
+			node = node->rb_left;
+		} else {
+			node = node->rb_right;
+		}
+	}
+	ctl_node = first_usable_entry(after);
+	spin_unlock(&sysctl_lock);
+	if (ctl_node) {
+		head = ctl_node->header;
+		entry = &head->ctl_table[ctl_node - head->node];
+	}
+	*phead = head;
+	*pentry = entry;
+	return 0;
+}
+
 /*
  * sysctl_perm does NOT grant the superuser all rights automatically, because
  * some sysctl variables are readonly even to root.
@@ -675,6 +723,36 @@ out:
 	return ret;
 }
 
+/*
+ * Where the next readdir resumes. The entries are counted, so one that is
+ * registered or unregistered in front of the position would shift what
+ * the next call returns. The name of the last entry emitted doesn't move.
+ */
+struct sysctl_cursor {
+	loff_t pos;			/* the position behind the entry */
+	int len;
+	char name[NAME_MAX + 1];
+};
+
+/* If this can't remember the entry the next call counts from the start. */
+static void sysctl_cursor_set(struct file *file, const char *name, int len,
+			      loff_t pos)
+{
+	struct sysctl_cursor *cursor = file->private_data;
+
+	if (len > NAME_MAX)
+		return;
+	if (!cursor) {
+		cursor = kmalloc(sizeof(*cursor), GFP_KERNEL);
+		if (!cursor)
+			return;
+		file->private_data = cursor;
+	}
+	cursor->pos = pos;
+	cursor->len = len;
+	memcpy(cursor->name, name, len);
+}
+
 static bool proc_sys_fill_cache(struct file *file,
 				struct dir_context *ctx,
 				struct ctl_table_header *head,
@@ -756,8 +834,11 @@ static int scan(struct ctl_table_header *head, const struct ctl_table *table,
 	else
 		res = proc_sys_fill_cache(file, ctx, head, table);
 
-	if (res)
+	if (res) {
 		ctx->pos = *pos;
+		sysctl_cursor_set(file, table->procname, strlen(table->procname),
+				  ctx->pos);
+	}
 
 	return res;
 }
@@ -765,6 +846,7 @@ static int scan(struct ctl_table_header *head, const struct ctl_table *table,
 static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 {
 	struct ctl_table_header *head = grab_header(file_inode(file));
+	struct sysctl_cursor *cursor = file->private_data;
 	struct ctl_table_header *h = NULL;
 	const struct ctl_table *entry;
 	struct ctl_dir *ctl_dir;
@@ -778,9 +860,20 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 	if (!dir_emit_dots(file, ctx))
 		goto out;
 
-	pos = 2;
+	/*
+	 * Resume after the last entry the previous call emitted. Only a seek
+	 * that moved the position away from it counts from the start, like
+	 * seeking to an arbitrary position always did.
+	 */
+	if (cursor && cursor->pos == ctx->pos) {
+		entry_after(ctl_dir, cursor->name, cursor->len, &h, &entry);
+		pos = ctx->pos;
+	} else {
+		first_entry(ctl_dir, &h, &entry);
+		pos = 2;
+	}
 
-	for (first_entry(ctl_dir, &h, &entry); h; next_entry(&h, &entry)) {
+	for (; h; next_entry(&h, &entry)) {
 		if (!scan(h, entry, &pos, file, ctx)) {
 			sysctl_head_finish(h);
 			break;
@@ -791,6 +884,12 @@ out:
 	return 0;
 }
 
+static int proc_sys_dir_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+}
+
 static int proc_sys_permission(const struct mnt_idmap *idmap,
 			       struct inode *inode, int mask)
 {
@@ -870,6 +969,7 @@ static const struct file_operations proc_sys_dir_file_operations = {
 	.read		= generic_read_dir,
 	.iterate_shared	= proc_sys_readdir,
 	.llseek		= generic_file_llseek,
+	.release	= proc_sys_dir_release,
 };
 
 static const struct inode_operations proc_sys_inode_operations = {

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 46/51] proc: sysctl: copy to and from userspace without holding the table
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (31 preceding siblings ...)
  2026-11-04 19:26   ` [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned Christian Brauner
@ 2026-11-04 19:27   ` Christian Brauner
  2026-11-04 19:27   ` [PATCH RESEND v2 48/51] proc: sysctl: don't hold the tables while readdir copies to userspace Christian Brauner
  2026-11-04 19:28   ` [PATCH RESEND v2 50/51] proc: don't pin a symlink's entry while readlink() copies it out Christian Brauner
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:27 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_sys_call_handler() grabs the table, copies in what is written,
calls the handler and copies what it produced out to userspace before
it lets go of the table again. Unregistering a table waits until nobody
uses it anymore. So a reader or writer whose copy faults for as long as
it wants, with userfaultfd or a stalled FUSE mapping, keeps
unregister_sysctl_table() waiting, and its callers like to hold locks
while they do that. addrconf unregisters the ipv6 sysctls of a device
with RTNL held, so a stalled read of /proc/sys/net/ipv6/conf/<dev>/mtu
holds up every RTNL user until the reader gives up.

Copy in before grabbing the table and copy out after letting go of it,
like procfs and kernfs do for their files. The buffer is ours, nothing
needs the table for the copies.

That moves the allocation and the copy in before the permission check.
A write that can't be copied in, or a request that is too large, now
fails with -EFAULT or -ENOMEM where it used to fail with -EPERM.

Fixes: 330d57fb98a9 ("[PATCH] Fix sysctl unregistration oops (CVE-2005-2709)")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 50 ++++++++++++++++++++++++++++----------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index ba52f13eb8f3..f5c9f04e8509 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -602,14 +602,36 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
 		int write)
 {
 	struct inode *inode = file_inode(iocb->ki_filp);
-	struct ctl_table_header *head = grab_header(inode);
 	const struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+	struct ctl_table_header *head;
 	size_t count = iov_iter_count(iter);
 	char *kbuf;
 	ssize_t error;
 
-	if (IS_ERR(head))
-		return PTR_ERR(head);
+	/* don't even try if the size is too large */
+	if (count >= KMALLOC_MAX_SIZE)
+		return -ENOMEM;
+	kbuf = kvzalloc(count + 1, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	/*
+	 * Copy in before grabbing the table and copy out after letting go of
+	 * it. Unregistering the table waits for us and a copy that faults can
+	 * take as long as userspace wants.
+	 */
+	if (write) {
+		error = -EFAULT;
+		if (!copy_from_iter_full(kbuf, count, iter))
+			goto out_free_buf;
+		kbuf[count] = '\0';
+	}
+
+	head = grab_header(inode);
+	if (IS_ERR(head)) {
+		error = PTR_ERR(head);
+		goto out_free_buf;
+	}
 
 	/*
 	 * At this point we know that the sysctl was not unregistered
@@ -624,28 +646,15 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
 	if (!table->proc_handler)
 		goto out;
 
-	/* don't even try if the size is too large */
-	error = -ENOMEM;
-	if (count >= KMALLOC_MAX_SIZE)
-		goto out;
-	kbuf = kvzalloc(count + 1, GFP_KERNEL);
-	if (!kbuf)
-		goto out;
-
-	if (write) {
-		error = -EFAULT;
-		if (!copy_from_iter_full(kbuf, count, iter))
-			goto out_free_buf;
-		kbuf[count] = '\0';
-	}
-
 	error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, &kbuf, &count,
 					   &iocb->ki_pos);
 	if (error)
-		goto out_free_buf;
+		goto out;
 
 	/* careful: calling conventions are nasty here */
 	error = table->proc_handler(table, write, kbuf, &count, &iocb->ki_pos);
+out:
+	sysctl_head_finish(head);
 	if (error)
 		goto out_free_buf;
 
@@ -658,9 +667,6 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
 	error = count;
 out_free_buf:
 	kvfree(kbuf);
-out:
-	sysctl_head_finish(head);
-
 	return error;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 48/51] proc: sysctl: don't hold the tables while readdir copies to userspace
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (32 preceding siblings ...)
  2026-11-04 19:27   ` [PATCH RESEND v2 46/51] proc: sysctl: copy to and from userspace without holding the table Christian Brauner
@ 2026-11-04 19:27   ` Christian Brauner
  2026-11-04 19:28   ` [PATCH RESEND v2 50/51] proc: don't pin a symlink's entry while readlink() copies it out Christian Brauner
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:27 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_sys_readdir() holds the directory's table for the whole readdir
and the table of each entry while dir_emit() copies its name to
userspace. Unregistering a table waits until nobody uses it anymore. So
a getdents64() whose buffer faults for as long as it wants keeps
unregister_sysctl_table() waiting, just like a stalled read() does.
Listing /proc/sys/net/ipv6/conf/<dev>/ that way holds up the removal of
the device under RTNL.

So don't take any table into use for a listing. A header counts what
refers to its memory, the inodes made for it, and unregistering doesn't
wait for those. readdir takes one of those references on the entry it is
at instead, which keeps the header around and nothing else. Under
sysctl_lock, where unregistering erases an entry, it finds the entry,
copies its name, follows a link to the entry it stands for and pins
that. Then it looks the dentry up, creates the inode if there is none,
which checks for itself under sysctl_lock that the table is still
registered, and emits the entry with nothing held at all. An entry whose
table is being unregistered by then is still skipped. The next entry is
the first live one after that name, which is where a listing resumes
between calls since "proc: sysctl: resume readdir after the last name it
returned" anyway. kernfs_fop_readdir() and proc_readdir_de() work the
same way, a reference on the entry and the lock only to find the next
one.

While the entry is still registered its node is still in the tree and
the next one is right behind it, like kernfs_dir_next_pos() continues
from the node it holds. Only once it went away, and to resume, is the
next entry found by name. That's one trip through sysctl_lock per entry
like before, and the link entries of /proc/sys/net no longer take two
more. The directory isn't taken into use either, an entry keeps its
directory registered while it is used and readdir looks at the directory
under the lock. So the dots don't wait for anything, a directory that
went away lists them and then fails with -ENOENT. A seek to an arbitrary
position counts the entries from the start like before, one trip per
entry.

While at it, return errors instead of ending the listing as if the
directory was done.

Fixes: 77b14db502cb ("[PATCH] sysctl: reimplement the sysctl proc support")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 311 +++++++++++++++++++++++++++-----------------------
 1 file changed, 166 insertions(+), 145 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 82024f1ea756..6a92c33db9b5 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -91,6 +91,8 @@ static void drop_sysctl_table(struct ctl_table_header *header);
 static int sysctl_follow_link(struct ctl_table_header **phead,
 	const struct ctl_table **pentry);
 static int insert_links(struct ctl_table_header *head);
+static struct ctl_table_set *lookup_header_set(struct ctl_table_root *root);
+static struct ctl_dir *xlate_dir(struct ctl_table_set *set, struct ctl_dir *dir);
 static void put_links(struct ctl_table_header *header);
 
 static void sysctl_print_dir(struct ctl_dir *dir)
@@ -367,86 +369,96 @@ static const struct ctl_table *lookup_entry(struct ctl_table_header **phead,
 	return entry;
 }
 
-static struct ctl_node *first_usable_entry(struct rb_node *node)
-{
-	struct ctl_node *ctl_node;
+/*
+ * What readdir takes along for an entry between the trips through
+ * sysctl_lock: the header pinned, which keeps its memory and nothing else,
+ * the entry and its name. For a link the header and entry are the
+ * ones it stands for in the caller's set.
+ */
+struct sysctl_dirent {
+	struct ctl_table_header *head;
+	const struct ctl_table *table;
+	struct ctl_node *node;		/* the entry's node in the directory, NULL for a link */
+	ino_t ino;
+	unsigned int type;
+	bool skip;
+	int len;
+	char name[NAME_MAX + 1];
+};
 
-	for (;node; node = rb_next(node)) {
-		ctl_node = rb_entry(node, struct ctl_node, node);
-		if (use_table(ctl_node->header))
-			return ctl_node;
-	}
-	return NULL;
+/* A reference on the memory of @head, like an inode made for it holds. */
+static void sysctl_pin(struct ctl_table_header *head)
+{
+	lockdep_assert_held(&sysctl_lock);
+	head->count++;
 }
 
-static void first_entry(struct ctl_dir *dir,
-	struct ctl_table_header **phead, const struct ctl_table **pentry)
+static void sysctl_unpin(struct ctl_table_header *head)
 {
-	struct ctl_table_header *head = NULL;
-	const struct ctl_table *entry = NULL;
-	struct ctl_node *ctl_node;
-
-	spin_lock(&sysctl_lock);
-	ctl_node = first_usable_entry(rb_first(&dir->root));
-	spin_unlock(&sysctl_lock);
-	if (ctl_node) {
-		head = ctl_node->header;
-		entry = &head->ctl_table[ctl_node - head->node];
-	}
-	*phead = head;
-	*pentry = entry;
+	lockdep_assert_held(&sysctl_lock);
+	if (!--head->count)
+		kfree_rcu(head, rcu);
 }
 
-static void next_entry(struct ctl_table_header **phead, const struct ctl_table **pentry)
+/* the first entry from @node on whose table isn't being unregistered */
+static struct ctl_node *first_live_entry(struct rb_node *node)
 {
-	struct ctl_table_header *head = *phead;
-	const struct ctl_table *entry = *pentry;
-	struct ctl_node *ctl_node = &head->node[entry - head->ctl_table];
-
-	spin_lock(&sysctl_lock);
-	unuse_table(head);
+	struct ctl_node *ctl_node;
 
-	ctl_node = first_usable_entry(rb_next(&ctl_node->node));
-	spin_unlock(&sysctl_lock);
-	head = NULL;
-	if (ctl_node) {
-		head = ctl_node->header;
-		entry = &head->ctl_table[ctl_node - head->node];
+	lockdep_assert_held(&sysctl_lock);
+	for (; node; node = rb_next(node)) {
+		ctl_node = rb_entry(node, struct ctl_node, node);
+		if (!ctl_node->header->unregistering)
+			return ctl_node;
 	}
-	*phead = head;
-	*pentry = entry;
+	return NULL;
 }
 
 /*
- * The first usable entry of @dir after @name, or the first one at all for a
- * NULL @name. The entries are sorted by name, so that is where a listing
- * resumes when entries in front of it came or went in between. -ENOENT if
- * the directory is being unregistered.
+ * Fill @d with the entry of @dir behind the one it holds, letting go of that
+ * one, or with the first live entry after @name, or the first one at all for
+ * a NULL @name. While the entry @d holds is still registered its node is
+ * still in the tree and the next one is right behind it. Once it is gone the
+ * entries are found by name, they are sorted by it, so that is also where a
+ * listing resumes when entries in front of it came or went in between.
+ * 1 with the next entry pinned in @d, 0 at the end of the directory and
+ * -ENOENT once @dir itself is being unregistered.
+ *
+ * Everything that reads the tables happens under sysctl_lock, where an entry
+ * is erased when it is unregistered, since nothing holds the tables in use.
  */
-static int entry_after(struct ctl_dir *dir, const char *name, int namelen,
-		       struct ctl_table_header **phead,
-		       const struct ctl_table **pentry)
+static int sysctl_dir_next(struct ctl_dir *dir, struct sysctl_dirent *d,
+			   const char *name, int namelen)
 {
-	struct ctl_table_header *head = NULL;
-	const struct ctl_table *entry = NULL;
 	struct rb_node *node, *after = NULL;
+	struct ctl_table_header *head;
+	const struct ctl_table *entry;
 	struct ctl_node *ctl_node;
+	bool by_name = true;
+	int ret = 0;
 
 	spin_lock(&sysctl_lock);
+	if (d->head) {
+		if (d->node && !d->head->unregistering) {
+			after = rb_next(&d->node->node);
+			by_name = false;
+		}
+		sysctl_unpin(d->head);
+		d->head = NULL;
+	}
 	if (unlikely(dir->header.unregistering)) {
-		spin_unlock(&sysctl_lock);
-		return -ENOENT;
+		ret = -ENOENT;
+		goto out;
 	}
-	if (!name) {
+	if (!by_name) {
+		/* behind the entry @d held */
+	} else if (!name) {
 		after = rb_first(&dir->root);
 	} else for (node = dir->root.rb_node; node;) {
-		const struct ctl_table *table;
-		struct ctl_table_header *h;
-
 		ctl_node = rb_entry(node, struct ctl_node, node);
-		h = ctl_node->header;
-		table = &h->ctl_table[ctl_node - h->node];
-		if (namecmp(table->procname, strlen(table->procname),
+		head = ctl_node->header;
+		entry = &head->ctl_table[ctl_node - head->node];
+		if (namecmp(entry->procname, strlen(entry->procname),
 			    name, namelen) > 0) {
 			after = node;
 			node = node->rb_left;
@@ -454,15 +466,55 @@ static int entry_after(struct ctl_dir *dir, const char *name, int namelen,
 			node = node->rb_right;
 		}
 	}
-	ctl_node = first_usable_entry(after);
-	spin_unlock(&sysctl_lock);
-	if (ctl_node) {
-		head = ctl_node->header;
-		entry = &head->ctl_table[ctl_node - head->node];
+	ctl_node = first_live_entry(after);
+	if (!ctl_node)
+		goto out;
+	head = ctl_node->header;
+	entry = &head->ctl_table[ctl_node - head->node];
+
+	d->len = strlen(entry->procname);
+	if (d->len > NAME_MAX) {
+		ret = -ENAMETOOLONG;
+		goto out;
 	}
-	*phead = head;
-	*pentry = entry;
-	return 0;
+	memcpy(d->name, entry->procname, d->len);
+	d->skip = false;
+	d->node = ctl_node;
+	if (unlikely(S_ISLNK(entry->mode))) {
+		/* the entry it stands for in the caller's set, or nothing */
+		struct ctl_table_root *root = entry->data;
+		struct ctl_table_set *set = lookup_header_set(root);
+		struct ctl_dir *xdir = xlate_dir(set, head->parent);
+		struct ctl_table_header *xhead = NULL;
+		const struct ctl_table *xentry = NULL;
+
+		if (!IS_ERR(xdir))
+			xentry = find_entry(&xhead, xdir, d->name, d->len);
+		if (!xentry || xhead->unregistering) {
+			d->skip = true;
+		} else {
+			head = xhead;
+			entry = xentry;
+		}
+		d->node = NULL;
+	}
+	sysctl_pin(head);
+	d->head = head;
+	d->table = entry;
+	ret = 1;
+out:
+	spin_unlock(&sysctl_lock);
+	return ret;
+}
+
+static void sysctl_dir_done(struct sysctl_dirent *d)
+{
+	if (!d->head)
+		return;
+	spin_lock(&sysctl_lock);
+	sysctl_unpin(d->head);
+	spin_unlock(&sysctl_lock);
+	d->head = NULL;
 }
 
 /*
@@ -765,112 +817,71 @@ static void sysctl_cursor_set(struct file *file, const char *name, int len,
 	memcpy(cursor->name, name, len);
 }
 
-static bool proc_sys_fill_cache(struct file *file,
-				struct dir_context *ctx,
-				struct ctl_table_header *head,
-				const struct ctl_table *table)
+/*
+ * The inode number and type of the entry @d holds, instantiating its dentry
+ * if there is none. Needs the header pinned, proc_sys_make_inode() checks
+ * for itself that the table is still registered.
+ */
+static int proc_sys_fill_dirent(struct file *file, struct sysctl_dirent *d)
 {
 	struct dentry *child, *dir = file->f_path.dentry;
 	struct inode *inode;
 	struct qstr qname;
-	ino_t ino = 0;
-	unsigned type = DT_UNKNOWN;
 
-	qname.name = table->procname;
-	qname.len  = strlen(table->procname);
+	qname.name = d->name;
+	qname.len  = d->len;
 	qname.hash = full_name_hash(dir, qname.name, qname.len);
 
 	child = d_lookup(dir, &qname);
 	if (!child) {
 		child = d_alloc_parallel(dir, &qname);
 		if (IS_ERR(child))
-			return false;
+			return PTR_ERR(child);
 		if (d_in_lookup(child)) {
 			struct dentry *res;
-			inode = proc_sys_make_inode(dir->d_sb, head, table);
+			inode = proc_sys_make_inode(dir->d_sb, d->head, d->table);
 			res = d_splice_alias_ops(inode, child,
 						 &proc_sys_dentry_operations);
 			d_lookup_done(child);
 			if (unlikely(res)) {
 				dput(child);
 
-				/* skip an entry whose table is being unregistered */
-				if (res == ERR_PTR(-ENOENT))
-					return true;
 				if (IS_ERR(res))
-					return false;
+					return PTR_ERR(res);
 
 				child = res;
 			}
 		}
 	}
 	inode = d_inode(child);
-	ino  = inode->i_ino;
-	type = inode->i_mode >> 12;
+	d->ino = inode->i_ino;
+	d->type = inode->i_mode >> 12;
 	dput(child);
-	return dir_emit(ctx, qname.name, qname.len, ino, type);
-}
-
-static bool proc_sys_link_fill_cache(struct file *file,
-				    struct dir_context *ctx,
-				    struct ctl_table_header *head,
-				    const struct ctl_table *table)
-{
-	bool ret = true;
-
-	head = sysctl_head_grab(head);
-	if (IS_ERR(head))
-		return false;
-
-	/* It is not an error if we can not follow the link ignore it */
-	if (sysctl_follow_link(&head, &table))
-		goto out;
-
-	ret = proc_sys_fill_cache(file, ctx, head, table);
-out:
-	sysctl_head_finish(head);
-	return ret;
-}
-
-static int scan(struct ctl_table_header *head, const struct ctl_table *table,
-		unsigned long *pos, struct file *file,
-		struct dir_context *ctx)
-{
-	bool res;
-
-	if ((*pos)++ < ctx->pos)
-		return true;
-
-	if (unlikely(S_ISLNK(table->mode)))
-		res = proc_sys_link_fill_cache(file, ctx, head, table);
-	else
-		res = proc_sys_fill_cache(file, ctx, head, table);
-
-	if (res) {
-		ctx->pos = *pos;
-		sysctl_cursor_set(file, table->procname, strlen(table->procname),
-				  ctx->pos);
-	}
-
-	return res;
+	return 0;
 }
 
+/*
+ * Unregistering a table waits until nobody uses it anymore and a copy to
+ * userspace can take as long as userspace wants. So a listing doesn't take
+ * any table into use. It pins the memory of the entry it is at, finds the
+ * next one under sysctl_lock and emits with nothing held, like
+ * kernfs_fop_readdir() and proc_readdir_de() do. Between calls a listing
+ * resumes after the name of the last entry it emitted.
+ */
 static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 {
-	struct ctl_table_header *head = grab_header(file_inode(file));
+	struct ctl_table_header *head = PROC_I(file_inode(file))->sysctl;
 	struct sysctl_cursor *cursor = file->private_data;
-	struct ctl_table_header *h = NULL;
-	const struct ctl_table *entry;
+	struct sysctl_dirent d = {};
 	struct ctl_dir *ctl_dir;
-	unsigned long pos;
-
-	if (IS_ERR(head))
-		return PTR_ERR(head);
+	int err;
 
+	if (!head)
+		head = &sysctl_table_root.default_set.dir.header;
 	ctl_dir = container_of(head, struct ctl_dir, header);
 
 	if (!dir_emit_dots(file, ctx))
-		goto out;
+		return 0;
 
 	/*
 	 * Resume after the last entry the previous call emitted. Only a seek
@@ -878,22 +889,32 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 	 * seeking to an arbitrary position always did.
 	 */
 	if (cursor && cursor->pos == ctx->pos) {
-		entry_after(ctl_dir, cursor->name, cursor->len, &h, &entry);
-		pos = ctx->pos;
+		err = sysctl_dir_next(ctl_dir, &d, cursor->name, cursor->len);
 	} else {
-		first_entry(ctl_dir, &h, &entry);
-		pos = 2;
+		loff_t pos = 2;
+
+		err = sysctl_dir_next(ctl_dir, &d, NULL, 0);
+		while (err > 0 && pos++ < ctx->pos)
+			err = sysctl_dir_next(ctl_dir, &d, d.name, d.len);
 	}
+	if (err < 0)
+		return err;
 
-	for (; h; next_entry(&h, &entry)) {
-		if (!scan(h, entry, &pos, file, ctx)) {
-			sysctl_head_finish(h);
-			break;
+	while (err > 0) {
+		if (!d.skip) {
+			err = proc_sys_fill_dirent(file, &d);
+			/* skip an entry whose table is being unregistered */
+			if (err && err != -ENOENT)
+				break;
+			if (!err && !dir_emit(ctx, d.name, d.len, d.ino, d.type))
+				break;
 		}
+		ctx->pos++;
+		sysctl_cursor_set(file, d.name, d.len, ctx->pos);
+		err = sysctl_dir_next(ctl_dir, &d, d.name, d.len);
 	}
-out:
-	sysctl_head_finish(head);
-	return 0;
+	sysctl_dir_done(&d);
+	return err;
 }
 
 static int proc_sys_dir_release(struct inode *inode, struct file *file)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH RESEND v2 50/51] proc: don't pin a symlink's entry while readlink() copies it out
  2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
                     ` (33 preceding siblings ...)
  2026-11-04 19:27   ` [PATCH RESEND v2 48/51] proc: sysctl: don't hold the tables while readdir copies to userspace Christian Brauner
@ 2026-11-04 19:28   ` Christian Brauner
  34 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-04 19:28 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_get_link() pins the entry with use_pde() and the delayed call
unpins it. vfs_readlink() copies the link to userspace in between. So a
page fault in that copy holds up remove_proc_entry() just like the
copies of reads and writes do, and the callers hold locks while they
wait. /proc/asound/<id> is removed with snd_card_mutex and info_mutex
held when a card changes its id.

The pin doesn't keep anything alive. proc_symlink() copies the target
and pde_free() frees it with the entry, and the inode holds a reference
to the entry. What the pin does is keep a removed symlink from being
followed. So check ->in_use instead and drop the delayed call. That
also takes an atomic pair off every traversal of /proc/net and
/proc/mounts.

Fixes: 7e0e953bb0cf ("procfs: fix race between symlink removals and traversals")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/inode.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 9933f03ab9fb..2d65f3c8ec03 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -629,19 +629,19 @@ static const struct file_operations proc_iter_file_ops_compat = {
 };
 #endif
 
-static void proc_put_link(void *p)
-{
-	unuse_pde(p);
-}
-
 static const char *proc_get_link(struct dentry *dentry,
 				 struct inode *inode,
 				 struct delayed_call *done)
 {
 	struct proc_dir_entry *pde = PDE(inode);
-	if (!use_pde(pde))
+
+	/*
+	 * No pin. The link lives with the entry, which the inode holds, and a
+	 * reader could fault while vfs_readlink() copies it out. Just don't
+	 * follow a removed one.
+	 */
+	if (unlikely(atomic_read(&pde->in_use) < 0))
 		return ERR_PTR(-EINVAL);
-	set_delayed_call(done, proc_put_link, pde);
 	return pde->data;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned
  2026-11-04 19:26   ` [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned Christian Brauner
@ 2026-11-05  0:08     ` Jann Horn
  2026-11-05  8:43       ` Christian Brauner
  2026-11-05 16:26     ` Jann Horn
  1 sibling, 1 reply; 41+ messages in thread
From: Jann Horn @ 2026-11-05  0:08 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Al Viro, Kees Cook, Joel Granados, Jan Kara, linux-fsdevel,
	linux-kernel, stable

On Wed, Nov 4, 2026 at 8:27 PM Christian Brauner <brauner@kernel.org> wrote:
> proc_sys_readdir() counts the entries of a directory and resumes a
> listing at the number getdents64() stopped at. The entries are sorted
> by name, so one that is registered or unregistered in between shifts
> everything behind it by one. Listing /proc/sys/net/ipv4/conf with a
> buffer that holds ".", ".." and "all" and registering a device whose
> name sorts before "all" makes the next call return "all" a second time.
> Unregistering one that sorted before the position makes it skip an
> entry instead. So a listing that takes more than one call shows a name
> twice, or not at all. Even the 64k buffer of ls needs a second call to
> find the end of the directory, so a device that comes up right then
> repeats the last entry.
>
> So remember the name of the last entry a call emitted in the file and
> resume at the first usable entry after it, like kernfs resumes after
> the node it stopped at. The entry itself can't be pinned across calls,
> unregistering waits for that. If the position was moved by a seek in
> between the listing counts from the start like before, seeking to an
> arbitrary position has never been better than that.
>
> That also takes one walk down the tree to resume instead of a walk over
> every entry in front of the position with a round trip through
> sysctl_lock for each of them.
>
> Fixes: 77b14db502cb ("[PATCH] sysctl: reimplement the sysctl proc support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> ---
[...]
> @@ -756,8 +834,11 @@ static int scan(struct ctl_table_header *head, const struct ctl_table *table,
>         else
>                 res = proc_sys_fill_cache(file, ctx, head, table);
>
> -       if (res)
> +       if (res) {
>                 ctx->pos = *pos;
> +               sysctl_cursor_set(file, table->procname, strlen(table->procname),
> +                                 ctx->pos);

sysctl_cursor_set() is called here, when the output buffer is too
small; but not if the proc_sys_readdir() loop ends because the end of
the table was reached and "h" is NULL?

Doesn't that mean that another getdents() after reaching end of the
file would observe the last entries again?

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RESEND v2 01/51] alloc_tag: don't remove /proc/allocinfo under the codetag module lock
  2026-11-04 19:06   ` [PATCH RESEND v2 01/51] alloc_tag: don't remove /proc/allocinfo under the codetag module lock Christian Brauner
@ 2026-11-05  2:15     ` Hao Ge
  0 siblings, 0 replies; 41+ messages in thread
From: Hao Ge @ 2026-11-05  2:15 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers, Chuck Lever, stable,
	linux-fsdevel, Suren Baghdasaryan

Cc Suren

Hi Christian

On 2026/11/5 03:06, Christian Brauner wrote:
> codetag_alloc_module_section() takes the codetag type's mod_lock for
> writing and calls reserve_module_tags() with it held. When that can't
> reserve the tags, because the section's alignment doesn't fit, because
> there are too many tags for the page flag bits or because the memory
> for them can't be allocated, it shuts memory allocation profiling down
> and removes /proc/allocinfo, still with mod_lock held.
> 
> remove_proc_entry() waits until every reader of the entry has let go of
> it, and a reader of /proc/allocinfo takes mod_lock for reading in
> allocinfo_start() while it holds the entry. So a module load that hits
> one of those paths while /proc/allocinfo is being read waits for the
> reader and the reader waits for the module load, for good.
> 
> Let a work item remove the entry in that case. The profiling is shut
> down right away like before, the file just goes a moment later.
> 
> Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Thanks.

is this a problem you have hit in practice?
Sashiko discovered this issue earlier and reported it as follows:

> diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
> index 2070e682fe106..112a014d4b890 100644
> --- a/mm/alloc_tag.c
> +++ b/mm/alloc_tag.c
> @@ -950,10 +950,12 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
>  		int grow_res;
>  
>  		module_tags.size = offset + size;
> -		if (mem_alloc_profiling_enabled() && !tags_addressable()) {
> +		if (!tags_addressable()) {
>  			shutdown_mem_profiling(true);
This is a pre-existing issue, but does this shutdown_mem_profiling(true) call
introduce an AB-BA deadlock between /proc/allocinfo file readers (pde->in_use)
and module loading (cttype->mod_lock)?
A user reading /proc/allocinfo acquires the procfs in_use reference in
use_pde(), then blocks on down_read(&alloc_tag_cttype->mod_lock) in
allocinfo_start().
Concurrently, if a module loads and tags_addressable() fails,
codetag_alloc_module_section() holds down_write(&cttype->mod_lock) and calls
shutdown_mem_profiling(true), which removes the proc entry. This removal waits
indefinitely for the in_use reference to drop to zero, hanging both procfs
reads and module loading.
> -			pr_warn("With module %s there are too many tags to fit in %d page flag bits. Memory allocation profiling is disabled!\n",
> -				mod->name, NR_UNUSED_PAGEFLAG_BITS);
> +			pr_warn_once("With module %s there are too many tags to fit in %d page flag bits. Memory allocation profiling is disabled!\n",
> +				     mod->name, NR_UNUSED_PAGEFLAG_BITS);
> +			release_module_tags(mod, false);
> +			return ERR_PTR(-EAGAIN);
>  		}
>  
>  		grow_res = vm_module_tags_populate();

I previously sent a patch with a comparable idea, though Sashiko remained unconvinced.

[patch]: https://lore.kernel.org/all/20260817062726.106511-3-hao.ge@linux.dev/

[Sashiko's objections]: https://sashiko.dev/#/patchset/20260817062726.106511-1-hao.ge@linux.dev

Could this patch perhaps wait for mine? No worries if this is inconvenient.

Best regards                                                                                                                                                                                                                                                                  
Hao

> ---
>  mm/alloc_tag.c | 27 ++++++++++++++++++++++++---
>  1 file changed, 24 insertions(+), 3 deletions(-)
> 
> diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
> index 82e2c3448dcf..7d7ad6704953 100644
> --- a/mm/alloc_tag.c
> +++ b/mm/alloc_tag.c
> @@ -15,6 +15,7 @@
>  #include <linux/seq_file.h>
>  #include <linux/string_choices.h>
>  #include <linux/vmalloc.h>
> +#include <linux/workqueue.h>
>  #include <linux/kmemleak.h>
>  #include <uapi/linux/alloc_tag.h>
>  
> @@ -604,6 +605,26 @@ static void shutdown_mem_profiling(bool remove_file)
>  	mem_profiling_support = false;
>  }
>  
> +static void remove_allocinfo_file(struct work_struct *work)
> +{
> +	remove_proc_entry(ALLOCINFO_FILE_NAME, NULL);
> +}
> +static DECLARE_WORK(remove_allocinfo_work, remove_allocinfo_file);
> +
> +/*
> + * A module's tags are reserved with the codetag module lock held for
> + * writing, and a reader of /proc/allocinfo takes that lock with the entry
> + * pinned. Removing the entry there would wait for that reader for good, so
> + * let a work item do it.
> + */
> +static void shutdown_mem_profiling_async(void)
> +{
> +	if (!mem_profiling_support)
> +		return;
> +	shutdown_mem_profiling(false);
> +	schedule_work(&remove_allocinfo_work);
> +}
> +
>  void __init alloc_tag_sec_init(void)
>  {
>  	struct alloc_tag *last_codetag;
> @@ -855,7 +876,7 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
>  		align = 1;
>  
>  	if (!ensure_alignment(align, &prepend)) {
> -		shutdown_mem_profiling(true);
> +		shutdown_mem_profiling_async();
>  		pr_err("%s: alignment %lu is incompatible with allocation tag indexing. Memory allocation profiling is disabled!\n",
>  			mod->name, align);
>  		return ERR_PTR(-EINVAL);
> @@ -905,14 +926,14 @@ unlock:
>  
>  		module_tags.size = offset + size;
>  		if (mem_alloc_profiling_enabled() && !tags_addressable()) {
> -			shutdown_mem_profiling(true);
> +			shutdown_mem_profiling_async();
>  			pr_warn("With module %s there are too many tags to fit in %d page flag bits. Memory allocation profiling is disabled!\n",
>  				mod->name, NR_UNUSED_PAGEFLAG_BITS);
>  		}
>  
>  		grow_res = vm_module_tags_populate();
>  		if (grow_res) {
> -			shutdown_mem_profiling(true);
> +			shutdown_mem_profiling_async();
>  			pr_err("Failed to allocate memory for allocation tags in the module %s. Memory allocation profiling is disabled!\n",
>  			       mod->name);
>  			return ERR_PTR(grow_res);
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned
  2026-11-05  0:08     ` Jann Horn
@ 2026-11-05  8:43       ` Christian Brauner
  0 siblings, 0 replies; 41+ messages in thread
From: Christian Brauner @ 2026-11-05  8:43 UTC (permalink / raw)
  To: Jann Horn
  Cc: Al Viro, Kees Cook, Joel Granados, Jan Kara, linux-fsdevel,
	linux-kernel, stable

On Thu, 5 Nov 2026 01:08:36 +0100, Jann Horn wrote:
> sysctl_cursor_set() is called here, when the output buffer is too
> small; but not if the proc_sys_readdir() loop ends because the end of
> the table was reached and "h" is NULL?

It's the other way around. res is true when the entry went into the
buffer. The cursor moves with every entry that was emitted. That
includes the last entry of the table.

When the buffer is full, res is false, we stop, and the cursor leads us
to the last entry that we sent.

> Doesn't that mean that another getdents() after reaching end of the
> file would observe the last entries again?

No. After the last entry, cursor->pos == ctx->pos. So the next getdents
does entry_after(last_name). That doens't find anything. So h is NULL,
the loop doesn't run and it returns zero.

If an entry registered behind the name it would show up.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned
  2026-11-04 19:26   ` [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned Christian Brauner
  2026-11-05  0:08     ` Jann Horn
@ 2026-11-05 16:26     ` Jann Horn
  1 sibling, 0 replies; 41+ messages in thread
From: Jann Horn @ 2026-11-05 16:26 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Al Viro, Kees Cook, Joel Granados, Jan Kara, linux-fsdevel,
	kernel list, stable

On Wed, Nov 4, 2026 at 8:27 PM Christian Brauner <brauner@kernel.org> wrote:
> proc_sys_readdir() counts the entries of a directory and resumes a
> listing at the number getdents64() stopped at. The entries are sorted
> by name, so one that is registered or unregistered in between shifts
> everything behind it by one. Listing /proc/sys/net/ipv4/conf with a
> buffer that holds ".", ".." and "all" and registering a device whose
> name sorts before "all" makes the next call return "all" a second time.
> Unregistering one that sorted before the position makes it skip an
> entry instead. So a listing that takes more than one call shows a name
> twice, or not at all. Even the 64k buffer of ls needs a second call to
> find the end of the directory, so a device that comes up right then
> repeats the last entry.
>
> So remember the name of the last entry a call emitted in the file and
> resume at the first usable entry after it, like kernfs resumes after
> the node it stopped at. The entry itself can't be pinned across calls,
> unregistering waits for that. If the position was moved by a seek in
> between the listing counts from the start like before, seeking to an
> arbitrary position has never been better than that.
>
> That also takes one walk down the tree to resume instead of a walk over
> every entry in front of the position with a round trip through
> sysctl_lock for each of them.
>
> Fixes: 77b14db502cb ("[PATCH] sysctl: reimplement the sysctl proc support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Reviewed-by: Jann Horn <jannh@google.com>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RESEND v2 03/51] ALSA: cs46xx: hand the region to the region proc files
  2026-11-04 19:16   ` [PATCH RESEND v2 03/51] ALSA: cs46xx: hand the region to the region proc files Christian Brauner
@ 2026-11-07  9:00     ` Takashi Iwai
  0 siblings, 0 replies; 41+ messages in thread
From: Takashi Iwai @ 2026-11-07  9:00 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-fsdevel, Linus Torvalds, Jann Horn, Josef Bacik,
	Chris Mason, Jeff Layton, NeilBrown, Eric Dumazet,
	Thomas Gleixner, Al Viro, Jonathan Corbet, Christoph Hellwig,
	Greg Kroah-Hartman, Joel Granados, Kees Cook, Jan Kara, Jan Kara,
	linux-kernel, linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi, linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers, Chuck Lever, stable

On Wed, 04 Nov 2026 20:16:45 +0100,
Christian Brauner wrote:
> 
> snd_cs46xx_proc_init() stores the chip in the private data of the five
> region entries, but snd_cs46xx_io_read() reads that back as the region,
> so a read of /proc/asound/cardN/CS46xx_BA0 and friends copies from
> whatever sits at the region's offset in struct snd_cs46xx instead of
> the mapped registers. Store the region.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Reviewed-by: Takashi Iwai <tiwai@suse.de>


thanks,

Takashi

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2026-11-07  9:00 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20261104-work-procfs-v2-0-b45cd5789ac5@brauner>
2026-11-04 19:05 ` [PATCH RESEND v2 00/51] proc: fix a few old bugs in /proc handlers (Part 1/7) Christian Brauner
2026-11-04 19:06   ` [PATCH RESEND v2 01/51] alloc_tag: don't remove /proc/allocinfo under the codetag module lock Christian Brauner
2026-11-05  2:15     ` Hao Ge
2026-11-04 19:16   ` [PATCH RESEND v2 02/51] scsi: esas2r: don't copy kernel pointers out in EXPRESS_IOCTL_GET_MOD_INFO Christian Brauner
2026-11-04 19:16   ` [PATCH RESEND v2 03/51] ALSA: cs46xx: hand the region to the region proc files Christian Brauner
2026-11-07  9:00     ` Takashi Iwai
2026-11-04 19:16   ` [PATCH RESEND v2 04/51] PNP: isapnp: don't go on when isapnp_cfg_begin() fails Christian Brauner
2026-11-04 19:17   ` [PATCH RESEND v2 05/51] xtensa: simdisk: remove the proc entry before the disk Christian Brauner
2026-11-04 19:17   ` [PATCH RESEND v2 08/51] powerpc/pseries/lparcfg: don't drop the last byte of a write Christian Brauner
2026-11-04 19:19   ` [PATCH RESEND v2 13/51] sched/psi: don't drop the last byte of a trigger write Christian Brauner
2026-11-04 19:19   ` [PATCH RESEND v2 14/51] proc: sysctl: don't stop readdir at an entry that is being unregistered Christian Brauner
2026-11-04 19:19   ` [PATCH RESEND v2 15/51] PCI/proc: don't truncate the position of config space accesses Christian Brauner
2026-11-04 19:19   ` [PATCH RESEND v2 16/51] PNP: isapnp: don't read past the end of the buffer on a large pread() Christian Brauner
2026-11-04 19:20   ` [PATCH RESEND v2 17/51] s390/dasd: don't look in front of the buffer on an empty write Christian Brauner
2026-11-04 19:20   ` [PATCH RESEND v2 18/51] platform/x86: toshiba_acpi: reject malformed brightness and fan writes Christian Brauner
2026-11-04 19:20   ` [PATCH RESEND v2 19/51] xtensa: simdisk: don't read the file name without the lock Christian Brauner
2026-11-04 19:20   ` [PATCH RESEND v2 20/51] powerpc/rtas_flash: don't read past the validate message Christian Brauner
2026-11-04 19:21   ` [PATCH RESEND v2 21/51] powerpc/rtasd: zero the error log buffer Christian Brauner
2026-11-04 19:21   ` [PATCH RESEND v2 22/51] ALSA: info: don't write past the end of a data entry Christian Brauner
2026-11-04 19:22   ` [PATCH RESEND v2 26/51] scsi: core: NUL-terminate what is written to the host proc files Christian Brauner
2026-11-04 19:22   ` [PATCH RESEND v2 27/51] um: NUL-terminate what is written to /proc/exitcode Christian Brauner
2026-11-04 19:22   ` [PATCH RESEND v2 28/51] ntfs3: don't warn when a huge label is written Christian Brauner
2026-11-04 19:23   ` [PATCH RESEND v2 29/51] proc: don't walk through a non-directory when resolving a name Christian Brauner
2026-11-04 19:23   ` [PATCH RESEND v2 30/51] net: pktgen: reject device names with a '/' Christian Brauner
2026-11-04 19:23   ` [PATCH RESEND v2 31/51] proc: don't WARN about a name that is already registered Christian Brauner
2026-11-04 19:23   ` [PATCH RESEND v2 32/51] proc: resume readdir after the last name it returned Christian Brauner
2026-11-04 19:24   ` [PATCH RESEND v2 36/51] proc: don't let NOMMU map procfs files Christian Brauner
2026-11-04 19:25   ` [PATCH RESEND v2 37/51] proc/vmcore: don't let vmcore_cleanup() run twice at once Christian Brauner
2026-11-04 19:25   ` [PATCH RESEND v2 38/51] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page() Christian Brauner
2026-11-04 19:25   ` [PATCH RESEND v2 39/51] powerpc/crash_dump: recover from a machine check " Christian Brauner
2026-11-04 19:25   ` [PATCH RESEND v2 40/51] sunrpc: make queue_wait a global wait queue again Christian Brauner
2026-11-04 19:26   ` [PATCH RESEND v2 41/51] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls Christian Brauner
2026-11-04 19:26   ` [PATCH RESEND v2 42/51] powerpc/pseries: bound the lengths in the ofdt property parser Christian Brauner
2026-11-04 19:26   ` [PATCH RESEND v2 43/51] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long Christian Brauner
2026-11-04 19:26   ` [PATCH RESEND v2 44/51] proc: sysctl: resume readdir after the last name it returned Christian Brauner
2026-11-05  0:08     ` Jann Horn
2026-11-05  8:43       ` Christian Brauner
2026-11-05 16:26     ` Jann Horn
2026-11-04 19:27   ` [PATCH RESEND v2 46/51] proc: sysctl: copy to and from userspace without holding the table Christian Brauner
2026-11-04 19:27   ` [PATCH RESEND v2 48/51] proc: sysctl: don't hold the tables while readdir copies to userspace Christian Brauner
2026-11-04 19:28   ` [PATCH RESEND v2 50/51] proc: don't pin a symlink's entry while readlink() copies it out Christian Brauner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox