From patchwork Mon Apr 13 16:33:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "H.J. Lu" X-Patchwork-Id: 38812 Return-Path: X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-il1-x131.google.com (mail-il1-x131.google.com [IPv6:2607:f8b0:4864:20::131]) by sourceware.org (Postfix) with ESMTPS id 9119E3887000 for ; Mon, 13 Apr 2020 16:33:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 9119E3887000 Received: by mail-il1-x131.google.com with SMTP id i14so8981250ilr.11 for ; Mon, 13 Apr 2020 09:33:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=jTXqLprPjvBHruJgPAMjT6G6o3sQpjPZ6Q/+lDhno7g=; b=Lnu2zX6IFKlHUC2dWTxIeA+vC6b2WUNcnqzqZGlGSchMZnLv6UoZ5wLUOaeDTZnZL7 kSSGjC45JjON6VddeH1GKnYVz1drMudgaQQeIq3r/OEqJNhcS6W1cXIdbOChMeMUpaG3 irXR/DmSzGeWJJf23Vb19p5u2mZm317ibmGYkCDLINP41f+IxJit98wXOqgGp50RGrxK Z9KkBvgroVw1LtGmTgMRi+THuh8Uco+Yx0waCZfr2gXtEqgAmXm1jCHQyhhgsW4zbVra +1Bvw6IIWrgqtWsBNsSDcIasHjfduRRmebqA5Zxy0llBQRdcu3DPdxEJTP6O247SFlpY +Jiw== X-Gm-Message-State: AGi0PubaYOCy7QYxZzLdGETrFqGO+JBvQX7mDT7/8jFqK4ItNKmtHpvu lYJxb3b++9Z3MskjVm8vzqCbOQRG2Zp6fjy6uMLuag== X-Google-Smtp-Source: APiQypL/CYbXZT+29RFG1nwLy0bic4V78lFKIOZW+dlS/Jd1rjQGe7D7iG88pbOPT+40WrJDSPHzqstFjmQTiJdudt8= X-Received: by 2002:a92:cc11:: with SMTP id s17mr16293208ilp.13.1586795621871; Mon, 13 Apr 2020 09:33:41 -0700 (PDT) MIME-Version: 1.0 References: <20200413142515.16619-1-hjl.tools@gmail.com> <20200413142515.16619-3-hjl.tools@gmail.com> <8736974fag.fsf@mid.deneb.enyo.de> <87y2qz2z5l.fsf@mid.deneb.enyo.de> In-Reply-To: <87y2qz2z5l.fsf@mid.deneb.enyo.de> From: "H.J. Lu" Date: Mon, 13 Apr 2020 09:33:06 -0700 Message-ID: Subject: V2 [PATCH 2/3] x32: Properly pass long to syscall [BZ #25810] To: Florian Weimer Cc: "H.J. Lu via Libc-alpha" X-Spam-Status: No, score=-19.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 13 Apr 2020 16:33:44 -0000 On Mon, Apr 13, 2020 at 8:17 AM Florian Weimer wrote: > > * H. J. Lu: > > > On Mon, Apr 13, 2020 at 7:43 AM Florian Weimer 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. From fd210e7ada784986f5f5bff3e4892478fc998fea Mon Sep 17 00:00:00 2001 From: "H.J. Lu" 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