From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id Lw4RKCcpCGD8VwAAWB0awg (envelope-from ) for ; Wed, 20 Jan 2021 07:59:19 -0500 Received: by simark.ca (Postfix, from userid 112) id 9607F1EF80; Wed, 20 Jan 2021 07:59:19 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=0.3 required=5.0 tests=MAILING_LIST_MULTI,RDNS_NONE, URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 Received: from sourceware.org (unknown [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 1A1811E939 for ; Wed, 20 Jan 2021 07:59:19 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6E2663854817; Wed, 20 Jan 2021 12:59:18 +0000 (GMT) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) by sourceware.org (Postfix) with ESMTPS id 207793857814 for ; Wed, 20 Jan 2021 12:59:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 207793857814 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=suse.de Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=tdevries@suse.de X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 3F7B9AC4F; Wed, 20 Jan 2021 12:59:14 +0000 (UTC) Date: Wed, 20 Jan 2021 13:59:12 +0100 From: Tom de Vries To: gdb-patches@sourceware.org Subject: [PATCH][gdb/server] Don't overwrite fs/gs_base with -m32 Message-ID: <20210120125910.GA23080@delia> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.10.1 (2018-07-13) X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces@sourceware.org Sender: "Gdb-patches" Hi, Consider a minimal test-case test.c: ... int main (void) { return 0; } ... compiled with -m32: ... $ gcc test.c -m32 ... When running the exec using gdbserver on openSUSE Factory (currently running a linux kernel version 5.10.5): ... $ gdbserver localhost:12345 a.out ... to which we connect in a gdb session, we run into a segfault in the inferior: ... $ gdb -batch -q -ex "target remote localhost:12345" -ex continue Program received signal SIGSEGV, Segmentation fault. 0xf7dd8bd2 in init_cacheinfo () at ../sysdeps/x86/cacheinfo.c:761 ... The segfault is caused by gdbserver overwriting $gs_base with 0 using PTRACE_SETREGS. After it is overwritten, the next use of $gs in the inferior will trigger the segfault. Before linux kernel version 5.9, the value used by PTRACE_SETREGS for $gs_base was ignored, but starting version 5.9, the linux kernel has support for intel architecture extension FSGSBASE, which allows users to modify $gs_base, and consequently PTRACE_SETREGS can no longer ignore the $gs_base value. The overwrite of $gs_base with 0 is done by a memset in x86_fill_gregset, which was added in commit 9e0aa64f551 "Fix gdbserver qGetTLSAddr for x86_64 -m32". The memset intends to zero-extend 32-bit registers that are tracked in the regcache to 64-bit when writing them into the PTRACE_SETREGS data argument. But in addition, it overwrites other registers that are not tracked in the regcache, such as $gs_base. Fix the segfault by redoing the fix from commit 9e0aa64f551 in minimal form. Tested on x86_64-linux: - openSUSE Leap 15.2 (using kernel version 5.3.18): - native - gdbserver -m32 - -m32 - openSUSE Factory (using kernel version 5.10.5): - native - m32 Any comments? Thanks, - Tom [gdb/server] Don't overwrite fs/gs_base with -m32 gdbserver/ChangeLog: 2021-01-19 Tom de Vries * linux-x86-low.cc (collect_register_i386): New function. (x86_fill_gregset): Remove memset. Use collect_register_i386. --- gdbserver/linux-x86-low.cc | 55 +++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc index 35dfdd7e0f3..d3273a18f47 100644 --- a/gdbserver/linux-x86-low.cc +++ b/gdbserver/linux-x86-low.cc @@ -397,6 +397,35 @@ x86_target::low_cannot_fetch_register (int regno) return regno >= I386_NUM_REGS; } +static void +collect_register_i386 (struct regcache *regcache, int regno, void *buf) +{ + collect_register (regcache, regno, buf); + +#ifdef __x86_64__ + /* In case of x86_64 -m32, collect_register only writes 4 bytes, but the + space reserved in buf for the register is 8 bytes. Make sure the entire + reserved space is initialized. */ + + gdb_assert (register_size (regcache->tdesc, regno) == 4); + + if (regno == RAX) + { + /* Sign extend EAX value to avoid potential syscall restart + problems. + + See amd64_linux_collect_native_gregset() in + gdb/amd64-linux-nat.c for a detailed explanation. */ + *(int64_t *) buf = *(int32_t *) buf; + } + else + { + /* Zero-extend. */ + *(uint64_t *) buf = *(uint32_t *) buf; + } +#endif +} + static void x86_fill_gregset (struct regcache *regcache, void *buf) { @@ -411,32 +440,14 @@ x86_fill_gregset (struct regcache *regcache, void *buf) return; } - - /* 32-bit inferior registers need to be zero-extended. - Callers would read uninitialized memory otherwise. */ - memset (buf, 0x00, X86_64_USER_REGS * 8); #endif for (i = 0; i < I386_NUM_REGS; i++) - collect_register (regcache, i, ((char *) buf) + i386_regmap[i]); - - collect_register_by_name (regcache, "orig_eax", - ((char *) buf) + ORIG_EAX * REGSIZE); + collect_register_i386 (regcache, i, ((char *) buf) + i386_regmap[i]); -#ifdef __x86_64__ - /* Sign extend EAX value to avoid potential syscall restart - problems. - - See amd64_linux_collect_native_gregset() in gdb/amd64-linux-nat.c - for a detailed explanation. */ - if (register_size (regcache->tdesc, 0) == 4) - { - void *ptr = ((gdb_byte *) buf - + i386_regmap[find_regno (regcache->tdesc, "eax")]); - - *(int64_t *) ptr = *(int32_t *) ptr; - } -#endif + /* Handle ORIG_EAX, which is not in i386_regmap. */ + collect_register_i386 (regcache, find_regno (regcache->tdesc, "orig_eax"), + ((char *) buf) + ORIG_EAX * REGSIZE); } static void