From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id CBAF63858016 for ; Wed, 11 May 2022 16:59:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org CBAF63858016 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-479-lRXLJJVkO5me4ksgA1IzRg-1; Wed, 11 May 2022 12:59:29 -0400 X-MC-Unique: lRXLJJVkO5me4ksgA1IzRg-1 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 1AD87185A794; Wed, 11 May 2022 16:59:29 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.39.192.194]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1DAD154CAF6; Wed, 11 May 2022 16:59:27 +0000 (UTC) From: Florian Weimer To: binutils@sourceware.org, libc-alpha@sourceware.org Subject: PT_GNU_RELRO is somewhat broken Date: Wed, 11 May 2022 18:59:26 +0200 Message-ID: <871qx0dmz5.fsf@oldenburg.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.85 on 10.11.54.9 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-5.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: binutils@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Binutils mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 11 May 2022 16:59:34 -0000 PT_GNU_RELRO is supposed to identify a region in the process image which has to be flipped to PROT_READ (only) permission after relocation (=E2=80=9CRead-Only after RELocation=E2=80=9D). glibc has this code in the dynamic loader in elf/dl-reloc.c: | void | _dl_protect_relro (struct link_map *l) | { | ElfW(Addr) start =3D ALIGN_DOWN((l->l_addr | + l->l_relro_addr), | GLRO(dl_pagesize)); | ElfW(Addr) end =3D ALIGN_DOWN((l->l_addr | + l->l_relro_addr | + l->l_relro_size), | GLRO(dl_pagesize)); | if (start !=3D end | && __mprotect ((void *) start, end - start, PROT_READ) < 0) | { | static const char errstring[] =3D N_("\ | cannot apply additional memory protection after relocation"); | _dl_signal_error (errno, l->l_name, NULL, errstring); | } | } I assume the intent is to conservatively apply the largest possible RELRO region given GLRO(dl_pagesize), the run-time page size reported by the kernel. If the binary is built to a smaller page size (to save disk space), glibc can still load it, but apply only some RELRO protection. But _dl_relocate_object has a bug: to be conservative, it would have to use ALGIN_UP for the start (lower) address of the range. But it turns out we can't make this change without incurring a loss of hardening: BFD ld does not align the start address to a page boundary. For example, /bin/true in Fedora 35 x86-64 has this: | $ readelf -l /bin/true |=20 | Elf file type is DYN (Position-Independent Executable file) | Entry point 0x1960 | There are 13 program headers, starting at offset 64 |=20 | Program Headers: | Type Offset VirtAddr PhysAddr | FileSiz MemSiz Flags Align | PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 | 0x00000000000002d8 0x00000000000002d8 R 0x8 | INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318 | 0x000000000000001c 0x000000000000001c R 0x1 | [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] | LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 | 0x0000000000000ff8 0x0000000000000ff8 R 0x1000 | LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 | 0x00000000000029a1 0x00000000000029a1 R E 0x1000 | LOAD 0x0000000000004000 0x0000000000004000 0x0000000000004000 | 0x0000000000000d38 0x0000000000000d38 R 0x1000 | LOAD 0x0000000000005c78 0x0000000000006c78 0x0000000000006c78 | 0x0000000000000390 0x00000000000003a0 RW 0x1000 | DYNAMIC 0x0000000000005c90 0x0000000000006c90 0x0000000000006c90 | 0x00000000000001f0 0x00000000000001f0 RW 0x8 | NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338 | 0x0000000000000050 0x0000000000000050 R 0x8 | NOTE 0x0000000000000388 0x0000000000000388 0x0000000000000388 | 0x0000000000000044 0x0000000000000044 R 0x4 | GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338 | 0x0000000000000050 0x0000000000000050 R 0x8 | GNU_EH_FRAME 0x00000000000049c4 0x00000000000049c4 0x00000000000049c4 | 0x000000000000007c 0x000000000000007c R 0x4 | GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 | 0x0000000000000000 0x0000000000000000 RW 0x10 | GNU_RELRO 0x0000000000005c78 0x0000000000006c78 0x0000000000006c78 | 0x0000000000000388 0x0000000000000388 R 0x1 | [=E2=80=A6] The virtual address for PT_GNU_RELRO is 0x388, which is definitely not aligned to a 4K page. (0x388 + 0x6c78 =3D=3D 0x7000, so at least the end address is aligned.) In practice, this seems to work because the RELRO area seems to be at the start of the RW LOAD segment, so we can safely flip the slack space at the start of the page to RO. It still looks like a major wart to me, though. Any suggestions what should we do to fix this properly, mainly for targets that have varying page size in practice? Thanks, Florian