From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-oi1-x233.google.com (mail-oi1-x233.google.com [IPv6:2607:f8b0:4864:20::233]) by sourceware.org (Postfix) with ESMTPS id 08F5F384B12B for ; Thu, 12 May 2022 13:15:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 08F5F384B12B Received: by mail-oi1-x233.google.com with SMTP id n24so6312874oie.12 for ; Thu, 12 May 2022 06:15:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:to:references:from:in-reply-to :content-transfer-encoding; bh=2umhodqhDLT2a14QdxviQLwtuNUEirc+CgKJR01PdzM=; b=fdLL+has2DKEgvvfGnH7yXUBxcYUNWV724YTWRAwoFxMUGrfkZV7PWQMZkE2508PqS NCIWpnTtje1E4faUa+gHzZJd4g1G0vcAf67Zlcltb/8ZllheHBJ1mDFxDCHDKAoBdIWL yzmFFmF0b1iZJQApff3GBd90B2NR2KCzIrt5r3cumyzEW2kTGCVV/bx2lMY7CAHfewBf ecKrTiqyQ9jMiGwDfvPC3nk3Dq6LcT3SSgOERXeEPmzcgU3SQRKsnR7l0pHpPysil/O/ Gh0ytlZ0i5JaKMlq5zasoiFuZlSZlVgUh41y/QDJgK9Aut+oirAtaY8mzsKXHPJFpFoq sa7A== X-Gm-Message-State: AOAM531/na/uvkI3K18Pr9kUXH1osbQ3bVzvggx7B3gvFj2t1pz9S1ko 8C2wff/4Hd15Gd87lp6omrYuVx/K4qEvVw== X-Google-Smtp-Source: ABdhPJynfgIYAbWXxWfhr91Cq0BXK266G7v5BxY1d7cSm3xLFk6AQZWQsXormIT0OBoLN5fOLO440g== X-Received: by 2002:a05:6808:14cd:b0:326:d85f:b09e with SMTP id f13-20020a05680814cd00b00326d85fb09emr5157797oiw.263.1652361308091; Thu, 12 May 2022 06:15:08 -0700 (PDT) Received: from ?IPV6:2804:431:c7ca:5fbd:6a01:8849:5cb:128b? ([2804:431:c7ca:5fbd:6a01:8849:5cb:128b]) by smtp.gmail.com with ESMTPSA id x12-20020a9d6d8c000000b006060322125bsm1972470otp.43.2022.05.12.06.15.06 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 12 May 2022 06:15:07 -0700 (PDT) Message-ID: <63d4e062-b5a5-a430-fa56-da09912f1d1a@linaro.org> Date: Thu, 12 May 2022 10:15:04 -0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.9.0 Subject: Re: [PATCH v5 2/2] csu: Implement and use _dl_early_allocate during static startup Content-Language: en-US To: Florian Weimer , libc-alpha@sourceware.org References: From: Adhemerval Zanella In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-14.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, NICE_REPLY_A, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) 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: Thu, 12 May 2022 13:15:11 -0000 On 09/05/2022 15:18, Florian Weimer via Libc-alpha wrote: > This implements mmap fallback for a brk failure during TLS > allocation. > > scripts/tls-elf-edit.py is updated to support the new patching method. > The script no longer requires that in the input object is of ET_DYN > type. > --- > v5: Use __brk_call. > csu/libc-tls.c | 11 ++- > elf/Makefile | 19 +++++ > elf/dl-early_allocate.c | 30 ++++++++ > elf/tst-tls-allocation-failure-static.c | 31 ++++++++ > scripts/tst-elf-edit.py | 34 +++++++-- > sysdeps/generic/ldsodefs.h | 5 ++ > sysdeps/unix/sysv/linux/dl-early_allocate.c | 82 +++++++++++++++++++++ > 7 files changed, 202 insertions(+), 10 deletions(-) > create mode 100644 elf/dl-early_allocate.c > create mode 100644 elf/tst-tls-allocation-failure-static.c > create mode 100644 sysdeps/unix/sysv/linux/dl-early_allocate.c > > diff --git a/csu/libc-tls.c b/csu/libc-tls.c > index bef92a7568..0a216c5502 100644 > --- a/csu/libc-tls.c > +++ b/csu/libc-tls.c > @@ -145,11 +145,16 @@ __libc_setup_tls (void) > _dl_allocate_tls_storage (in elf/dl-tls.c) does using __libc_memalign > and dl_tls_static_align. */ > tcb_offset = roundup (memsz + GLRO(dl_tls_static_surplus), max_align); > - tlsblock = __sbrk (tcb_offset + TLS_INIT_TCB_SIZE + max_align); > + tlsblock = _dl_early_allocate (tcb_offset + TLS_INIT_TCB_SIZE + max_align); > + if (tlsblock == NULL) > + _startup_fatal ("Fatal glibc error: Cannot allocate TLS block\n"); > #elif TLS_DTV_AT_TP > tcb_offset = roundup (TLS_INIT_TCB_SIZE, align ?: 1); > - tlsblock = __sbrk (tcb_offset + memsz + max_align > - + TLS_PRE_TCB_SIZE + GLRO(dl_tls_static_surplus)); > + tlsblock = _dl_early_allocate (tcb_offset + memsz + max_align > + + TLS_PRE_TCB_SIZE > + + GLRO(dl_tls_static_surplus)); > + if (tlsblock == NULL) > + _startup_fatal ("Fatal glibc error: Cannot allocate TLS block\n"); > tlsblock += TLS_PRE_TCB_SIZE; > #else > /* In case a model with a different layout for the TCB and DTV > diff --git a/elf/Makefile b/elf/Makefile > index fc9860edee..ce3345ed92 100644 > --- a/elf/Makefile > +++ b/elf/Makefile > @@ -33,6 +33,7 @@ routines = \ > $(all-dl-routines) \ > dl-addr \ > dl-addr-obj \ > + dl-early_allocate \ > dl-error \ > dl-iteratephdr \ > dl-libc \ > @@ -108,6 +109,7 @@ all-dl-routines = $(dl-routines) $(sysdep-dl-routines) > # But they are absent from the shared libc, because that code is in ld.so. > elide-routines.os = \ > $(all-dl-routines) \ > + dl-early_allocate \ > dl-exception \ > dl-origin \ > dl-reloc-static-pie \ > @@ -276,6 +278,7 @@ tests-static-normal := \ > tst-linkall-static \ > tst-single_threaded-pthread-static \ > tst-single_threaded-static \ > + tst-tls-allocation-failure-static \ > tst-tlsalign-extern-static \ > tst-tlsalign-static \ > # tests-static-normal > @@ -1213,6 +1216,10 @@ $(objpfx)tst-glibcelf.out: tst-glibcelf.py elf.h $(..)/scripts/glibcelf.py \ > --cc="$(CC) $(patsubst -DMODULE_NAME=%,-DMODULE_NAME=testsuite,$(CPPFLAGS))" \ > < /dev/null > $@ 2>&1; $(evaluate-test) > > +ifeq ($(run-built-tests),yes) > +tests-special += $(objpfx)tst-tls-allocation-failure-static-patched.out > +endif > + > # The test requires shared _and_ PIE because the executable > # unit test driver must be able to link with the shared object > # that is going to eventually go into an installed DSO. > @@ -2937,3 +2944,15 @@ $(eval $(call tst-trace-skeleton,4,\ > $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4)) > $(eval $(call tst-trace-skeleton,5,\ > $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4:$(objpfx)libtracemod5)) > + > +$(objpfx)tst-tls-allocation-failure-static-patched: \ > + $(objpfx)tst-tls-allocation-failure-static $(..)scripts/tst-elf-edit.py > + cp $< $@ > + $(PYTHON) $(..)scripts/tst-elf-edit.py --maximize-tls-size $@ > + > +$(objpfx)tst-tls-allocation-failure-static-patched.out: \ > + $(objpfx)tst-tls-allocation-failure-static-patched > + $< > $@ 2>&1; echo "status: $$?" >> $@ > + grep -q '^Fatal glibc error: Cannot allocate TLS block$$' $@ \ > + && grep -q '^status: 127$$' $@; \ > + $(evaluate-test) > diff --git a/elf/dl-early_allocate.c b/elf/dl-early_allocate.c > new file mode 100644 > index 0000000000..61677aaa03 > --- /dev/null > +++ b/elf/dl-early_allocate.c > @@ -0,0 +1,30 @@ > +/* Early memory allocation for the dynamic loader. Generic version. > + Copyright (C) 2022 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 > + . */ > + > +#include > +#include > +#include > + > +void * > +_dl_early_allocate (size_t size) > +{ > + void *result = __sbrk (size); > + if (result == (void *) -1) > + result = NULL; > + return result; > +} > diff --git a/elf/tst-tls-allocation-failure-static.c b/elf/tst-tls-allocation-failure-static.c > new file mode 100644 > index 0000000000..8de831b246 > --- /dev/null > +++ b/elf/tst-tls-allocation-failure-static.c > @@ -0,0 +1,31 @@ > +/* Base for test program with impossiblyh large PT_TLS segment. > + Copyright (C) 2022 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 > + . */ > + > +/* The test actual binary is patched using scripts/tst-elf-edit.py > + --maximize-tls-size, and this introduces the expected test > + allocation failure due to an excessive PT_LS p_memsz value. > + > + Patching the binary is required because on some 64-bit targets, TLS > + relocations can only cover a 32-bit range, and glibc-internal TLS > + variables such as errno end up outside that range. */ > + > +int > +main (void) > +{ > + return 0; > +} > diff --git a/scripts/tst-elf-edit.py b/scripts/tst-elf-edit.py > index a514179bbf..0e19ce1e73 100644 > --- a/scripts/tst-elf-edit.py > +++ b/scripts/tst-elf-edit.py > @@ -43,9 +43,11 @@ EI_DATA=5 > ELFDATA2LSB=b'\x01' > ELFDATA2MSB=b'\x02' > > +ET_EXEC=2 > ET_DYN=3 > > PT_LOAD=1 > +PT_TLS=7 > > def elf_types_fmts(e_ident): > endian = '<' if e_ident[EI_DATA] == ELFDATA2LSB else '>' > @@ -146,8 +148,15 @@ def elf_edit_align(phdr, align): > else: > phdr.p_align = int(align) > > +def elf_edit_maximize_tls_size(phdr, elfclass): > + if elfclass == ELFCLASS32: > + # It is possible that the kernel can allocate half of the > + # address space, so use something larger. > + phdr.p_memsz = 0xfff00000 > + else: > + phdr.p_memsz = 1 << 63 > > -def elf_edit(f, align): > +def elf_edit(f, opts): > ei_nident_fmt = 'c' * EI_NIDENT > ei_nident_len = struct.calcsize(ei_nident_fmt) > > @@ -172,24 +181,35 @@ def elf_edit(f, align): > > ehdr = Elf_Ehdr(e_ident) > ehdr.read(f) > - if ehdr.e_type != ET_DYN: > - error('{}: not a shared library'.format(f.name)) > + if ehdr.e_type not in (ET_EXEC, ET_DYN): > + error('{}: not an executable or shared library'.format(f.name)) > > phdr = Elf_Phdr(e_ident) > + maximize_tls_size_done = False > for i in range(0, ehdr.e_phnum): > f.seek(ehdr.e_phoff + i * phdr.len) > phdr.read(f) > - if phdr.p_type == PT_LOAD: > - elf_edit_align(phdr, align) > + if phdr.p_type == PT_LOAD and opts.align is not None: > + elf_edit_align(phdr, opts.align) > + f.seek(ehdr.e_phoff + i * phdr.len) > + phdr.write(f) > + break > + if phdr.p_type == PT_TLS and opts.maximize_tls_size: > + elf_edit_maximize_tls_size(phdr, e_ident[EI_CLASS]) > f.seek(ehdr.e_phoff + i * phdr.len) > phdr.write(f) > + maximize_tls_size_done = True > break > > + if opts.maximize_tls_size and not maximize_tls_size_done: > + error('{}: TLS maximum size was not updated'.format(f.name)) > > def get_parser(): > parser = argparse.ArgumentParser(description=__doc__) > - parser.add_argument('-a', dest='align', required=True, > + parser.add_argument('-a', dest='align', > help='How to set the LOAD alignment') > + parser.add_argument('--maximize-tls-size', action='store_true', > + help='Set maximum PT_TLS size') > parser.add_argument('output', > help='ELF file to edit') > return parser > @@ -199,7 +219,7 @@ def main(argv): > parser = get_parser() > opts = parser.parse_args(argv) > with open(opts.output, 'r+b') as fout: > - elf_edit(fout, opts.align) > + elf_edit(fout, opts) > > > if __name__ == '__main__': > diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h > index 4a5e698db2..5d0369358d 100644 > --- a/sysdeps/generic/ldsodefs.h > +++ b/sysdeps/generic/ldsodefs.h > @@ -1211,6 +1211,11 @@ extern struct link_map * _dl_get_dl_main_map (void) > # endif > #endif > > +/* Perform early memory allocation, avoding a TCB dependency. > + Terminate the process if allocation fails. May attempt to use > + brk. */ > +void *_dl_early_allocate (size_t size) attribute_hidden; > + > /* Initialize the DSO sort algorithm to use. */ > #if !HAVE_TUNABLES > static inline void > diff --git a/sysdeps/unix/sysv/linux/dl-early_allocate.c b/sysdeps/unix/sysv/linux/dl-early_allocate.c > new file mode 100644 > index 0000000000..52c538e85a > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/dl-early_allocate.c > @@ -0,0 +1,82 @@ > +/* Early memory allocation for the dynamic loader. Generic version. > + Copyright (C) 2022 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 > + . */ > + > +/* Mark symbols hidden in static PIE for early self relocation to work. */ > +#if BUILD_PIE_DEFAULT > +# pragma GCC visibility push(hidden) > +#endif > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +/* Defined in brk.c. */ > +extern void *__curbrk; > + > +void * > +_dl_early_allocate (size_t size) > +{ > + void *result; > + > + if (__curbrk != NULL) > + /* If the break has been initialized, brk must have run before, > + so just call it once more. */ > + { > + result = __sbrk (size); > + if (result == (void *) -1) > + result = NULL; > + } > + else > + { > + /* If brk has not been invoked, there is no need to update > + __curbrk. The first call to brk will take care of that. */ > + void *previous = __brk_call (0); > + result = __brk_call (previous + size); > + if (result == previous) > + result = NULL; > + else > + result = previous; > + } Why do you need to avoid update __curbrk here? Otherwise it seems that a __sbrk() should be suffice here. Rest looks ok. > + > + /* If brk fails, fall back to mmap. This can happen due to > + unfortunate ASLR layout decisions and kernel bugs, particularly > + for static PIE. */ > + if (result == NULL) > + { > + long int ret; > + int prot = PROT_READ | PROT_WRITE; > + int flags = MAP_PRIVATE | MAP_ANONYMOUS; > +#ifdef __NR_mmap2 > + ret = MMAP_CALL_INTERNAL (mmap2, 0, size, prot, flags, -1, 0); > +#else > + ret = MMAP_CALL_INTERNAL (mmap, 0, size, prot, flags, -1, 0); > +#endif > + if (INTERNAL_SYSCALL_ERROR_P (ret)) > + result = NULL; > + else > + result = (void *) ret; > + } > + > + return result; > +}