public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/3] x32: Properly pass long to syscall [BZ #25810]
@ 2020-04-13 14:25 H.J. Lu
  2020-04-13 14:25 ` [PATCH 1/3] Add SYSCALL_ULONG_ARG_[12] to " H.J. Lu
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 14:25 UTC (permalink / raw)
  To: libc-alpha

X32 has 32-bit long and pointer with 64-bit off_t.  Since x32 psABI
requires that pointers passed in registers must be zero-extended to
64bit, x32 can share many syscall interfaces with LP64.  When a LP64
syscall with long and unsigned long arguments is used for x32, these
arguments must be properly extended to 64-bit.  Otherwise if the upper
32 bits of the register have undefined value, such a syscall will be
rejected by kernel.

For syscalls implemented in assembly codes, 'U' is added to syscall
signature key letters for unsigned long.  SYSCALL_ULONG_ARG_1 and
SYSCALL_ULONG_ARG_2 are passed to syscall-template.S for the first
and the second unsigned long arguments if PSEUDOS_HAVE_4_ARGS is
defined.  They are used by x32 to zero-extend 32-bit arguments to
64 bits.

For x32 INLINE_SYSCALLs, cast

1. pointer to unsigned long (32 bit).
2. 32-bit unsigned integer to unsigned long long (64 bit).
3. 32-bit integer to long long (64 bit).

For

       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

we now generate

   0:	41 f7 c1 ff 0f 00 00 	test   $0xfff,%r9d
   7:	75 1f                	jne    28 <__mmap64+0x28>
   9:	48 63 d2             	movslq %edx,%rdx
   c:	89 f6                	mov    %esi,%esi
   e:	4d 63 c0             	movslq %r8d,%r8
  11:	4c 63 d1             	movslq %ecx,%r10
  14:	b8 09 00 00 40       	mov    $0x40000009,%eax
  19:	0f 05                	syscall

That is

1. addr is unchanged.
2. length is zero-extend to 64 bits.
3. prot is sign-extend to 64 bits.
4. flags is sign-extend to 64 bits.
5. fd is sign-extend to 64 bits.
6. offset is unchanged.

For int arguments, since kernel uses only the lower 32 bits and ignores
the upper 32 bits in 64-bit registers, these work correctly.

Tested on i386, x86-64 and x32 as well as with build-many-glibcs.py.

H.J. Lu (3):
  Add SYSCALL_ULONG_ARG_[12] to pass long to syscall [BZ #25810]
  x32: Properly pass long to syscall [BZ #25810]
  Add a syscall test for [BZ #25810]

 misc/Makefile                               |   2 +-
 misc/tst-syscalls.c                         | 146 ++++++++++++++++++++
 sysdeps/unix/make-syscalls.sh               |  88 ++++++++++++
 sysdeps/unix/syscall-template.S             |  43 +++++-
 sysdeps/unix/syscalls.list                  |   6 +-
 sysdeps/unix/sysv/linux/syscalls.list       |  14 +-
 sysdeps/unix/sysv/linux/x86_64/sysdep.h     |  80 ++++++++---
 sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h |  38 +++++
 8 files changed, 385 insertions(+), 32 deletions(-)
 create mode 100644 misc/tst-syscalls.c

-- 
2.25.2


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

* [PATCH 1/3] Add SYSCALL_ULONG_ARG_[12] to pass long to syscall [BZ #25810]
  2020-04-13 14:25 [PATCH 0/3] x32: Properly pass long to syscall [BZ #25810] H.J. Lu
@ 2020-04-13 14:25 ` H.J. Lu
  2020-04-13 14:25 ` [PATCH 2/3] x32: Properly " H.J. Lu
  2020-04-13 14:25 ` [PATCH 3/3] Add a syscall test for " H.J. Lu
  2 siblings, 0 replies; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 14:25 UTC (permalink / raw)
  To: libc-alpha

X32 has 32-bit long and pointer with 64-bit off_t.  Since x32 psABI
requires that pointers passed in registers must be zero-extended to
64bit, x32 can share many syscall interfaces with LP64.  When a LP64
syscall with long and unsigned long arguments is used for x32, these
arguments must be properly extended to 64-bit.  Otherwise if the upper
32 bits of the register have undefined value, such a syscall will be
rejected by kernel.

For syscalls implemented in assembly codes, 'U' is added to syscall
signature key letters for unsigned long.  SYSCALL_ULONG_ARG_1 and
SYSCALL_ULONG_ARG_2 are passed to syscall-template.S for the first
and the second unsigned long arguments if PSEUDOS_HAVE_4_ARGS is
defined.  They are used by x32 to zero-extend 32-bit arguments to
64 bits.

Tested on i386, x86-64 and x32 as well as with build-many-glibcs.py.
---
 sysdeps/unix/make-syscalls.sh               | 88 +++++++++++++++++++++
 sysdeps/unix/syscall-template.S             | 43 +++++++++-
 sysdeps/unix/syscalls.list                  |  6 +-
 sysdeps/unix/sysv/linux/syscalls.list       | 14 ++--
 sysdeps/unix/sysv/linux/x86_64/sysdep.h     | 70 ++++++++++++----
 sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h | 20 +++++
 6 files changed, 214 insertions(+), 27 deletions(-)

diff --git a/sysdeps/unix/make-syscalls.sh b/sysdeps/unix/make-syscalls.sh
index c07626677f..4a28badea6 100644
--- a/sysdeps/unix/make-syscalls.sh
+++ b/sysdeps/unix/make-syscalls.sh
@@ -30,6 +30,7 @@
 # P: optionally-NULL pointer to typed object (e.g., 3rd argument to sigaction)
 # s: non-NULL string (e.g., 1st arg to open)
 # S: optionally-NULL string (e.g., 1st arg to acct)
+# U: unsigned long
 # v: vararg scalar (e.g., optional 3rd arg to open)
 # V: byte-per-page vector (3rd arg to mincore)
 # W: wait status, optionally-NULL pointer to int (e.g., 2nd arg of wait4)
@@ -184,6 +185,91 @@ while read file srcfile caller syscall args strong weak; do
   ?:?????????) nargs=9;;
   esac
 
+  # Derive the unsigned long arguments from the argument signature
+  ulong_arg_1=0
+  ulong_arg_2=0
+  case $args in
+  ?:U*)
+    ulong_arg_1=1
+    case $args in
+    ?:UU*) ulong_arg_2=2;;
+    ?:U?U*) ulong_arg_2=3;;
+    ?:U??U*) ulong_arg_2=4;;
+    ?:U???U*) ulong_arg_2=5;;
+    ?:U????U*) ulong_arg_2=6;;
+    ?:U?????U*) ulong_arg_2=7;;
+    ?:U??????U*) ulong_arg_2=8;;
+    ?:U???????U) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:?U*)
+    ulong_arg_1=2
+    case $args in
+    ?:?UU*) ulong_arg_2=3;;
+    ?:?U?U*) ulong_arg_2=4;;
+    ?:?U??U*) ulong_arg_2=5;;
+    ?:?U???U*) ulong_arg_2=6;;
+    ?:?U????U*) ulong_arg_2=7;;
+    ?:?U?????U*) ulong_arg_2=8;;
+    ?:?U??????U) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:??U*)
+    ulong_arg_1=3
+    case $args in
+    ?:??UU*) ulong_arg_2=4;;
+    ?:??U?U*) ulong_arg_2=5;;
+    ?:??U??U*) ulong_arg_2=6;;
+    ?:??U???U*) ulong_arg_2=7;;
+    ?:??U????U*) ulong_arg_2=8;;
+    ?:??U?????U) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:???U*)
+    ulong_arg_1=4
+    case $args in
+    ?:???UU*) ulong_arg_2=5;;
+    ?:???U?U*) ulong_arg_2=6;;
+    ?:???U??U*) ulong_arg_2=7;;
+    ?:???U???U*) ulong_arg_2=8;;
+    ?:???U????U) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:????U*)
+    ulong_arg_1=5
+    case $args in
+    ?:????UU*) ulong_arg_2=6;;
+    ?:????U?U*) ulong_arg_2=7;;
+    ?:????U??U*) ulong_arg_2=8;;
+    ?:????U???U) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:?????U*)
+    ulong_arg_1=6
+    case $args in
+    ?:?????UU*) ulong_arg_2=7;;
+    ?:?????U?U*) ulong_arg_2=8;;
+    ?:?????U??U) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:??????U*)
+    ulong_arg_1=7
+    case $args in
+    ?:??????UU*) ulong_arg_2=8;;
+    ?:??????U?U) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:???????U*)
+    ulong_arg_1=8
+    case $args in
+    ?:??????UU) ulong_arg_2=9;;
+    esac
+    ;;
+  ?:????????U)
+    ulong_arg_1=9
+    ;;
+  esac
+
   # Make sure only the first syscall rule is used, if multiple dirs
   # define the same syscall.
   echo ''
