[PATCH v3] x32: Implement prctl in assembly

"H.J. Lu" <[email protected]> Tue, 9 Dec 2025 06:41:28 +0800
Newsgroups org.kernel.vger.util-linux
Message-ID <CAMe9rOpkDi3Pq+vexnU8aP0f1xpcHRAAiwAR9CN7Ovh=3B3m9g@mail.gmail.com>
On Mon, Dec 8, 2025 at 10:25 PM Florian Weimer <[email protected]> wrote:
>
> * Adhemerval Zanella Netto:
>
> > On 08/12/25 06:09, Florian Weimer wrote:
> >> * H. J. Lu:
> >>
> >>> On Mon, Dec 8, 2025 at 4:11 PM Florian Weimer <[email protected]> wrote:
> >>>>
> >>>> * H. J. Lu:
> >>>>> Here is the v2 patch to implement prctl in assembly for x32.
> >>>>>
> >>>>> Since the variadic prctl function takes at most 5 integer arguments which
> >>>>> are passed in the same integer registers on x32 as the function with 5
> >>>>> integer arguments, we can use assembly for prctl.  Since upper 32-bits in
> >>>>> the last 4 arguments of prctl must be cleared to match the x32 prctl
> >>>>> syscall interface where the last 4 arguments are unsigned 64 bit longs,
> >>>>> implement prctl in assembly to clear upper 32-bits in the last 4 arguments
> >>>>> and add a test to verify it.
> >>>>
> >>>> What's the advantage of the assembler implementation over the C
> >>>> implementation?  I'm missing the context for this change.
> >>>>
> >>>
> >>> It is inspired by
> >>>
> >>> commit 6a04404521ac4119ae36827eeb288ea84eee7cf6
> >>> Author: Florian Weimer <[email protected]>
> >>> Date:   Sat Feb 17 09:17:04 2024 +0100
> >>>
> >>>     Linux: Switch back to assembly syscall wrapper for prctl (bug 29770)
> >>
> >> The justification for that does not apply to x32, though, because prctl
> >> doesn't take floating point arguments.  I don't have a strong opinion,
> >> the C and assembler versions are of similar complexity.
> >
> > The main justification is UB to va_args *all* the arguments without taking
> > in the consideration which option is passed.  If x32 requires additional
> > argument handling to clear the upper 32-bits, there is no advantage of
> > using the assembly wrapper.
>
> I'm okay with making this change to avoid UB.
>
> Patch looks okay to me.
>
> Reviewed-by: Florian Weimer <[email protected]>
>
> Minor nit:
>
> +weak_alias (__prctl, __prctl_time64)
> +hidden_weak (__prctl_time64)
>
> This isn't necessary because there is no __prctl_time64 on x32.

Fixed in the v3 patch I am checking in.

Thanks.

-- 
H.J.
v3-0001-x32-Implement-prctl-in-assembly.patch (application/x-patch, 5.6 KB)
From 24cf75e8d7ab735f9a06286e228466c0f33c7982 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Sun, 7 Dec 2025 11:33:33 +0800
Subject: [PATCH v3] x32: Implement prctl in assembly

Since the variadic prctl function takes at most 5 integer arguments which
are passed in the same integer registers on x32 as the function with 5
integer arguments, we can use assembly for prctl.  Since upper 32-bits in
the last 4 arguments of pcrtl must be cleared to match the x32 prctl
syscall interface where the last 4 arguments are unsigned 64 bit longs,
implement prctl in assembly to clear upper 32-bits in the last 4 arguments
and add a test to verify it.

Signed-off-by: H.J. Lu <[email protected]>
Reviewed-by: Florian Weimer <[email protected]>
---
 sysdeps/unix/sysv/linux/x86_64/x32/Makefile   |  6 ++
 .../linux/x86_64/x32/{prctl.c => prctl.S}     | 35 +++++------
 .../sysv/linux/x86_64/x32/tst-prctl-x32.c     | 63 +++++++++++++++++++
 3 files changed, 84 insertions(+), 20 deletions(-)
 rename sysdeps/unix/sysv/linux/x86_64/x32/{prctl.c => prctl.S} (50%)
 create mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/tst-prctl-x32.c

diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/Makefile b/sysdeps/unix/sysv/linux/x86_64/x32/Makefile
index 16b768d8ba..004f449883 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/Makefile
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/Makefile
@@ -3,6 +3,12 @@ default-abi := x32
 
 ifeq ($(subdir),misc)
 sysdep_routines += arch_prctl
+
+tests += \
+  tst-prctl-x32 \
+# tests
+
+CFLAGS-tst-prctl-x32.c += $(no-stack-protector)
 endif
 
 ifeq ($(subdir),conform)
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/prctl.c b/sysdeps/unix/sysv/linux/x86_64/x32/prctl.S
similarity index 50%
rename from sysdeps/unix/sysv/linux/x86_64/x32/prctl.c
rename to sysdeps/unix/sysv/linux/x86_64/x32/prctl.S
index 714fd28837..827101e8aa 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/prctl.c
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/prctl.S
@@ -1,5 +1,5 @@
-/* prctl - Linux specific syscall.  x86-64 x32 version.
-   Copyright (C) 2020-2025 Free Software Foundation, Inc.
+/* The prctl system call.  Linux/x32 version.
+   Copyright (C) 2025 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -17,26 +17,21 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <sysdep.h>
-#include <stdarg.h>
-#include <sys/prctl.h>
 
-/* Unconditionally read all potential arguments.  This may pass
-   garbage values to the kernel, but avoids the need for teaching
-   glibc the argument counts of individual options (including ones
-   that are added to the kernel in the future).  */
+/* Clear upper 32-bits in the last 4 arguments.  Since the first argument
+   of prctl is int, leave it alone.  */
+#undef	DO_CALL
+#define DO_CALL(syscall_name, args, ulong_arg_1, ulong_arg_2) \
+  movl %esi, %esi;			\
+  movl %edx, %edx;			\
+  movl %ecx, %r10d;			\
+  movl %r8d, %r8d;			\
+  movl $SYS_ify (syscall_name), %eax;	\
+  syscall;
 
-int
-__prctl (int option, ...)
-{
-  va_list arg;
-  va_start (arg, option);
-  unsigned long int arg2 = va_arg (arg, unsigned long int);
-  unsigned long int arg3 = va_arg (arg, unsigned long int);
-  unsigned long int arg4 = va_arg (arg, unsigned long int);
-  unsigned long int arg5 = va_arg (arg, unsigned long int);
-  va_end (arg);
-  return INLINE_SYSCALL_CALL (prctl, option, arg2, arg3, arg4, arg5);
-}
+PSEUDO (__prctl, prctl, 5)
+	ret
+PSEUDO_END (__prctl)
 
 libc_hidden_def (__prctl)
 weak_alias (__prctl, prctl)
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/tst-prctl-x32.c b/sysdeps/unix/sysv/linux/x86_64/x32/tst-prctl-x32.c
new file mode 100644
index 0000000000..295b09e364
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/tst-prctl-x32.c
@@ -0,0 +1,63 @@
+/* Smoke test for prctl.
+   Copyright (C) 2021-2025 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdint.h>
+#include <sys/prctl.h>
+#include <support/check.h>
+
+/* On x32, when parameters are passed in 64-bit registers, only the lower
+   32 bits are used and the upper 32 bits must be cleared.  */
+typedef union
+{
+  struct
+    {
+      union
+	{
+	  const char *ptr;
+	  int i1;
+	};
+      int i2;
+    } s;
+  long long ll;
+} parameter_t;
+
+static int
+__attribute__ ((noipa))
+do_prctl (int op, long long arg1, long long arg2, long long arg3,
+	  long long arg4)
+{
+  return prctl (op, arg1, arg2, arg3, arg4);
+}
+
+static int
+do_test (void)
+{
+  parameter_t name = { { { "thread name" }, -1 } };
+  parameter_t zero = { { { 0 }, -2 } };
+  TEST_COMPARE (do_prctl (PR_SET_NAME, name.ll, zero.ll, zero.ll,
+			  zero.ll), 0);
+  char buffer[16] = { 0, };
+  name.s.ptr = buffer;
+  TEST_COMPARE (do_prctl (PR_GET_NAME, name.ll, zero.ll, zero.ll,
+			  zero.ll), 0);
+  char expected[16] = "thread name";
+  TEST_COMPARE_BLOB (buffer, sizeof (buffer), expected, sizeof (expected));
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.52.0