mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] two small fixes out of kernel/sys.c
@ 2026-11-05  0:31 Bradley Morgan
  2026-11-05  0:31 ` [PATCH 1/3] tools: pass the unused prctl() args explicitly for PR_FUTEX_HASH Bradley Morgan
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Bradley Morgan @ 2026-11-05  0:31 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Arnaldo Carvalho de Melo, Namhyung Kim, Andrew Morton
  Cc: Bradley Morgan, Darren Hart, Davidlohr Bueso, André Almeida,
	Shuah Khan, Wake Liu, Arnd Bergmann, linux-perf-users,
	linux-kselftest, linux-kernel

Two unrelated things that came out of reading kernel/sys.c line by line.
They are in one series only because they came from the same read, patch 3
stands on its own and can be taken without the other two.

Patches 1 and 2 have to go in that order.  PR_FUTEX_HASH is the only case
in the prctl switch that does not check the args it does not use, arg5
does not even reach futex_hash_prctl().  Patch 2 adds the checks.  Patch 1
goes first because prctl() is variadic, so the two callers under tools/
leave the args they do not pass as whatever those registers hold, and
patch 2 would fail them at random without it.

Patch 3 is compat_sysinfo().  It scales the 64 bit memory values down so
they fit the 32 bit fields, and stops when the unit reaches PAGE_SIZE.
But do_sysinfo() hands it bytes with mem_unit at 1, so on a box with
enough ram it stops far too early and drops the high bits.  16 TiB reads
back as 0 and 17 TiB reads back as 1 TiB.  The loop only runs longer on
the path that was already wrong, nothing that works today changes.

Tested on ppc64.

Bradley Morgan (3):
  tools: pass the unused prctl() args explicitly for PR_FUTEX_HASH
  futex: reject the unused PR_FUTEX_HASH arguments
  compat: make compat_sysinfo() scale far enough to actually fit

 include/linux/futex.h                         |  6 ++--
 kernel/futex/core.c                           |  7 +++--
 kernel/sys.c                                  | 28 +++++++++----------
 tools/perf/bench/futex.c                      |  4 +--
 .../futex/functional/futex_priv_hash.c        |  4 +--
 5 files changed, 26 insertions(+), 23 deletions(-)

-- 
2.53.0

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

* [PATCH 1/3] tools: pass the unused prctl() args explicitly for PR_FUTEX_HASH
  2026-11-05  0:31 [PATCH 0/3] two small fixes out of kernel/sys.c Bradley Morgan
@ 2026-11-05  0:31 ` Bradley Morgan
  2026-11-05  0:31 ` [PATCH 2/3] futex: reject the unused PR_FUTEX_HASH arguments Bradley Morgan
  2026-11-05  0:31 ` [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit Bradley Morgan
  2 siblings, 0 replies; 7+ messages in thread
From: Bradley Morgan @ 2026-11-05  0:31 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Peter Zijlstra,
	Arnaldo Carvalho de Melo, Namhyung Kim
  Cc: Bradley Morgan, Darren Hart, Davidlohr Bueso, André Almeida,
	Shuah Khan, Wake Liu, linux-perf-users, linux-kselftest,
	linux-kernel

prctl() is declared variadic in sys/prctl.h, so an argument the caller
leaves out of the call is whatever that register happens to hold.
Neither file passes all five.  The two SET_SLOTS calls leave arg5 out
and the two GET_SLOTS calls leave arg3, arg4 and arg5 out, so those are
whatever the registers hold.  The kernel does not look at them today so
it works, but it works by luck.  I checked that against a running
kernel, garbage in arg5 on SET_SLOTS and in all three on GET_SLOTS are
accepted.

Pass the zeros.  No functional change.

Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
 tools/perf/bench/futex.c                                   | 4 ++--
 tools/testing/selftests/futex/functional/futex_priv_hash.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/tools/perf/bench/futex.c b/tools/perf/bench/futex.c
index 1968c9d00b5bb..f4c1c30efe0bd 100644
--- a/tools/perf/bench/futex.c
+++ b/tools/perf/bench/futex.c
@@ -20,7 +20,7 @@ void futex_set_nbuckets_param(struct bench_futex_parameters *params)
 	if (params->nbuckets < 0)
 		return;
 
-	ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, params->nbuckets, 0);
+	ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, params->nbuckets, 0, 0);
 	if (ret) {
 		printf("Requesting %d hash buckets failed: %d/%m\n",
 		       params->nbuckets, ret);
@@ -33,7 +33,7 @@ void futex_print_nbuckets(struct bench_futex_parameters *params)
 	char *futex_hash_mode;
 	int ret;
 
-	ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS);
+	ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS, 0, 0, 0);
 	if (params->nbuckets >= 0) {
 		if (ret != params->nbuckets) {
 			if (ret < 0) {
diff --git a/tools/testing/selftests/futex/functional/futex_priv_hash.c b/tools/testing/selftests/futex/functional/futex_priv_hash.c
index a8742e2045402..96bfb72362221 100644
--- a/tools/testing/selftests/futex/functional/futex_priv_hash.c
+++ b/tools/testing/selftests/futex/functional/futex_priv_hash.c
@@ -32,12 +32,12 @@ static int counter;
 
 static int futex_hash_slots_set(unsigned int slots)
 {
-	return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, slots, 0);
+	return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, slots, 0, 0);
 }
 
 static int futex_hash_slots_get(void)
 {
-	return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS);
+	return prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS, 0, 0, 0);
 }
 
 static void futex_hash_slots_set_verify(struct __test_metadata *_metadata, int slots)