@@ -245,6 +331,8 @@ while read file srcfile caller syscall args strong weak; do
 	\$(make-target-directory)
 	(echo '#define SYSCALL_NAME $syscall'; \\
 	 echo '#define SYSCALL_NARGS $nargs'; \\
+	 echo '#define SYSCALL_ULONG_ARG_1 $ulong_arg_1'; \\
+	 echo '#define SYSCALL_ULONG_ARG_2 $ulong_arg_2'; \\
 	 echo '#define SYSCALL_SYMBOL $strong'; \\
 	 echo '#define SYSCALL_NOERRNO $noerrno'; \\
 	 echo '#define SYSCALL_ERRVAL $errval'; \\
diff --git a/sysdeps/unix/syscall-template.S b/sysdeps/unix/syscall-template.S
index cf6c7a58fb..0824a3c61e 100644
--- a/sysdeps/unix/syscall-template.S
+++ b/sysdeps/unix/syscall-template.S
@@ -25,6 +25,10 @@
    defining a few macros:
 	SYSCALL_NAME		syscall name
 	SYSCALL_NARGS		number of arguments this call takes
+	SYSCALL_ULONG_ARG_1	the first unsigned long argument this
+				call takes
+	SYSCALL_ULONG_ARG_2	the second unsigned long argument this
+				call takes
 	SYSCALL_SYMBOL		primary symbol name
 	SYSCALL_NOERRNO		1 to define a no-errno version (see below)
 	SYSCALL_ERRVAL		1 to define an error-value version (see below)
@@ -44,9 +48,27 @@
 /* This indirection is needed so that SYMBOL gets macro-expanded.  */
 #define syscall_hidden_def(SYMBOL)		hidden_def (SYMBOL)
 
-#define T_PSEUDO(SYMBOL, NAME, N)		PSEUDO (SYMBOL, NAME, N)
-#define T_PSEUDO_NOERRNO(SYMBOL, NAME, N)	PSEUDO_NOERRNO (SYMBOL, NAME, N)
-#define T_PSEUDO_ERRVAL(SYMBOL, NAME, N)	PSEUDO_ERRVAL (SYMBOL, NAME, N)
+/* If PSEUDOS_HAVE_4_ARGS is defined, PSEUDO macros have 4 arguments.  */
+#ifndef PSEUDOS_HAVE_4_ARGS
+# undef SYSCALL_ULONG_ARG_1
+# define SYSCALL_ULONG_ARG_1 0
+#endif
+
+#if SYSCALL_ULONG_ARG_1
+# define T_PSEUDO(SYMBOL, NAME, N, U1, U2) \
+  PSEUDO (SYMBOL, NAME, N, U1, U2)
+# define T_PSEUDO_NOERRNO(SYMBOL, NAME, N, U1, U2) \
+  PSEUDO_NOERRNO (SYMBOL, NAME, N, U1, U2)
+# define T_PSEUDO_ERRVAL(SYMBOL, NAME, N, U1, U2) \
+  PSEUDO_ERRVAL (SYMBOL, NAME, N, U1, U2)
+#else
+# define T_PSEUDO(SYMBOL, NAME, N) \
+  PSEUDO (SYMBOL, NAME, N)
+# define T_PSEUDO_NOERRNO(SYMBOL, NAME, N) \
+  PSEUDO_NOERRNO (SYMBOL, NAME, N)
+# define T_PSEUDO_ERRVAL(SYMBOL, NAME, N) \
+  PSEUDO_ERRVAL (SYMBOL, NAME, N)
+#endif
 #define T_PSEUDO_END(SYMBOL)			PSEUDO_END (SYMBOL)
 #define T_PSEUDO_END_NOERRNO(SYMBOL)		PSEUDO_END_NOERRNO (SYMBOL)
 #define T_PSEUDO_END_ERRVAL(SYMBOL)		PSEUDO_END_ERRVAL (SYMBOL)
@@ -56,7 +78,12 @@
 /* This kind of system call stub never returns an error.
    We return the return value register to the caller unexamined.  */
 
+# if SYSCALL_ULONG_ARG_1
+T_PSEUDO_NOERRNO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS,
+		  SYSCALL_ULONG_ARG_1, SYSCALL_ULONG_ARG_2)
+# else
 T_PSEUDO_NOERRNO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
+# endif
 	ret_NOERRNO
 T_PSEUDO_END_NOERRNO (SYSCALL_SYMBOL)
 
@@ -66,7 +93,12 @@ T_PSEUDO_END_NOERRNO (SYSCALL_SYMBOL)
    value, or zero for success.  We may massage the kernel's return value
    to meet that ABI, but we never set errno here.  */
 
+# if SYSCALL_ULONG_ARG_1
+T_PSEUDO_ERRVAL (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS,
+		 SYSCALL_ULONG_ARG_1, SYSCALL_ULONG_ARG_2)
+# else
 T_PSEUDO_ERRVAL (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
+# endif
 	ret_ERRVAL
 T_PSEUDO_END_ERRVAL (SYSCALL_SYMBOL)
 
@@ -75,7 +107,12 @@ T_PSEUDO_END_ERRVAL (SYSCALL_SYMBOL)
 /* This is a "normal" system call stub: if there is an error,
    it returns -1 and sets errno.  */
 
+# if SYSCALL_ULONG_ARG_1
+T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS,
+	  SYSCALL_ULONG_ARG_1, SYSCALL_ULONG_ARG_2)
+# else
 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
+# endif
 	ret
 T_PSEUDO_END (SYSCALL_SYMBOL)
 
diff --git a/sysdeps/unix/syscalls.list b/sysdeps/unix/syscalls.list
index 01c4a0e6b1..e63a8b9d23 100644
--- a/sysdeps/unix/syscalls.list
+++ b/sysdeps/unix/syscalls.list
@@ -37,11 +37,11 @@ kill		-	kill		i:ii	__kill		kill
 link		-	link		i:ss	__link		link
 listen		-	listen		i:ii	__listen	listen
 lseek		-	lseek		i:iii	__libc_lseek	__lseek lseek
-madvise		-	madvise		i:pii	__madvise	madvise
+madvise		-	madvise		i:pUi	__madvise	madvise
 mkdir		-	mkdir		i:si	__mkdir		mkdir
 mmap		-	mmap		b:aniiii __mmap		mmap
-mprotect	-	mprotect	i:aii	__mprotect	mprotect
-munmap		-	munmap		i:ai	__munmap	munmap
+mprotect	-	mprotect	i:aUi	__mprotect	mprotect
+munmap		-	munmap		i:aU	__munmap	munmap
 open		-	open		Ci:siv	__libc_open __open open
 profil		-	profil		i:piii	__profil	profil
 ptrace		-	ptrace		i:iiii	ptrace
diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list
index e40f993495..4d4af84f9f 100644
--- a/sysdeps/unix/sysv/linux/syscalls.list
+++ b/sysdeps/unix/sysv/linux/syscalls.list
@@ -32,12 +32,12 @@ ioperm		-	ioperm		i:iii	ioperm
 iopl		-	iopl		i:i	iopl
 klogctl		EXTRA	syslog		i:isi	klogctl
 lchown		-	lchown		i:sii	__lchown	lchown
-mincore		-	mincore		i:anV	mincore
-mlock		-	mlock		i:bn	mlock
+mincore		-	mincore		i:aUV	mincore
+mlock		-	mlock		i:bU	mlock
 mlockall	-	mlockall	i:i	mlockall
-mount		EXTRA	mount		i:sssip	__mount	mount
-mremap		EXTRA	mremap		b:ainip	__mremap	mremap
-munlock		-	munlock		i:ai	munlock
+mount		EXTRA	mount		i:sssUp	__mount	mount
+mremap		EXTRA	mremap		b:aUUip	__mremap	mremap
+munlock		-	munlock		i:aU	munlock
 munlockall	-	munlockall	i:	munlockall
 nfsservctl	EXTRA	nfsservctl	i:ipp	__compat_nfsservctl	nfsservctl@GLIBC_2.0:GLIBC_2.28
 pipe		-	pipe		i:f	__pipe		pipe
@@ -46,7 +46,7 @@ pivot_root	EXTRA	pivot_root	i:ss	pivot_root
 prctl		EXTRA	prctl		i:iiiii	__prctl		prctl
 query_module	EXTRA	query_module	i:sipip	__compat_query_module	query_module@GLIBC_2.0:GLIBC_2.23
 quotactl	EXTRA	quotactl	i:isip	quotactl
-remap_file_pages -	remap_file_pages i:piiii	__remap_file_pages remap_file_pages
+remap_file_pages -	remap_file_pages i:pUiUi	__remap_file_pages remap_file_pages
 sched_getp	-	sched_getparam	i:ip	__sched_getparam	sched_getparam
 sched_gets	-	sched_getscheduler	i:i	__sched_getscheduler	sched_getscheduler
 sched_primax	-	sched_get_priority_max	i:i	__sched_get_priority_max	sched_get_priority_max
@@ -54,7 +54,7 @@ sched_primin	-	sched_get_priority_min	i:i	__sched_get_priority_min	sched_get_pri
 sched_setp	-	sched_setparam	i:ip	__sched_setparam	sched_setparam
 sched_sets	-	sched_setscheduler	i:iip	__sched_setscheduler	sched_setscheduler
 sched_yield	-	sched_yield	i:	__sched_yield	sched_yield
-sendfile	-	sendfile	i:iipi	sendfile
+sendfile	-	sendfile	i:iipU	sendfile
 sendfile64	-	sendfile64	i:iipi	sendfile64
 setfsgid	EXTRA	setfsgid	i:i	setfsgid
 setfsuid	EXTRA	setfsuid	i:i	setfsuid
diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
index 24d8b8ec20..06e39212ee 100644
--- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
+++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
@@ -57,13 +57,30 @@
 #  define SYSCALL_ERROR_LABEL syscall_error
 # endif
 
+/* PSEUDO macros have 4 arguments.  */
+# define PSEUDOS_HAVE_4_ARGS 1
+
+# ifndef SYSCALL_ULONG_ARG_1
+#  define SYSCALL_ULONG_ARG_1 0
+#  define SYSCALL_ULONG_ARG_2 0
+# endif
+
 # undef	PSEUDO
-# define PSEUDO(name, syscall_name, args)				      \
-  .text;								      \
-  ENTRY (name)								      \
-    DO_CALL (syscall_name, args);					      \
-    cmpq $-4095, %rax;							      \
+# if SYSCALL_ULONG_ARG_1
+#  define PSEUDO(name, syscall_name, args, ulong_arg_1, ulong_arg_2) \
+  .text;							      \
+  ENTRY (name)							      \
+    DO_CALL (syscall_name, args, ulong_arg_1, ulong_arg_2);	      \
+    cmpq $-4095, %rax;						      \
     jae SYSCALL_ERROR_LABEL
+# else
+#  define PSEUDO(name, syscall_name, args) \
+  .text;							      \
+  ENTRY (name)							      \
+    DO_CALL (syscall_name, args, 0, 0);				      \
+    cmpq $-4095, %rax;						      \
+    jae SYSCALL_ERROR_LABEL
+# endif
 
 # undef	PSEUDO_END
 # define PSEUDO_END(name)						      \
@@ -71,10 +88,17 @@
   END (name)
 
 # undef	PSEUDO_NOERRNO
-# define PSEUDO_NOERRNO(name, syscall_name, args) \
-  .text;								      \
-  ENTRY (name)								      \
-    DO_CALL (syscall_name, args)
+# if SYSCALL_ULONG_ARG_1
+#  define PSEUDO_NOERRNO(name, syscall_name, args, ulong_arg_1, ulong_arg_2) \
+  .text;							      \
+  ENTRY (name)							      \
+    DO_CALL (syscall_name, args, ulong_arg_1, ulong_arg_2)
+# else
+#  define PSEUDO_NOERRNO(name, syscall_name, args) \
+  .text;							      \
+  ENTRY (name)							      \
+    DO_CALL (syscall_name, args, 0, 0)
+# endif
 
 # undef	PSEUDO_END_NOERRNO
 # define PSEUDO_END_NOERRNO(name) \
@@ -83,11 +107,19 @@
 # define ret_NOERRNO ret
 
 # undef	PSEUDO_ERRVAL
-# define PSEUDO_ERRVAL(name, syscall_name, args) \
-  .text;								      \
-  ENTRY (name)								      \
-    DO_CALL (syscall_name, args);					      \
+# if SYSCALL_ULONG_ARG_1
+#  define PSEUDO_ERRVAL(name, syscall_name, args, ulong_arg_1, ulong_arg_2) \
+  .text;							\
+  ENTRY (name)							\
+    DO_CALL (syscall_name, args, ulong_arg_1, ulong_arg_2);	\
+    negq %rax
+# else
+#  define PSEUDO_ERRVAL(name, syscall_name, args) \
+  .text;							\
+  ENTRY (name)							\
+    DO_CALL (syscall_name, args, 0, 0);				\
     negq %rax
+# endif
 
 # undef	PSEUDO_END_ERRVAL
 # define PSEUDO_END_ERRVAL(name) \
@@ -159,8 +191,10 @@
     Syscalls of more than 6 arguments are not supported.  */
 
 # undef	DO_CALL
-# define DO_CALL(syscall_name, args)		\
+# define DO_CALL(syscall_name, args, ulong_arg_1, ulong_arg_2) \
     DOARGS_##args				\
+    ZERO_EXTEND_##ulong_arg_1			\
+    ZERO_EXTEND_##ulong_arg_2			\
     movl $SYS_ify (syscall_name), %eax;		\
     syscall;
 
@@ -172,6 +206,14 @@
 # define DOARGS_5 DOARGS_4
 # define DOARGS_6 DOARGS_5
 
+# define ZERO_EXTEND_0 /* nothing */
+# define ZERO_EXTEND_1 /* nothing */
+# define ZERO_EXTEND_2 /* nothing */
+# define ZERO_EXTEND_3 /* nothing */
+# define ZERO_EXTEND_4 /* nothing */
+# define ZERO_EXTEND_5 /* nothing */
+# define ZERO_EXTEND_6 /* nothing */
+
 #else	/* !__ASSEMBLER__ */
 
 /* Registers clobbered by syscall.  */
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
index 5bf9eed80b..2b5cf0c0ea 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
@@ -26,4 +26,24 @@
 #undef LO_HI_LONG
 #define LO_HI_LONG(val) (val)
 
+#ifdef __ASSEMBLER__
+# undef ZERO_EXTEND_1
+# define ZERO_EXTEND_1 movl %edi, %edi;
+# undef ZERO_EXTEND_2
+# define ZERO_EXTEND_2 movl %esi, %esi;
+# undef ZERO_EXTEND_3
+# define ZERO_EXTEND_3 movl %edx, %edx;
+# if SYSCALL_ULONG_ARG_1 == 4 || SYSCALL_ULONG_ARG_2 == 4
+#  undef DOARGS_4
+#  define DOARGS_4 movl %ecx, %r10d;
+# else
+#  undef ZERO_EXTEND_4
+#  define ZERO_EXTEND_4 movl %r10d, %r10d;
+# endif
+# undef ZERO_EXTEND_5
+# define ZERO_EXTEND_5 movl %r8d, %r8d;
+# undef ZERO_EXTEND_6
+# define ZERO_EXTEND_6 movl %r9d, %r9d;
+#endif	/* __ASSEMBLER__ */
+
 #endif /* linux/x86_64/x32/sysdep.h */
-- 
2.25.2


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