-- 
2.53.0

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

* [PATCH 2/3] futex: reject the unused PR_FUTEX_HASH arguments
  2026-11-05  0:31 [PATCH 0/3] two small fixes out of kernel/sys.c Bradley Morgan
  2026-11-05  0:31 ` [PATCH 1/3] tools: pass the unused prctl() args explicitly for PR_FUTEX_HASH Bradley Morgan
@ 2026-11-05  0:31 ` Bradley Morgan
  2026-11-05  0:31 ` [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit Bradley Morgan
  2 siblings, 0 replies; 7+ messages in thread
From: Bradley Morgan @ 2026-11-05  0:31 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Peter Zijlstra
  Cc: Bradley Morgan, Darren Hart, Davidlohr Bueso, André Almeida,
	linux-kernel

PR_SET_MM, PR_SET_MDWE and the shadow stack cases all check that the
args they do not use are zero before doing anything.  PR_FUTEX_HASH is
the one that does not.  arg5 never even reaches futex_hash_prctl(), and
the GET_SLOTS branch never looks at arg3 or arg4, so
prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS, 1, 2, 3) hands back the
slot count.

Once an arg is ignored it is burned, whatever userspace sends there
today becomes part of the ABI and we can never use it for anything
without breaking whoever already passes garbage.  Take arg5 too and
reject the unused args per sub command, same as the neighbours do.

prctl() is variadic, so the two callers under tools/ leave those args as
whatever the register holds, they only work today because nothing was
checked.  The preceding patch in this series writes the zeros out first,
so this does not break them.

Fixes: 80367ad01d93 ("futex: Add basic infrastructure for local task local hash")
Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
 include/linux/futex.h | 6 ++++--
 kernel/futex/core.c   | 7 +++++--
 kernel/sys.c          | 2 +-
 3 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/include/linux/futex.h b/include/linux/futex.h
index a895d8c69d663..70adc9fce6e63 100644
--- a/include/linux/futex.h
+++ b/include/linux/futex.h
@@ -80,7 +80,8 @@ void futex_exec_done(struct task_struct *tsk);
 
 long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
 	      u32 __user *uaddr2, u32 val2, u32 val3);
-int futex_hash_prctl(unsigned long arg2, unsigned long arg3, unsigned long arg4);
+int futex_hash_prctl(unsigned long arg2, unsigned long arg3, unsigned long arg4,
+		     unsigned long arg5);
 
 #ifdef CONFIG_FUTEX_PRIVATE_HASH
 int futex_hash_allocate_default(void);
@@ -100,7 +101,8 @@ static inline long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout
 {
 	return -EINVAL;
 }
-static inline int futex_hash_prctl(unsigned long arg2, unsigned long arg3, unsigned long arg4)
+static inline int futex_hash_prctl(unsigned long arg2, unsigned long arg3,
+				   unsigned long arg4, unsigned long arg5)
 {
 	return -EINVAL;
 }
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index a240638f69e5c..eabf8abc2624b 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -2068,19 +2068,22 @@ void futex_mm_init(struct mm_struct *mm)
 	futex_robust_unlock_init_mm(&mm->futex);
 }
 
-int futex_hash_prctl(unsigned long arg2, unsigned long arg3, unsigned long arg4)
+int futex_hash_prctl(unsigned long arg2, unsigned long arg3, unsigned long arg4,
+		     unsigned long arg5)
 {
 	unsigned int flags = FH_CUSTOM;
 	int ret;
 
 	switch (arg2) {
 	case PR_FUTEX_HASH_SET_SLOTS:
-		if (arg4)
+		if (arg4 || arg5)
 			return -EINVAL;
 		ret = futex_hash_allocate(arg3, flags);
 		break;
 
 	case PR_FUTEX_HASH_GET_SLOTS:
+		if (arg3 || arg4 || arg5)
+			return -EINVAL;
 		ret = futex_hash_get_slots();
 		break;
 
diff --git a/kernel/sys.c b/kernel/sys.c
index 755bfa5ccd92c..c4839c986f5e0 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2885,7 +2885,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
 		error = posixtimer_create_prctl(arg2);
 		break;
 	case PR_FUTEX_HASH:
-		error = futex_hash_prctl(arg2, arg3, arg4);
+		error = futex_hash_prctl(arg2, arg3, arg4, arg5);
 		break;
 	case PR_RSEQ_SLICE_EXTENSION:
 		if (arg4 || arg5)
-- 
2.53.0

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

* [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit
  2026-11-05  0:31 [PATCH 0/3] two small fixes out of kernel/sys.c Bradley Morgan
  2026-11-05  0:31 ` [PATCH 1/3] tools: pass the unused prctl() args explicitly for PR_FUTEX_HASH Bradley Morgan
  2026-11-05  0:31 ` [PATCH 2/3] futex: reject the unused PR_FUTEX_HASH arguments Bradley Morgan
@ 2026-11-05  0:31 ` Bradley Morgan
  2026-11-05  7:25   ` Arnd Bergmann
  2 siblings, 1 reply; 7+ messages in thread
From: Bradley Morgan @ 2026-11-05  0:31 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Bradley Morgan, Arnd Bergmann, linux-kernel

compat_sysinfo() checks whether totalram or totalswap is too big for the
compat struct and scales the values down to fit, but the loop stops as
soon as s.mem_unit reaches PAGE_SIZE.  That caps the compression at one
page, 12 bits with 4K pages.  On a 64 bit kernel do_sysinfo() always
hands it values in bytes with mem_unit == 1, its own overflow math runs
on unsigned long so it does not bail until 16 EiB, so the loop runs its
12 turns, the values come back out as a page count, and anything past
2^32 pages still does not fit.  Storing that into the u32 field then
just drops the top bits.

So a 32 bit process on a box past 2^32 pages gets a wrapped number
instead of a coarse one, with 4K pages that starts at 16 TiB:

  ram      old                  new
  8 TiB    2147483648 x 4096    unchanged
  16 TiB   0 x 4096             2147483648 x 8192
  17 TiB   268435456 x 4096     2281701376 x 8192
  32 TiB   0 x 4096             2147483648 x 16384

The 17 TiB row is the nasty one, a 32 bit process is told the box has
1 TiB of ram.  Keep scaling until the values fit instead of until the
unit reaches a page, the unit then says how coarse the number is, which
is what mem_unit is for.

Nothing that works today changes.  I ran both loops side by side over
ram from 1 MiB to 2^56 with 4K and 64K pages, they disagree on exactly
the inputs where the old one dropped bits and agree on every one it got
right.

Signed-off-by: Bradley Morgan <brads@mainlining.org>
---
 kernel/sys.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/kernel/sys.c b/kernel/sys.c
index c4839c986f5e0..8f683b6bb87b8 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -3035,24 +3035,22 @@ COMPAT_SYSCALL_DEFINE1(sysinfo, struct compat_sysinfo __user *, info)
 	do_sysinfo(&s);
 
 	/* Check to see if any memory value is too large for 32-bit and scale
-	 *  down if needed
+	 * down if needed.  Keep going until the values fit, not just until the
+	 * unit reaches a page.  The values halve each turn, so this ends.
 	 */
 	if (upper_32_bits(s.totalram) || upper_32_bits(s.totalswap)) {
-		int bitcount = 0;
-
-		while (s.mem_unit < PAGE_SIZE) {
+		while (s.mem_unit < PAGE_SIZE || upper_32_bits(s.totalram) ||
+		       upper_32_bits(s.totalswap)) {
 			s.mem_unit <<= 1;
-			bitcount++;
+			s.totalram >>= 1;
+			s.freeram >>= 1;
+			s.sharedram >>= 1;
+			s.bufferram >>= 1;
+			s.totalswap >>= 1;
+			s.freeswap >>= 1;
+			s.totalhigh >>= 1;
+			s.freehigh >>= 1;
 		}
-
-		s.totalram >>= bitcount;
-		s.freeram >>= bitcount;
-		s.sharedram >>= bitcount;
-		s.bufferram >>= bitcount;
-		s.totalswap >>= bitcount;
-		s.freeswap >>= bitcount;
-		s.totalhigh >>= bitcount;
-		s.freehigh >>= bitcount;
 	}
 
 	memset(&s_32, 0, sizeof(s_32));
-- 
2.53.0

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

* Re: [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit
  2026-11-05  0:31 ` [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit Bradley Morgan
@ 2026-11-05  7:25   ` Arnd Bergmann
  2026-11-05 16:47     ` Bradley Morgan
  0 siblings, 1 reply; 7+ messages in thread
From: Arnd Bergmann @ 2026-11-05  7:25 UTC (permalink / raw)
  To: Bradley Morgan, Andrew Morton; +Cc: linux-kernel

On Thu, Nov 5, 2026, at 01:31, Bradley Morgan wrote:
> compat_sysinfo() checks whether totalram or totalswap is too big for the
> compat struct and scales the values down to fit, but the loop stops as
> soon as s.mem_unit reaches PAGE_SIZE.  That caps the compression at one
> page, 12 bits with 4K pages.  On a 64 bit kernel do_sysinfo() always
> hands it values in bytes with mem_unit == 1, its own overflow math runs
> on unsigned long so it does not bail until 16 EiB, so the loop runs its
> 12 turns, the values come back out as a page count, and anything past
> 2^32 pages still does not fit.  Storing that into the u32 field then
> just drops the top bits.
>
> So a 32 bit process on a box past 2^32 pages gets a wrapped number
> instead of a coarse one, with 4K pages that starts at 16 TiB:
>
>   ram      old                  new
>   8 TiB    2147483648 x 4096    unchanged
>   16 TiB   0 x 4096             2147483648 x 8192
>   17 TiB   268435456 x 4096     2281701376 x 8192
>   32 TiB   0 x 4096             2147483648 x 16384
>
> The 17 TiB row is the nasty one, a 32 bit process is told the box has
> 1 TiB of ram.  Keep scaling until the values fit instead of until the
> unit reaches a page, the unit then says how coarse the number is, which
> is what mem_unit is for.
>
> Nothing that works today changes.  I ran both loops side by side over
> ram from 1 MiB to 2^56 with 4K and 64K pages, they disagree on exactly
> the inputs where the old one dropped bits and agree on every one it got
> right.
>
> Signed-off-by: Bradley Morgan <brads@mainlining.org>

Makes sense, thanks for the fix!

Reviewed-by: Arnd Bergmann <arnd@arndb.de>

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

* Re: [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit
  2026-11-05  7:25   ` Arnd Bergmann
@ 2026-11-05 16:47     ` Bradley Morgan
  2026-11-05 16:52       ` Arnd Bergmann
  0 siblings, 1 reply; 7+ messages in thread
From: Bradley Morgan @ 2026-11-05 16:47 UTC (permalink / raw)
  To: Arnd Bergmann, Andrew Morton; +Cc: linux-kernel

On 5 November 2026 07:25:22 GMT, Arnd Bergmann <arnd@arndb.de> wrote:
>On Thu, Nov 5, 2026, at 01:31, Bradley Morgan wrote:
>> compat_sysinfo() checks whether totalram or totalswap is too big for the
>> compat struct and scales the values down to fit, but the loop stops as
>> soon as s.mem_unit reaches PAGE_SIZE.  That caps the compression at one
>> page, 12 bits with 4K pages.  On a 64 bit kernel do_sysinfo() always
>> hands it values in bytes with mem_unit == 1, its own overflow math runs
>> on unsigned long so it does not bail until 16 EiB, so the loop runs its
>> 12 turns, the values come back out as a page count, and anything past
>> 2^32 pages still does not fit.  Storing that into the u32 field then
>> just drops the top bits.
>>
>> So a 32 bit process on a box past 2^32 pages gets a wrapped number
>> instead of a coarse one, with 4K pages that starts at 16 TiB:
>>
>>   ram      old                  new
>>   8 TiB    2147483648 x 4096    unchanged
>>   16 TiB   0 x 4096             2147483648 x 8192
>>   17 TiB   268435456 x 4096     2281701376 x 8192
>>   32 TiB   0 x 4096             2147483648 x 16384
>>
>> The 17 TiB row is the nasty one, a 32 bit process is told the box has
>> 1 TiB of ram.  Keep scaling until the values fit instead of until the
>> unit reaches a page, the unit then says how coarse the number is, which
>> is what mem_unit is for.
>>
>> Nothing that works today changes.  I ran both loops side by side over
>> ram from 1 MiB to 2^56 with 4K and 64K pages, they disagree on exactly
>> the inputs where the old one dropped bits and agree on every one it got
>> right.
>>
>> Signed-off-by: Bradley Morgan <brads@mainlining.org>
>
>Makes sense, thanks for the fix!
>
>Reviewed-by: Arnd Bergmann <arnd@arndb.de>


Do you want to take this or do you want Andrew to?

--- Thanks!
https://lore.kernel.org/all/EE579805-42F2-4C58-B752-F28779EEB717@grrlz.net/

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

* Re: [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit
  2026-11-05 16:47     ` Bradley Morgan
@ 2026-11-05 16:52       ` Arnd Bergmann
  0 siblings, 0 replies; 7+ messages in thread
From: Arnd Bergmann @ 2026-11-05 16:52 UTC (permalink / raw)
  To: Bradley Morgan, Andrew Morton; +Cc: linux-kernel

On Thu, Nov 5, 2026, at 17:47, Bradley Morgan wrote:
> On 5 November 2026 07:25:22 GMT, Arnd Bergmann <arnd@arndb.de> wrote:
>>On Thu, Nov 5, 2026, at 01:31, Bradley Morgan wrote:

>>>
>>> Signed-off-by: Bradley Morgan <brads@mainlining.org>
>>
>>Makes sense, thanks for the fix!
>>
>>Reviewed-by: Arnd Bergmann <arnd@arndb.de>
>
>
> Do you want to take this or do you want Andrew to?

I don't usually merge patches for syscalls, I think it would be
best if Andrew can take it.

    Arnd

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

end of thread, other threads:[~2026-11-05 16:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-05  0:31 [PATCH 0/3] two small fixes out of kernel/sys.c Bradley Morgan
2026-11-05  0:31 ` [PATCH 1/3] tools: pass the unused prctl() args explicitly for PR_FUTEX_HASH Bradley Morgan
2026-11-05  0:31 ` [PATCH 2/3] futex: reject the unused PR_FUTEX_HASH arguments Bradley Morgan
2026-11-05  0:31 ` [PATCH 3/3] compat: make compat_sysinfo() scale far enough to actually fit Bradley Morgan
2026-11-05  7:25   ` Arnd Bergmann
2026-11-05 16:47     ` Bradley Morgan
2026-11-05 16:52       ` Arnd Bergmann

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

all inboxes | Powered by JetHome®