* [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 14:25 [PATCH 0/3] x32: Properly pass long to syscall [BZ #25810] H.J. Lu
  2020-04-13 14:25 ` [PATCH 1/3] Add SYSCALL_ULONG_ARG_[12] to " H.J. Lu
@ 2020-04-13 14:25 ` H.J. Lu
  2020-04-13 14:43   ` Florian Weimer
  2020-04-13 14:25 ` [PATCH 3/3] Add a syscall test for " H.J. Lu
  2 siblings, 1 reply; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 14:25 UTC (permalink / raw)
  To: libc-alpha

X32 has 32-bit long and pointer with 64-bit off_t.  Since x32 psABI
requires that pointers passed in registers must be zero-extended to
64bit, x32 can share many syscall interfaces with LP64.  When a LP64
syscall with long and unsigned long arguments is used for x32, these
arguments must be properly extended to 64-bit.  Otherwise if the upper
32 bits of the register have undefined value, such a syscall will be
rejected by kernel.

For x32 INLINE_SYSCALLs, cast

1. pointer to unsigned long (32 bit).
2. 32-bit unsigned integer to unsigned long long (64 bit).
3. 32-bit integer to long long (64 bit).

For

       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

we now generate

   0:	41 f7 c1 ff 0f 00 00 	test   $0xfff,%r9d
   7:	75 1f                	jne    28 <__mmap64+0x28>
   9:	48 63 d2             	movslq %edx,%rdx
   c:	89 f6                	mov    %esi,%esi
   e:	4d 63 c0             	movslq %r8d,%r8
  11:	4c 63 d1             	movslq %ecx,%r10
  14:	b8 09 00 00 40       	mov    $0x40000009,%eax
  19:	0f 05                	syscall

That is

1. addr is unchanged.
2. length is zero-extend to 64 bits.
3. prot is sign-extend to 64 bits.
4. flags is sign-extend to 64 bits.
5. fd is sign-extend to 64 bits.
6. offset is unchanged.

For int arguments, since kernel uses only the lower 32 bits and ignores
the upper 32 bits in 64-bit registers, these work correctly.

Tested on x86-64 and x32.  There are no code changes on x86-64.
---
 sysdeps/unix/sysv/linux/x86_64/sysdep.h     | 10 ++++++----
 sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h | 18 ++++++++++++++++++
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
index 06e39212ee..93dad97c8f 100644
--- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
+++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
@@ -219,12 +219,14 @@
 /* Registers clobbered by syscall.  */
 # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
 
-/* Create a variable 'name' based on type 'X' to avoid explicit types.
-   This is mainly used set use 64-bits arguments in x32.   */
-#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
+/* This also works when X is an array.  */
+#define TYPEFY1(X) __typeof__ ((X) - (X))
 /* Explicit cast the argument to avoid integer from pointer warning on
    x32.  */
-#define ARGIFY(X) ((__typeof__ ((X) - (X))) (X))
+#define ARGIFY(X) ((TYPEFY1 (X)) (X))
+/* Create a variable 'name' based on type 'X' to avoid explicit types.
+   This is mainly used set use 64-bits arguments in x32.   */
+#define TYPEFY(X, name) __typeof__ (ARGIFY (X) - ARGIFY (X)) name
 
 #undef INTERNAL_SYSCALL
 #define INTERNAL_SYSCALL(name, nr, args...)				\
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
index 2b5cf0c0ea..f3bc7e89df 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
@@ -44,6 +44,24 @@
 # define ZERO_EXTEND_5 movl %r8d, %r8d;
 # undef ZERO_EXTEND_6
 # define ZERO_EXTEND_6 movl %r9d, %r9d;
+#else /*! __ASSEMBLER__ */
+# undef ARGIFY
+/* For x32, cast
+   1. pointer to unsigned long (32 bit).
+   2. 32-bit unsigned integer to unsigned long long (64 bit).
+   3. 32-bit integer to long long (64 bit).
+ */
+# define ARGIFY(X) \
+ ({									\
+    _Pragma ("GCC diagnostic push");					\
+    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");	\
+    (__builtin_classify_type (X) == 5					\
+     ? (unsigned long) (X)						\
+     : ((sizeof (X) <= 4 && (TYPEFY1 (X)) 0 < (TYPEFY1 (X)) -1)		\
+	? (unsigned long long) (X)					\
+	: (long long) (X)));						\
+    _Pragma ("GCC diagnostic pop");					\
+  })
 #endif	/* __ASSEMBLER__ */
 
 #endif /* linux/x86_64/x32/sysdep.h */
-- 
2.25.2


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

* [PATCH 3/3] Add a syscall test for [BZ #25810]
  2020-04-13 14:25 [PATCH 0/3] x32: Properly pass long to syscall [BZ #25810] H.J. Lu
  2020-04-13 14:25 ` [PATCH 1/3] Add SYSCALL_ULONG_ARG_[12] to " H.J. Lu
  2020-04-13 14:25 ` [PATCH 2/3] x32: Properly " H.J. Lu
@ 2020-04-13 14:25 ` H.J. Lu
  2 siblings, 0 replies; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 14:25 UTC (permalink / raw)
  To: libc-alpha

Add a test to pass 64-bit long arguments to syscall with undefined upper
32 bits on ILP32 systems.

Tested on i386, x86-64 and x32 as well as with build-many-glibcs.py.
---
 misc/Makefile       |   2 +-
 misc/tst-syscalls.c | 146 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 147 insertions(+), 1 deletion(-)
 create mode 100644 misc/tst-syscalls.c

diff --git a/misc/Makefile b/misc/Makefile
index b8fed5783d..67c5237f97 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -87,7 +87,7 @@ tests := tst-dirname tst-tsearch tst-fdset tst-mntent tst-hsearch \
 	 tst-preadvwritev tst-preadvwritev64 tst-makedev tst-empty \
 	 tst-preadvwritev2 tst-preadvwritev64v2 tst-warn-wide \
 	 tst-ldbl-warn tst-ldbl-error tst-dbl-efgcvt tst-ldbl-efgcvt \
-	 tst-mntent-autofs
+	 tst-mntent-autofs tst-syscalls
 
 # Tests which need libdl.
 ifeq (yes,$(build-shared))
diff --git a/misc/tst-syscalls.c b/misc/tst-syscalls.c
new file mode 100644
index 0000000000..d07f03633b
--- /dev/null
+++ b/misc/tst-syscalls.c
@@ -0,0 +1,146 @@
+/* Test for syscall interfaces.
+   Copyright (C) 2020 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 <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+
+struct Array
+{
+  size_t length;
+  void *ptr;
+};
+
+static int error_count;
+
+__attribute__ ((noclone, noinline))
+struct Array
+allocate (size_t bytes)
+{
+  if (!bytes)
+    return __extension__ (struct Array) {0, 0};
+
+  void *p = mmap (0x0, bytes, PROT_READ | PROT_WRITE,
+		  MAP_PRIVATE | MAP_ANON, -1, 0);
+  if (p == MAP_FAILED)
+    return __extension__ (struct Array) {0, 0};
+
+  return __extension__ (struct Array) {bytes, p};
+}
+
+__attribute__ ((noclone, noinline))
+void
+deallocate (struct Array b)
+{
+  if (b.length && munmap (b.ptr, b.length))
+    {
+      printf ("munmap error: %m\n");
+      error_count++;
+    }
+}
+
+__attribute__ ((noclone, noinline))
+void *
+do_mmap (void *addr, size_t length)
+{
+  return mmap (addr, length, PROT_READ | PROT_WRITE,
+	       MAP_PRIVATE | MAP_ANON, -1, 0);
+}
+
+__attribute__ ((noclone, noinline))
+void *
+reallocate (struct Array b)
+{
+  if (b.length)
+    return do_mmap (b.ptr, b.length);
+  return NULL;
+}
+
+__attribute__ ((noclone, noinline))
+void
+protect (struct Array b)
+{
+  if (b.length)
+    {
+      if (mprotect (b.ptr, b.length,
+		    PROT_READ | PROT_WRITE | PROT_EXEC))
+	{
+	  printf ("mprotect error: %m\n");
+	  error_count++;
+	}
+    }
+}
+
+__attribute__ ((noclone, noinline))
+ssize_t
+do_read (int fd, void *ptr, struct Array b)
+{
+  if (b.length)
+    return read (fd, ptr, b.length);
+  return 0;
+}
+
+__attribute__ ((noclone, noinline))
+ssize_t
+do_write (int fd, void *ptr, struct Array b)
+{
+  if (b.length)
+    return write (fd, ptr, b.length);
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  struct Array array;
+
+  array = allocate (1);
+  protect (array);
+  deallocate (array);
+  void *p = reallocate (array);
+  if (p == MAP_FAILED)
+    {
+      printf ("mmap error: %m\n");
+      error_count++;
+    }
+  array.ptr = p;
+  protect (array);
+  deallocate (array);
+
+  int fd = xopen ("/dev/null", O_RDWR, 0);
+  char buf[2];
+  array.ptr = buf;
+  if (do_read (fd, array.ptr, array) == -1)
+    {
+      printf ("read error: %m\n");
+      error_count++;
+    }
+  if (do_write (fd, array.ptr, array) == -1)
+    {
+      printf ("write error: %m\n");
+      error_count++;
+    }
+  xclose (fd);
+
+  return error_count ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+#include <support/test-driver.c>
-- 
2.25.2


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

* Re: [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 14:25 ` [PATCH 2/3] x32: Properly " H.J. Lu
@ 2020-04-13 14:43   ` Florian Weimer
  2020-04-13 15:03     ` H.J. Lu
  0 siblings, 1 reply; 13+ messages in thread
From: Florian Weimer @ 2020-04-13 14:43 UTC (permalink / raw)
  To: H.J. Lu via Libc-alpha

* H. J. Lu via Libc-alpha:

> diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> index 06e39212ee..93dad97c8f 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> +++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> @@ -219,12 +219,14 @@
>  /* Registers clobbered by syscall.  */
>  # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
>  
> -/* Create a variable 'name' based on type 'X' to avoid explicit types.
> -   This is mainly used set use 64-bits arguments in x32.   */
> -#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
> +/* This also works when X is an array.  */
> +#define TYPEFY1(X) __typeof__ ((X) - (X))

I think the comment is now misleading.  For an array X, (X) - (X) is
of type ptrdiff_t, which is signed, so it triggers sign extension, not
zero extension, in the x32 case.  I think this is the reason why my
proposed construct went astray in your experiments.

Is it really necessary to support arrays for system call arguments?

> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> index 2b5cf0c0ea..f3bc7e89df 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> @@ -44,6 +44,24 @@
>  # define ZERO_EXTEND_5 movl %r8d, %r8d;
>  # undef ZERO_EXTEND_6
>  # define ZERO_EXTEND_6 movl %r9d, %r9d;
> +#else /*! __ASSEMBLER__ */
> +# undef ARGIFY
> +/* For x32, cast
> +   1. pointer to unsigned long (32 bit).
> +   2. 32-bit unsigned integer to unsigned long long (64 bit).
> +   3. 32-bit integer to long long (64 bit).
> + */
> +# define ARGIFY(X) \
> + ({									\
> +    _Pragma ("GCC diagnostic push");					\
> +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");	\
> +    (__builtin_classify_type (X) == 5					\
> +     ? (unsigned long) (X)						\
> +     : ((sizeof (X) <= 4 && (TYPEFY1 (X)) 0 < (TYPEFY1 (X)) -1)		\
> +	? (unsigned long long) (X)					\
> +	: (long long) (X)));						\
> +    _Pragma ("GCC diagnostic pop");					\
> +  })
>  #endif	/* __ASSEMBLER__ */

This should use long int instead long everywhere.
__builtin_classify_type is undocumented.  I'm not sure if its behavior
and the constant 5 is actually stable across GCC versions, but the
powerpc uses it as well, for a slightly difference purpose (to
recognize array arguments, it seems).

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

* Re: [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 14:43   ` Florian Weimer
@ 2020-04-13 15:03     ` H.J. Lu
  2020-04-13 15:17       ` Florian Weimer
  0 siblings, 1 reply; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 15:03 UTC (permalink / raw)
  To: Florian Weimer; +Cc: H.J. Lu via Libc-alpha

On Mon, Apr 13, 2020 at 7:43 AM Florian Weimer <fw@deneb.enyo.de> wrote:
>
> * H. J. Lu via Libc-alpha:
>
> > diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> > index 06e39212ee..93dad97c8f 100644
> > --- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> > +++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> > @@ -219,12 +219,14 @@
> >  /* Registers clobbered by syscall.  */
> >  # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
> >
> > -/* Create a variable 'name' based on type 'X' to avoid explicit types.
> > -   This is mainly used set use 64-bits arguments in x32.   */
> > -#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
> > +/* This also works when X is an array.  */
> > +#define TYPEFY1(X) __typeof__ ((X) - (X))
>
> I think the comment is now misleading.  For an array X, (X) - (X) is
> of type ptrdiff_t, which is signed, so it triggers sign extension, not

I will update the comments.

> zero extension, in the x32 case.  I think this is the reason why my
> proposed construct went astray in your experiments.
>
> Is it really necessary to support arrays for system call arguments?

int
fexecve (int fd, char *const argv[], char *const envp[])
{
  if (fd < 0 || argv == NULL || envp == NULL)
    {
      __set_errno (EINVAL);
      return -1;
    }

#ifdef __NR_execveat
  /* Avoid implicit array coercion in syscall macros.  */
  INLINE_SYSCALL (execveat, 5, fd, "", &argv[0], &envp[0], AT_EMPTY_PATH);

"" is an array.

> > diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> > index 2b5cf0c0ea..f3bc7e89df 100644
> > --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> > +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> > @@ -44,6 +44,24 @@
> >  # define ZERO_EXTEND_5 movl %r8d, %r8d;
> >  # undef ZERO_EXTEND_6
> >  # define ZERO_EXTEND_6 movl %r9d, %r9d;
> > +#else /*! __ASSEMBLER__ */
> > +# undef ARGIFY
> > +/* For x32, cast
> > +   1. pointer to unsigned long (32 bit).
> > +   2. 32-bit unsigned integer to unsigned long long (64 bit).
> > +   3. 32-bit integer to long long (64 bit).
> > + */
> > +# define ARGIFY(X) \
> > + ({                                                                  \
> > +    _Pragma ("GCC diagnostic push");                                 \
> > +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
> > +    (__builtin_classify_type (X) == 5                                        \
> > +     ? (unsigned long) (X)                                           \
> > +     : ((sizeof (X) <= 4 && (TYPEFY1 (X)) 0 < (TYPEFY1 (X)) -1)              \
> > +     ? (unsigned long long) (X)                                      \
> > +     : (long long) (X)));                                            \
> > +    _Pragma ("GCC diagnostic pop");                                  \
> > +  })
> >  #endif       /* __ASSEMBLER__ */
>
> This should use long int instead long everywhere.

I will update.

> __builtin_classify_type is undocumented.  I'm not sure if its behavior
> and the constant 5 is actually stable across GCC versions, but the
> powerpc uses it as well, for a slightly difference purpose (to
> recognize array arguments, it seems).

include/libc-pointer-arith.h has

/* 1 if 'type' is a pointer type, 0 otherwise.  */
# define __pointer_type(type) (__builtin_classify_type ((type) 0) == 5)

If __builtin_classify_type ever changes, glibc won't build correctly.

-- 
H.J.

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

* Re: [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 15:03     ` H.J. Lu
@ 2020-04-13 15:17       ` Florian Weimer
  2020-04-13 16:33         ` V2 " H.J. Lu
  0 siblings, 1 reply; 13+ messages in thread
From: Florian Weimer @ 2020-04-13 15:17 UTC (permalink / raw)
  To: H.J. Lu; +Cc: H.J. Lu via Libc-alpha

* H. J. Lu:

> On Mon, Apr 13, 2020 at 7:43 AM Florian Weimer <fw@deneb.enyo.de> wrote:
>>
>> * H. J. Lu via Libc-alpha:
>>
>> > diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
>> > index 06e39212ee..93dad97c8f 100644
>> > --- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
>> > +++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
>> > @@ -219,12 +219,14 @@
>> >  /* Registers clobbered by syscall.  */
>> >  # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
>> >
>> > -/* Create a variable 'name' based on type 'X' to avoid explicit types.
>> > -   This is mainly used set use 64-bits arguments in x32.   */
>> > -#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
>> > +/* This also works when X is an array.  */
>> > +#define TYPEFY1(X) __typeof__ ((X) - (X))
>>
>> I think the comment is now misleading.  For an array X, (X) - (X) is
>> of type ptrdiff_t, which is signed, so it triggers sign extension, not
>
> I will update the comments.

Thanks.

>> zero extension, in the x32 case.  I think this is the reason why my
>> proposed construct went astray in your experiments.
>>
>> Is it really necessary to support arrays for system call arguments?
>
> int
> fexecve (int fd, char *const argv[], char *const envp[])
> {
>   if (fd < 0 || argv == NULL || envp == NULL)
>     {
>       __set_errno (EINVAL);
>       return -1;
>     }
>
> #ifdef __NR_execveat
>   /* Avoid implicit array coercion in syscall macros.  */
>   INLINE_SYSCALL (execveat, 5, fd, "", &argv[0], &envp[0], AT_EMPTY_PATH);
>
> "" is an array.

If that were the only reason for enforcing pointer decay, I'd say we'd
change the fexecve implementation.  But see below.

>> > diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>> > index 2b5cf0c0ea..f3bc7e89df 100644
>> > --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>> > +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>> > @@ -44,6 +44,24 @@
>> >  # define ZERO_EXTEND_5 movl %r8d, %r8d;
>> >  # undef ZERO_EXTEND_6
>> >  # define ZERO_EXTEND_6 movl %r9d, %r9d;
>> > +#else /*! __ASSEMBLER__ */
>> > +# undef ARGIFY
>> > +/* For x32, cast
>> > +   1. pointer to unsigned long (32 bit).
>> > +   2. 32-bit unsigned integer to unsigned long long (64 bit).
>> > +   3. 32-bit integer to long long (64 bit).
>> > + */
>> > +# define ARGIFY(X) \
>> > + ({                                                                  \
>> > +    _Pragma ("GCC diagnostic push");                                 \
>> > +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
>> > +    (__builtin_classify_type (X) == 5                                        \
>> > +     ? (unsigned long) (X)                                           \
>> > +     : ((sizeof (X) <= 4 && (TYPEFY1 (X)) 0 < (TYPEFY1 (X)) -1)              \
>> > +     ? (unsigned long long) (X)                                      \
>> > +     : (long long) (X)));                                            \
>> > +    _Pragma ("GCC diagnostic pop");                                  \
>> > +  })
>> >  #endif       /* __ASSEMBLER__ */
>>
>> This should use long int instead long everywhere.
>
> I will update.
>
>> __builtin_classify_type is undocumented.  I'm not sure if its behavior
>> and the constant 5 is actually stable across GCC versions, but the
>> powerpc uses it as well, for a slightly difference purpose (to
>> recognize array arguments, it seems).
>
> include/libc-pointer-arith.h has
>
> /* 1 if 'type' is a pointer type, 0 otherwise.  */
> # define __pointer_type(type) (__builtin_classify_type ((type) 0) == 5)
>
> If __builtin_classify_type ever changes, glibc won't build correctly.

I see.  In this case, can we solely rely on __builtin_classify_type?
I dont't think the inner ternary operator is necessary.

/* Enforce zero-extension for pointers and array system call
   arguments.  For integer types, extend to long long int (the full
   register) using a regular cast, resulting in zero or sign extension
   based on the signed-ness of the original type.  */
#define ARGIFY(X)                                                    \
  ({                                                                 \
    _Pragma ("GCC diagnostic push");                                 \
    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
    (__builtin_classify_type (X) == 5                                \
     ? (long long int) (uintptr_t) (X)                               \
     : (long long int) (X)));                                        \
    _Pragma ("GCC diagnostic pop");                                  \
  })

(Untested, sorry.)

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

* V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 15:17       ` Florian Weimer
@ 2020-04-13 16:33         ` H.J. Lu
  2020-04-13 17:00           ` Florian Weimer
  2020-04-13 18:34           ` Adhemerval Zanella
  0 siblings, 2 replies; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 16:33 UTC (permalink / raw)
  To: Florian Weimer; +Cc: H.J. Lu via Libc-alpha

[-- Attachment #1: Type: text/plain, Size: 5001 bytes --]

On Mon, Apr 13, 2020 at 8:17 AM Florian Weimer <fw@deneb.enyo.de> wrote:
>
> * H. J. Lu:
>
> > On Mon, Apr 13, 2020 at 7:43 AM Florian Weimer <fw@deneb.enyo.de> wrote:
> >>
> >> * H. J. Lu via Libc-alpha:
> >>
> >> > diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> >> > index 06e39212ee..93dad97c8f 100644
> >> > --- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> >> > +++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> >> > @@ -219,12 +219,14 @@
> >> >  /* Registers clobbered by syscall.  */
> >> >  # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
> >> >
> >> > -/* Create a variable 'name' based on type 'X' to avoid explicit types.
> >> > -   This is mainly used set use 64-bits arguments in x32.   */
> >> > -#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
> >> > +/* This also works when X is an array.  */
> >> > +#define TYPEFY1(X) __typeof__ ((X) - (X))
> >>
> >> I think the comment is now misleading.  For an array X, (X) - (X) is
> >> of type ptrdiff_t, which is signed, so it triggers sign extension, not
> >
> > I will update the comments.
>
> Thanks.
>
> >> zero extension, in the x32 case.  I think this is the reason why my
> >> proposed construct went astray in your experiments.
> >>
> >> Is it really necessary to support arrays for system call arguments?
> >
> > int
> > fexecve (int fd, char *const argv[], char *const envp[])
> > {
> >   if (fd < 0 || argv == NULL || envp == NULL)
> >     {
> >       __set_errno (EINVAL);
> >       return -1;
> >     }
> >
> > #ifdef __NR_execveat
> >   /* Avoid implicit array coercion in syscall macros.  */
> >   INLINE_SYSCALL (execveat, 5, fd, "", &argv[0], &envp[0], AT_EMPTY_PATH);
> >
> > "" is an array.
>
> If that were the only reason for enforcing pointer decay, I'd say we'd
> change the fexecve implementation.  But see below.
>
> >> > diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >> > index 2b5cf0c0ea..f3bc7e89df 100644
> >> > --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >> > +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >> > @@ -44,6 +44,24 @@
> >> >  # define ZERO_EXTEND_5 movl %r8d, %r8d;
> >> >  # undef ZERO_EXTEND_6
> >> >  # define ZERO_EXTEND_6 movl %r9d, %r9d;
> >> > +#else /*! __ASSEMBLER__ */
> >> > +# undef ARGIFY
> >> > +/* For x32, cast
> >> > +   1. pointer to unsigned long (32 bit).
> >> > +   2. 32-bit unsigned integer to unsigned long long (64 bit).
> >> > +   3. 32-bit integer to long long (64 bit).
> >> > + */
> >> > +# define ARGIFY(X) \
> >> > + ({                                                                  \
> >> > +    _Pragma ("GCC diagnostic push");                                 \
> >> > +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
> >> > +    (__builtin_classify_type (X) == 5                                        \
> >> > +     ? (unsigned long) (X)                                           \
> >> > +     : ((sizeof (X) <= 4 && (TYPEFY1 (X)) 0 < (TYPEFY1 (X)) -1)              \
> >> > +     ? (unsigned long long) (X)                                      \
> >> > +     : (long long) (X)));                                            \
> >> > +    _Pragma ("GCC diagnostic pop");                                  \
> >> > +  })
> >> >  #endif       /* __ASSEMBLER__ */
> >>
> >> This should use long int instead long everywhere.
> >
> > I will update.
> >
> >> __builtin_classify_type is undocumented.  I'm not sure if its behavior
> >> and the constant 5 is actually stable across GCC versions, but the
> >> powerpc uses it as well, for a slightly difference purpose (to
> >> recognize array arguments, it seems).
> >
> > include/libc-pointer-arith.h has
> >
> > /* 1 if 'type' is a pointer type, 0 otherwise.  */
> > # define __pointer_type(type) (__builtin_classify_type ((type) 0) == 5)
> >
> > If __builtin_classify_type ever changes, glibc won't build correctly.
>
> I see.  In this case, can we solely rely on __builtin_classify_type?
> I dont't think the inner ternary operator is necessary.
>
> /* Enforce zero-extension for pointers and array system call
>    arguments.  For integer types, extend to long long int (the full
>    register) using a regular cast, resulting in zero or sign extension
>    based on the signed-ness of the original type.  */
> #define ARGIFY(X)                                                    \
>   ({                                                                 \
>     _Pragma ("GCC diagnostic push");                                 \
>     _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
>     (__builtin_classify_type (X) == 5                                \
>      ? (long long int) (uintptr_t) (X)                               \
>      : (long long int) (X)));                                        \
>     _Pragma ("GCC diagnostic pop");                                  \
>   })
>
> (Untested, sorry.)

Here is the updated patch.

-- 
H.J.

[-- Attachment #2: 0002-x32-Properly-pass-long-to-syscall-BZ-25810.patch --]
[-- Type: text/x-patch, Size: 4189 bytes --]

From fd210e7ada784986f5f5bff3e4892478fc998fea Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 13 Apr 2020 07:02:46 -0700
Subject: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]

X32 has 32-bit long and pointer with 64-bit off_t.  Since x32 psABI
requires that pointers passed in registers must be zero-extended to
64bit, x32 can share many syscall interfaces with LP64.  When a LP64
syscall with long and unsigned long arguments is used for x32, these
arguments must be properly extended to 64-bit.  Otherwise if the upper
32 bits of the register have undefined value, such a syscall will be
rejected by kernel.

Enforce zero-extension for pointers and array system call arguments.
For integer types, extend to int64_t (the full register) using a
regular cast, resulting in zero or sign extension based on the
signedness of the original type.

For

       void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);

we now generate

   0:	41 f7 c1 ff 0f 00 00 	test   $0xfff,%r9d
   7:	75 1f                	jne    28 <__mmap64+0x28>
   9:	48 63 d2             	movslq %edx,%rdx
   c:	89 f6                	mov    %esi,%esi
   e:	4d 63 c0             	movslq %r8d,%r8
  11:	4c 63 d1             	movslq %ecx,%r10
  14:	b8 09 00 00 40       	mov    $0x40000009,%eax
  19:	0f 05                	syscall

That is

1. addr is unchanged.
2. length is zero-extend to 64 bits.
3. prot is sign-extend to 64 bits.
4. flags is sign-extend to 64 bits.
5. fd is sign-extend to 64 bits.
6. offset is unchanged.

For int arguments, since kernel uses only the lower 32 bits and ignores
the upper 32 bits in 64-bit registers, these work correctly.

Tested on x86-64 and x32.  There are no code changes on x86-64.
---
 sysdeps/unix/sysv/linux/x86_64/sysdep.h     | 15 +++++++++------
 sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h | 14 ++++++++++++++
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
index 06e39212ee..3596273c94 100644
--- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
+++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
@@ -219,12 +219,15 @@
 /* Registers clobbered by syscall.  */
 # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
 
-/* Create a variable 'name' based on type 'X' to avoid explicit types.
-   This is mainly used set use 64-bits arguments in x32.   */
-#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
-/* Explicit cast the argument to avoid integer from pointer warning on
-   x32.  */
-#define ARGIFY(X) ((__typeof__ ((X) - (X))) (X))
+/* NB: This also works when X is an array.  For an array X,  type of
+   (X) - (X) is ptrdiff_t, which is signed, since size of ptrdiff_t
+   == size of pointer, cast is a NOP.   */
+#define TYPEFY1(X) __typeof__ ((X) - (X))
+/* Explicit cast the argument.  */
+#define ARGIFY(X) ((TYPEFY1 (X)) (X))
+/* Create a variable 'name' based on type of variable 'X' to avoid
+   explicit types.  */
+#define TYPEFY(X, name) __typeof__ (ARGIFY (X)) name
 
 #undef INTERNAL_SYSCALL
 #define INTERNAL_SYSCALL(name, nr, args...)				\
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
index 2b5cf0c0ea..67335c2a7f 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
@@ -44,6 +44,20 @@
 # define ZERO_EXTEND_5 movl %r8d, %r8d;
 # undef ZERO_EXTEND_6
 # define ZERO_EXTEND_6 movl %r9d, %r9d;
+#else /*! __ASSEMBLER__ */
+# undef ARGIFY
+/* Enforce zero-extension for pointers and array system call arguments.
+   For integer types, extend to int64_t (the full register) using a
+   regular cast, resulting in zero or sign extension based on the
+   signedness of the original type.  */
+# define ARGIFY(X) \
+ ({									\
+    _Pragma ("GCC diagnostic push");					\
+    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");	\
+    (__builtin_classify_type (X) == 5					\
+     ? (uintptr_t) (X) : (int64_t) (X));				\
+    _Pragma ("GCC diagnostic pop");					\
+  })
 #endif	/* __ASSEMBLER__ */
 
 #endif /* linux/x86_64/x32/sysdep.h */
-- 
2.25.2


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

* Re: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 16:33         ` V2 " H.J. Lu
@ 2020-04-13 17:00           ` Florian Weimer
  2020-04-13 18:34           ` Adhemerval Zanella
  1 sibling, 0 replies; 13+ messages in thread
From: Florian Weimer @ 2020-04-13 17:00 UTC (permalink / raw)
  To: H.J. Lu via Libc-alpha

* H. J. Lu via Libc-alpha:

> From: "H.J. Lu" <hjl.tools@gmail.com>
> Date: Mon, 13 Apr 2020 07:02:46 -0700
> Subject: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
>
> X32 has 32-bit long and pointer with 64-bit off_t.  Since x32 psABI
> requires that pointers passed in registers must be zero-extended to
> 64bit, x32 can share many syscall interfaces with LP64.  When a LP64
> syscall with long and unsigned long arguments is used for x32, these
> arguments must be properly extended to 64-bit.  Otherwise if the upper
> 32 bits of the register have undefined value, such a syscall will be
> rejected by kernel.

This version looks okay to me, thanks.

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

* Re: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 16:33         ` V2 " H.J. Lu
  2020-04-13 17:00           ` Florian Weimer
@ 2020-04-13 18:34           ` Adhemerval Zanella
  2020-04-13 20:43             ` H.J. Lu
  1 sibling, 1 reply; 13+ messages in thread
From: Adhemerval Zanella @ 2020-04-13 18:34 UTC (permalink / raw)
  To: libc-alpha



On 13/04/2020 13:33, H.J. Lu via Libc-alpha wrote:
> On Mon, Apr 13, 2020 at 8:17 AM Florian Weimer <fw@deneb.enyo.de> wrote:
>>
>> * H. J. Lu:
>>
>>> On Mon, Apr 13, 2020 at 7:43 AM Florian Weimer <fw@deneb.enyo.de> wrote:
>>>>
>>>> * H. J. Lu via Libc-alpha:
>>>>
>>>>> diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
>>>>> index 06e39212ee..93dad97c8f 100644
>>>>> --- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
>>>>> +++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
>>>>> @@ -219,12 +219,14 @@
>>>>>  /* Registers clobbered by syscall.  */
>>>>>  # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
>>>>>
>>>>> -/* Create a variable 'name' based on type 'X' to avoid explicit types.
>>>>> -   This is mainly used set use 64-bits arguments in x32.   */
>>>>> -#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
>>>>> +/* This also works when X is an array.  */
>>>>> +#define TYPEFY1(X) __typeof__ ((X) - (X))
>>>>
>>>> I think the comment is now misleading.  For an array X, (X) - (X) is
>>>> of type ptrdiff_t, which is signed, so it triggers sign extension, not
>>>
>>> I will update the comments.
>>
>> Thanks.
>>
>>>> zero extension, in the x32 case.  I think this is the reason why my
>>>> proposed construct went astray in your experiments.
>>>>
>>>> Is it really necessary to support arrays for system call arguments?
>>>
>>> int
>>> fexecve (int fd, char *const argv[], char *const envp[])
>>> {
>>>   if (fd < 0 || argv == NULL || envp == NULL)
>>>     {
>>>       __set_errno (EINVAL);
>>>       return -1;
>>>     }
>>>
>>> #ifdef __NR_execveat
>>>   /* Avoid implicit array coercion in syscall macros.  */
>>>   INLINE_SYSCALL (execveat, 5, fd, "", &argv[0], &envp[0], AT_EMPTY_PATH);
>>>
>>> "" is an array.
>>
>> If that were the only reason for enforcing pointer decay, I'd say we'd
>> change the fexecve implementation.  But see below.
>>
>>>>> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>>>>> index 2b5cf0c0ea..f3bc7e89df 100644
>>>>> --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>>>>> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>>>>> @@ -44,6 +44,24 @@
>>>>>  # define ZERO_EXTEND_5 movl %r8d, %r8d;
>>>>>  # undef ZERO_EXTEND_6
>>>>>  # define ZERO_EXTEND_6 movl %r9d, %r9d;
>>>>> +#else /*! __ASSEMBLER__ */
>>>>> +# undef ARGIFY
>>>>> +/* For x32, cast
>>>>> +   1. pointer to unsigned long (32 bit).
>>>>> +   2. 32-bit unsigned integer to unsigned long long (64 bit).
>>>>> +   3. 32-bit integer to long long (64 bit).
>>>>> + */
>>>>> +# define ARGIFY(X) \
>>>>> + ({                                                                  \
>>>>> +    _Pragma ("GCC diagnostic push");                                 \
>>>>> +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
>>>>> +    (__builtin_classify_type (X) == 5                                        \
>>>>> +     ? (unsigned long) (X)                                           \
>>>>> +     : ((sizeof (X) <= 4 && (TYPEFY1 (X)) 0 < (TYPEFY1 (X)) -1)              \
>>>>> +     ? (unsigned long long) (X)                                      \
>>>>> +     : (long long) (X)));                                            \
>>>>> +    _Pragma ("GCC diagnostic pop");                                  \
>>>>> +  })
>>>>>  #endif       /* __ASSEMBLER__ */
>>>>
>>>> This should use long int instead long everywhere.
>>>
>>> I will update.
>>>
>>>> __builtin_classify_type is undocumented.  I'm not sure if its behavior
>>>> and the constant 5 is actually stable across GCC versions, but the
>>>> powerpc uses it as well, for a slightly difference purpose (to
>>>> recognize array arguments, it seems).
>>>
>>> include/libc-pointer-arith.h has
>>>
>>> /* 1 if 'type' is a pointer type, 0 otherwise.  */
>>> # define __pointer_type(type) (__builtin_classify_type ((type) 0) == 5)
>>>
>>> If __builtin_classify_type ever changes, glibc won't build correctly.
>>
>> I see.  In this case, can we solely rely on __builtin_classify_type?
>> I dont't think the inner ternary operator is necessary.
>>
>> /* Enforce zero-extension for pointers and array system call
>>    arguments.  For integer types, extend to long long int (the full
>>    register) using a regular cast, resulting in zero or sign extension
>>    based on the signed-ness of the original type.  */
>> #define ARGIFY(X)                                                    \
>>   ({                                                                 \
>>     _Pragma ("GCC diagnostic push");                                 \
>>     _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
>>     (__builtin_classify_type (X) == 5                                \
>>      ? (long long int) (uintptr_t) (X)                               \
>>      : (long long int) (X)));                                        \
>>     _Pragma ("GCC diagnostic pop");                                  \
>>   })
>>
>> (Untested, sorry.)
> 
> Here is the updated patch.
> 

> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> index 2b5cf0c0ea..67335c2a7f 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> @@ -44,6 +44,20 @@
>  # define ZERO_EXTEND_5 movl %r8d, %r8d;
>  # undef ZERO_EXTEND_6
>  # define ZERO_EXTEND_6 movl %r9d, %r9d;
> +#else /*! __ASSEMBLER__ */
> +# undef ARGIFY
> +/* Enforce zero-extension for pointers and array system call arguments.
> +   For integer types, extend to int64_t (the full register) using a
> +   regular cast, resulting in zero or sign extension based on the
> +   signedness of the original type.  */
> +# define ARGIFY(X) \
> + ({									\
> +    _Pragma ("GCC diagnostic push");					\
> +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");	\
> +    (__builtin_classify_type (X) == 5					\
> +     ? (uintptr_t) (X) : (int64_t) (X));				\
> +    _Pragma ("GCC diagnostic pop");					\
> +  })
>  #endif	/* __ASSEMBLER__ */
>  
>  #endif /* linux/x86_64/x32/sysdep.h */

I face a similar issue when fixing BZ#12683 for x32, and one possible
solution I found was:

  #define __SSC(__x)                                            \
  ({                                                            \
    __syscall_arg_t __arg = sizeof (1 ? (__x) : 0ULL) < 8       \
      ? (unsigned long int) (uintptr_t)(__x)                    \
      : (__syscall_arg_t) (__typeof__ ((__x) - (__x))) (__x);   \
    __arg;                                                      \
  })

Which was for argument passing in the internal cancellable bridge.
Later you suggested a solution I have also devised sometime ago [1].

Couldn't we use any of them instead of explicitly disable/enable
compile warnings?

[1] https://sourceware.org/legacy-ml/libc-alpha/2017-12/msg00315.html

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

* Re: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 18:34           ` Adhemerval Zanella
@ 2020-04-13 20:43             ` H.J. Lu
  2020-04-13 21:13               ` Florian Weimer
  0 siblings, 1 reply; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 20:43 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: GNU C Library

On Mon, Apr 13, 2020 at 11:35 AM Adhemerval Zanella via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
>
>
> On 13/04/2020 13:33, H.J. Lu via Libc-alpha wrote:
> > On Mon, Apr 13, 2020 at 8:17 AM Florian Weimer <fw@deneb.enyo.de> wrote:
> >>
> >> * H. J. Lu:
> >>
> >>> On Mon, Apr 13, 2020 at 7:43 AM Florian Weimer <fw@deneb.enyo.de> wrote:
> >>>>
> >>>> * H. J. Lu via Libc-alpha:
> >>>>
> >>>>> diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> >>>>> index 06e39212ee..93dad97c8f 100644
> >>>>> --- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> >>>>> +++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
> >>>>> @@ -219,12 +219,14 @@
> >>>>>  /* Registers clobbered by syscall.  */
> >>>>>  # define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
> >>>>>
> >>>>> -/* Create a variable 'name' based on type 'X' to avoid explicit types.
> >>>>> -   This is mainly used set use 64-bits arguments in x32.   */
> >>>>> -#define TYPEFY(X, name) __typeof__ ((X) - (X)) name
> >>>>> +/* This also works when X is an array.  */
> >>>>> +#define TYPEFY1(X) __typeof__ ((X) - (X))
> >>>>
> >>>> I think the comment is now misleading.  For an array X, (X) - (X) is
> >>>> of type ptrdiff_t, which is signed, so it triggers sign extension, not
> >>>
> >>> I will update the comments.
> >>
> >> Thanks.
> >>
> >>>> zero extension, in the x32 case.  I think this is the reason why my
> >>>> proposed construct went astray in your experiments.
> >>>>
> >>>> Is it really necessary to support arrays for system call arguments?
> >>>
> >>> int
> >>> fexecve (int fd, char *const argv[], char *const envp[])
> >>> {
> >>>   if (fd < 0 || argv == NULL || envp == NULL)
> >>>     {
> >>>       __set_errno (EINVAL);
> >>>       return -1;
> >>>     }
> >>>
> >>> #ifdef __NR_execveat
> >>>   /* Avoid implicit array coercion in syscall macros.  */
> >>>   INLINE_SYSCALL (execveat, 5, fd, "", &argv[0], &envp[0], AT_EMPTY_PATH);
> >>>
> >>> "" is an array.
> >>
> >> If that were the only reason for enforcing pointer decay, I'd say we'd
> >> change the fexecve implementation.  But see below.
> >>
> >>>>> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >>>>> index 2b5cf0c0ea..f3bc7e89df 100644
> >>>>> --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >>>>> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >>>>> @@ -44,6 +44,24 @@
> >>>>>  # define ZERO_EXTEND_5 movl %r8d, %r8d;
> >>>>>  # undef ZERO_EXTEND_6
> >>>>>  # define ZERO_EXTEND_6 movl %r9d, %r9d;
> >>>>> +#else /*! __ASSEMBLER__ */
> >>>>> +# undef ARGIFY
> >>>>> +/* For x32, cast
> >>>>> +   1. pointer to unsigned long (32 bit).
> >>>>> +   2. 32-bit unsigned integer to unsigned long long (64 bit).
> >>>>> +   3. 32-bit integer to long long (64 bit).
> >>>>> + */
> >>>>> +# define ARGIFY(X) \
> >>>>> + ({                                                                  \
> >>>>> +    _Pragma ("GCC diagnostic push");                                 \
> >>>>> +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
> >>>>> +    (__builtin_classify_type (X) == 5                                        \
> >>>>> +     ? (unsigned long) (X)                                           \
> >>>>> +     : ((sizeof (X) <= 4 && (TYPEFY1 (X)) 0 < (TYPEFY1 (X)) -1)              \
> >>>>> +     ? (unsigned long long) (X)                                      \
> >>>>> +     : (long long) (X)));                                            \
> >>>>> +    _Pragma ("GCC diagnostic pop");                                  \
> >>>>> +  })
> >>>>>  #endif       /* __ASSEMBLER__ */
> >>>>
> >>>> This should use long int instead long everywhere.
> >>>
> >>> I will update.
> >>>
> >>>> __builtin_classify_type is undocumented.  I'm not sure if its behavior
> >>>> and the constant 5 is actually stable across GCC versions, but the
> >>>> powerpc uses it as well, for a slightly difference purpose (to
> >>>> recognize array arguments, it seems).
> >>>
> >>> include/libc-pointer-arith.h has
> >>>
> >>> /* 1 if 'type' is a pointer type, 0 otherwise.  */
> >>> # define __pointer_type(type) (__builtin_classify_type ((type) 0) == 5)
> >>>
> >>> If __builtin_classify_type ever changes, glibc won't build correctly.
> >>
> >> I see.  In this case, can we solely rely on __builtin_classify_type?
> >> I dont't think the inner ternary operator is necessary.
> >>
> >> /* Enforce zero-extension for pointers and array system call
> >>    arguments.  For integer types, extend to long long int (the full
> >>    register) using a regular cast, resulting in zero or sign extension
> >>    based on the signed-ness of the original type.  */
> >> #define ARGIFY(X)                                                    \
> >>   ({                                                                 \
> >>     _Pragma ("GCC diagnostic push");                                 \
> >>     _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
> >>     (__builtin_classify_type (X) == 5                                \
> >>      ? (long long int) (uintptr_t) (X)                               \
> >>      : (long long int) (X)));                                        \
> >>     _Pragma ("GCC diagnostic pop");                                  \
> >>   })
> >>
> >> (Untested, sorry.)
> >
> > Here is the updated patch.
> >
>
> > diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> > index 2b5cf0c0ea..67335c2a7f 100644
> > --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> > +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> > @@ -44,6 +44,20 @@
> >  # define ZERO_EXTEND_5 movl %r8d, %r8d;
> >  # undef ZERO_EXTEND_6
> >  # define ZERO_EXTEND_6 movl %r9d, %r9d;
> > +#else /*! __ASSEMBLER__ */
> > +# undef ARGIFY
> > +/* Enforce zero-extension for pointers and array system call arguments.
> > +   For integer types, extend to int64_t (the full register) using a
> > +   regular cast, resulting in zero or sign extension based on the
> > +   signedness of the original type.  */
> > +# define ARGIFY(X) \
> > + ({                                                                  \
> > +    _Pragma ("GCC diagnostic push");                                 \
> > +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
> > +    (__builtin_classify_type (X) == 5                                        \
> > +     ? (uintptr_t) (X) : (int64_t) (X));                             \
> > +    _Pragma ("GCC diagnostic pop");                                  \
> > +  })
> >  #endif       /* __ASSEMBLER__ */
> >
> >  #endif /* linux/x86_64/x32/sysdep.h */
>
> I face a similar issue when fixing BZ#12683 for x32, and one possible
> solution I found was:
>
>   #define __SSC(__x)                                            \
>   ({                                                            \
>     __syscall_arg_t __arg = sizeof (1 ? (__x) : 0ULL) < 8       \
>       ? (unsigned long int) (uintptr_t)(__x)                    \
>       : (__syscall_arg_t) (__typeof__ ((__x) - (__x))) (__x);   \
>     __arg;                                                      \
>   })

This is similar to what I have checked in.

> Which was for argument passing in the internal cancellable bridge.
> Later you suggested a solution I have also devised sometime ago [1].
>
> Couldn't we use any of them instead of explicitly disable/enable
> compile warnings?
>
> [1] https://sourceware.org/legacy-ml/libc-alpha/2017-12/msg00315.html

This doesn't work on array, like "".

-- 
H.J.

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

* Re: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 20:43             ` H.J. Lu
@ 2020-04-13 21:13               ` Florian Weimer
  2020-04-13 21:19                 ` H.J. Lu
  0 siblings, 1 reply; 13+ messages in thread
From: Florian Weimer @ 2020-04-13 21:13 UTC (permalink / raw)
  To: H.J. Lu via Libc-alpha

* H. J. Lu via Libc-alpha:

>> > diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>> > index 2b5cf0c0ea..67335c2a7f 100644
>> > --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>> > +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
>> > @@ -44,6 +44,20 @@
>> >  # define ZERO_EXTEND_5 movl %r8d, %r8d;
>> >  # undef ZERO_EXTEND_6
>> >  # define ZERO_EXTEND_6 movl %r9d, %r9d;
>> > +#else /*! __ASSEMBLER__ */
>> > +# undef ARGIFY
>> > +/* Enforce zero-extension for pointers and array system call arguments.
>> > +   For integer types, extend to int64_t (the full register) using a
>> > +   regular cast, resulting in zero or sign extension based on the
>> > +   signedness of the original type.  */
>> > +# define ARGIFY(X) \
>> > + ({                                                                  \
>> > +    _Pragma ("GCC diagnostic push");                                 \
>> > +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
>> > +    (__builtin_classify_type (X) == 5                                        \
>> > +     ? (uintptr_t) (X) : (int64_t) (X));                             \
>> > +    _Pragma ("GCC diagnostic pop");                                  \
>> > +  })
>> >  #endif       /* __ASSEMBLER__ */
>> >
>> >  #endif /* linux/x86_64/x32/sysdep.h */
>>
>> I face a similar issue when fixing BZ#12683 for x32, and one possible
>> solution I found was:
>>
>>   #define __SSC(__x)                                            \
>>   ({                                                            \
>>     __syscall_arg_t __arg = sizeof (1 ? (__x) : 0ULL) < 8       \
>>       ? (unsigned long int) (uintptr_t)(__x)                    \
>>       : (__syscall_arg_t) (__typeof__ ((__x) - (__x))) (__x);   \
>>     __arg;                                                      \
>>   })
>
> This is similar to what I have checked in.

I think Adhemerval specifically meant the additional cast using
__typeof__ to suppress the warning (which I did not think of).

The _Pragma construct as written is a Clang porting hazard because it
causes the statement expression to return void (which is a Clang/GCC
discrepancy).  Rather than introducing a temporary, using the
__typeof__ hack is probably the better way of fixing it.

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

* Re: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810]
  2020-04-13 21:13               ` Florian Weimer
@ 2020-04-13 21:19                 ` H.J. Lu
  0 siblings, 0 replies; 13+ messages in thread
From: H.J. Lu @ 2020-04-13 21:19 UTC (permalink / raw)
  To: Florian Weimer; +Cc: H.J. Lu via Libc-alpha, Adhemerval Zanella

On Mon, Apr 13, 2020 at 2:13 PM Florian Weimer <fw@deneb.enyo.de> wrote:
>
> * H. J. Lu via Libc-alpha:
>
> >> > diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >> > index 2b5cf0c0ea..67335c2a7f 100644
> >> > --- a/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >> > +++ b/sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
> >> > @@ -44,6 +44,20 @@
> >> >  # define ZERO_EXTEND_5 movl %r8d, %r8d;
> >> >  # undef ZERO_EXTEND_6
> >> >  # define ZERO_EXTEND_6 movl %r9d, %r9d;
> >> > +#else /*! __ASSEMBLER__ */
> >> > +# undef ARGIFY
> >> > +/* Enforce zero-extension for pointers and array system call arguments.
> >> > +   For integer types, extend to int64_t (the full register) using a
> >> > +   regular cast, resulting in zero or sign extension based on the
> >> > +   signedness of the original type.  */
> >> > +# define ARGIFY(X) \
> >> > + ({                                                                  \
> >> > +    _Pragma ("GCC diagnostic push");                                 \
> >> > +    _Pragma ("GCC diagnostic ignored \"-Wpointer-to-int-cast\"");    \
> >> > +    (__builtin_classify_type (X) == 5                                        \
> >> > +     ? (uintptr_t) (X) : (int64_t) (X));                             \
> >> > +    _Pragma ("GCC diagnostic pop");                                  \
> >> > +  })
> >> >  #endif       /* __ASSEMBLER__ */
> >> >
> >> >  #endif /* linux/x86_64/x32/sysdep.h */
> >>
> >> I face a similar issue when fixing BZ#12683 for x32, and one possible
> >> solution I found was:
> >>
> >>   #define __SSC(__x)                                            \
> >>   ({                                                            \
> >>     __syscall_arg_t __arg = sizeof (1 ? (__x) : 0ULL) < 8       \
> >>       ? (unsigned long int) (uintptr_t)(__x)                    \
> >>       : (__syscall_arg_t) (__typeof__ ((__x) - (__x))) (__x);   \
> >>     __arg;                                                      \
> >>   })
> >
> > This is similar to what I have checked in.
>
> I think Adhemerval specifically meant the additional cast using
> __typeof__ to suppress the warning (which I did not think of).

His patch:

https://sourceware.org/pipermail/libc-alpha/2020-April/112520.html

also needs to suppress -Wpointer-to-int-cast

> The _Pragma construct as written is a Clang porting hazard because it
> causes the statement expression to return void (which is a Clang/GCC
> discrepancy).  Rather than introducing a temporary, using the
> __typeof__ hack is probably the better way of fixing it.

I don't think it works on array.

-- 
H.J.

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

end of thread, other threads:[~2020-04-13 21:19 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-13 14:25 [PATCH 0/3] x32: Properly pass long to syscall [BZ #25810] H.J. Lu
2020-04-13 14:25 ` [PATCH 1/3] Add SYSCALL_ULONG_ARG_[12] to " H.J. Lu
2020-04-13 14:25 ` [PATCH 2/3] x32: Properly " H.J. Lu
2020-04-13 14:43   ` Florian Weimer
2020-04-13 15:03     ` H.J. Lu
2020-04-13 15:17       ` Florian Weimer
2020-04-13 16:33         ` V2 " H.J. Lu
2020-04-13 17:00           ` Florian Weimer
2020-04-13 18:34           ` Adhemerval Zanella
2020-04-13 20:43             ` H.J. Lu
2020-04-13 21:13               ` Florian Weimer
2020-04-13 21:19                 ` H.J. Lu
2020-04-13 14:25 ` [PATCH 3/3] Add a syscall test for " H.J. Lu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).