From patchwork Mon Sep 30 20:08:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 831577 Delivered-To: patch@linaro.org Received: by 2002:a5d:66c8:0:b0:367:895a:4699 with SMTP id k8csp2121082wrw; Mon, 30 Sep 2024 13:11:13 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCVJhEvqNecVwDVhr+IoBJrYtRXsrmcvp/+YxymRM6x4zdsqLsdi/5T+l/0nGDE1V7MRZW7KbA==@linaro.org X-Google-Smtp-Source: AGHT+IFRhYxzWdQLa/thOSZC5oPNl472kYWr2rX1WElPgSoLhPe+aj2lPOTH52+yVMwxXUHXTwPs X-Received: by 2002:a25:b127:0:b0:e26:78d:25ca with SMTP id 3f1490d57ef6-e26078d3545mr6756745276.57.1727727073001; Mon, 30 Sep 2024 13:11:13 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1727727072; cv=pass; d=google.com; s=arc-20240605; b=Kv04+0QkHVOjLh2hFYfZ0mI1Zj6dBdJJsgl5td355oop/yEbfYAbTDGOMeeWsQmWkA XH+5EDeI8aKjqFRfNRzABdsrfQtpgp7aU7Ht8Sm/Aw1Nh99Tt0o8BG7xqS+n5vaYS5Ew pDo6eUNQXVmxbQndtDrtZf258265W/37Jqadh02bHsa9PvkYNSy17mhZmlDESomaovcR FN65L3FBAaZNqibxgmBncdSVcB9VG/28awt4xcoW2EkgvLY2KPBaB25kIiXJl/kDNRO4 53wYJzOQmsHG/I+g7WZogktKqg8aW6AMRuadou+fjJdma7ef9BpJEF9KZLG1meudRVkX GzAw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature:arc-filter:dmarc-filter:delivered-to; bh=6enK2tjMh2S/+8qOtGnPNnjnO6c9mAtE2mV1r/axQ7I=; fh=3tUIaab7WH3k5DYf15UFmY7vUQNGmrLDHVJFJrkNDfY=; b=HHKN0Ty2KaM3szi162G5vVqOgAOKiQtl06naDVASmtJVZaKHZFY4HHduKim3ayIBWw IndO1kc59RSz0Mkpmgj0O7TBinY79Jg+4Vp4NoXur2ptOyuYE06dL4o1d0GgYEckZz1B 97aGOLxjFcJ+Ru+Nr83aw6pH5zUW7vNkTSVOU5MS2qV8G0LVSUqXKfxSRYJZapSLn7L0 qL4Ejnj1rVEYm4qwcLUuxJQkHo+ryQ6m2ApeSzHBY94r9Oy4EyhCJhFLYj4MJM3XlY17 Or6wA6npx2Rbom72OI+pBvBQRiJqTJ6iobEmM9oBWO6jkNUPSHKt8EYk8Ox1ZICc/1sE yzWg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=krfTXoMv; arc=pass (i=1); spf=pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="libc-alpha-bounces~patch=linaro.org@sourceware.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id d75a77b69052e-45c9f37561fsi95222831cf.555.2024.09.30.13.11.12 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Sep 2024 13:11:12 -0700 (PDT) Received-SPF: pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=krfTXoMv; arc=pass (i=1); spf=pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="libc-alpha-bounces~patch=linaro.org@sourceware.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 90E823845141 for ; Mon, 30 Sep 2024 20:11:12 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-il1-x12a.google.com (mail-il1-x12a.google.com [IPv6:2607:f8b0:4864:20::12a]) by sourceware.org (Postfix) with ESMTPS id D6FE03846401 for ; Mon, 30 Sep 2024 20:08:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D6FE03846401 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linaro.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D6FE03846401 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::12a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727726953; cv=none; b=YtIGn52/c7frQgeryRnUBtQ6lrmWIm9tYzHGWppqwU+7WbpCr7qhexIRfywanFlDME+txIMZ+ymO2Kj927PQOwIJAtnMTXUG1fYvv/M6G0OV2uzheb9I1EUqyhB+EuCky3J739/jFfOrdoVGVTXKSl4Q8aJ7jNapRrFq7FZtU64= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727726953; c=relaxed/simple; bh=gZPo3nShziOmGAbd+0pCgUu6Qm850DH0SgyD005yejE=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=xLiU25eyzfsvItfHkS7gq5dwSTKrMU23/sREX7orN57FI0RjFKlPIYE4FEyw5EyWrnWzi6q/aOcPB7rux0CX9+xYMRiREPKy+j0FkB9/zttlP+lVi7Tzaju71hS6IKBmuuKoZy3pmx7G3XhX0+S/m5SQtr9gTas2PuqkCNQLm/0= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-il1-x12a.google.com with SMTP id e9e14a558f8ab-3a0cbc08b38so24897635ab.0 for ; Mon, 30 Sep 2024 13:08:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1727726937; x=1728331737; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6enK2tjMh2S/+8qOtGnPNnjnO6c9mAtE2mV1r/axQ7I=; b=krfTXoMvHYhRH8wIi/i6bAgKrwkv1wMnlkocJLQmL/MZA36xHYqF74nZ/vIvhF+wFA vAO5Gvp2T2WAF5WwhnO3drBa7qHTgxKvYkOd9tDKNBfbn3eLbXXDB8VMzS/eIzaJLu8t WiZrNO3oe/DttSFrTHcWHTjncB0jztCs3d/9JxCmeQ4Jm/qWgnEPkISsEEPM4tepzLkb cT9hwYer+nwt9+H91p3tL8a8YGCAMY5FMouyLRGpchcTX2xtKZSv+F/RmlxBgRlBlsv3 FJRVQoNnS/AOEL+JvWGIWuMqqgN8zSzlh2p9wr+6YgDM4OfRqHQHgzGHW78zyUZqPB3u lGAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727726937; x=1728331737; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6enK2tjMh2S/+8qOtGnPNnjnO6c9mAtE2mV1r/axQ7I=; b=AV0Qp643HrhIck0vvkFbV9v+yKr7grJHrrxxCJYb7dksAnp7IUlXbQTMJ68ZkwE+Pe nniZW93cs73misXtKXAJ0BapUGEAtOVJFlKFebYY1L13wRRgLP3yw1j4PcGzvM2m503J yfDdPmPUa7OQowIpe6DUidHGpTRhj3QzeWX4q7Es8+zuekEK/zL2P1cJRCoAWIMJM3bx Hm0GVk93Kutg5WkX3T5SlcPNEt8Fzi3N9bl4sAmQWuXSZsliUp2a9ovsXe/9F7acaSm+ J2476AUkk34Uw4C/WGgn/69gnB9gi5mi16YPtYOV89udDiyi0BPg0kVrGZ+QE9q9j/OL IMpA== X-Gm-Message-State: AOJu0Yx0vKWwqRLDMEY1T705i9SylfLRJQUQgSLFGdxMaKWLrFT5ac8w B8/R5BBPTtMWhElNPOZ7F1NTlSDjuk6Zf/vR6xj64keNbRXKtvCRehOAzOvs2vE3fcddogRT3mN 3V/Y= X-Received: by 2002:a05:6e02:1a6c:b0:3a0:98e9:1b7a with SMTP id e9e14a558f8ab-3a34514af43mr104310265ab.2.1727726936440; Mon, 30 Sep 2024 13:08:56 -0700 (PDT) Received: from ubuntu-vm.. (201-92-183-102.dsl.telesp.net.br. [201.92.183.102]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-7e6db2c4845sm6869565a12.43.2024.09.30.13.08.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Sep 2024 13:08:56 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: Stephen Roettger , Jeff Xu , Florian Weimer , Mike Hommey , Adhemerval Zanella Subject: [PATCH v3 6/9] elf: Add support to memory sealing Date: Mon, 30 Sep 2024 17:08:28 -0300 Message-Id: <20240930200831.1669010-7-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240930200831.1669010-1-adhemerval.zanella@linaro.org> References: <20240930200831.1669010-1-adhemerval.zanella@linaro.org> MIME-Version: 1.0 X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_BARRACUDACENTRAL, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patch=linaro.org@sourceware.org The new Linux mseal syscall allows mark a memory mapping to avoid further changes (such as chaing the protection flags). The memory sealing is done in multiple places where the memory is supposed to be immutable during program execution: * All shared library dependencies from the binary, including the read-only segments after PT_GNU_RELRO setup. * The binary itself, including dynamic and static linked ones. In both cases, it is up either to binary or the loader to set up the sealing. * Any preload libraries, including depedencies. * Any library loaded with dlopen with RTLD_NODELETE flag. * Audit modules. * The loader bump allocator. The memory sealing is controled by a new gnu attribute, GNU_PROPERTY_MEMORY_SEAL, added by the new static linker option '-z memory-seal'. It is set per binary, including statically linked and shared objects. Checked on x86_64-linux-gnu and aarch64-linux-gnu. --- NEWS | 6 ++++ elf/dl-load.c | 4 +++ elf/dl-map-segments.h | 6 ++++ elf/dl-minimal-malloc.c | 3 ++ elf/dl-open.c | 4 +++ elf/dl-reloc.c | 51 ++++++++++++++++++++++++++++++ elf/dl-support.c | 7 ++++ elf/elf.h | 2 ++ elf/rtld.c | 8 +++++ elf/setup-vdso.h | 2 ++ include/link.h | 8 +++++ sysdeps/aarch64/dl-prop.h | 5 +++ sysdeps/generic/dl-mseal.h | 23 ++++++++++++++ sysdeps/generic/dl-prop-mseal.h | 36 +++++++++++++++++++++ sysdeps/generic/dl-prop.h | 5 +++ sysdeps/generic/ldsodefs.h | 8 +++++ sysdeps/unix/sysv/linux/Makefile | 4 +++ sysdeps/unix/sysv/linux/dl-mseal.c | 41 ++++++++++++++++++++++++ sysdeps/unix/sysv/linux/dl-mseal.h | 27 ++++++++++++++++ sysdeps/x86/dl-prop.h | 4 +++ 20 files changed, 254 insertions(+) create mode 100644 sysdeps/generic/dl-mseal.h create mode 100644 sysdeps/generic/dl-prop-mseal.h create mode 100644 sysdeps/unix/sysv/linux/dl-mseal.c create mode 100644 sysdeps/unix/sysv/linux/dl-mseal.h diff --git a/NEWS b/NEWS index 8cbee5eb74..f103b4995b 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,12 @@ Major new features: mappings to avoid further change during process execution such as protection permissions, unmapping, moving to another location, or shrinking the size. +* The loader will memory seal all libraries that contains the new gnu + attribute GNU_PROPERTY_MEMORY_SEAL. The memory sealing uses the new Linux + mseal syscall, and it will be applied to all shared libraries dependencies, + the binary, any preload and audit modules, and aby library loaded with + RTLD_NODELETE. + Deprecated and removed features, and other changes affecting compatibility: * The big-endian ARC port (arceb-linux-gnu) has been removed. diff --git a/elf/dl-load.c b/elf/dl-load.c index ac8e217a7f..41165287ae 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1415,6 +1415,10 @@ cannot enable executable stack as shared object requires"); /* Assign the next available module ID. */ _dl_assign_tls_modid (l); + if (l->l_seal == lt_seal_toseal + && (mode & __RTLD_DLOPEN) && !(mode & RTLD_NODELETE)) + l->l_seal = lt_seal_dont_dlopen; + #ifdef DL_AFTER_LOAD DL_AFTER_LOAD (l); #endif diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h index 30977cf800..7cdf9ebde0 100644 --- a/elf/dl-map-segments.h +++ b/elf/dl-map-segments.h @@ -18,6 +18,7 @@ . */ #include +#include /* Map a segment and align it properly. */ @@ -188,6 +189,11 @@ _dl_map_segments (struct link_map *l, int fd, -1, 0); if (__glibc_unlikely (mapat == MAP_FAILED)) return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; + /* We need to seal this here because it will not be part of + the PT_LOAD segments, nor it is taken in RELRO + calculation. */ + if (l->l_seal) + _dl_mseal (mapat, zeroend - zeropage); } } diff --git a/elf/dl-minimal-malloc.c b/elf/dl-minimal-malloc.c index 25d870728d..232fc5cf9c 100644 --- a/elf/dl-minimal-malloc.c +++ b/elf/dl-minimal-malloc.c @@ -27,6 +27,7 @@ #include #include #include +#include static void *alloc_ptr, *alloc_end, *alloc_last_block; @@ -62,6 +63,8 @@ __minimal_malloc (size_t n) if (page == MAP_FAILED) return NULL; __set_vma_name (page, nup, " glibc: loader malloc"); + if (GL(dl_rtld_map).l_seal == lt_seal_toseal) + _dl_mseal (page, nup); if (page != alloc_end) alloc_ptr = page; alloc_end = page + nup; diff --git a/elf/dl-open.c b/elf/dl-open.c index f251781dc4..63834ff692 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -793,6 +793,10 @@ dl_open_worker (void *a) if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", new->l_name, new->l_ns, new->l_direct_opencount); + + /* The seal flag is set only for NEW, however its dependencies could not be + unloaded and thus can also be sealed. */ + _dl_mseal_map (new, true, false); } void * diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index 4bf7aec88b..637870d0c7 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -28,6 +28,7 @@ #include <_itoa.h> #include #include "dynamic-link.h" +#include /* Statistics function. */ #ifdef SHARED @@ -347,6 +348,11 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], done, do it. */ if (l->l_relro_size != 0) _dl_protect_relro (l); + + /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD + segments because even if relro splits the the original RW VMA, + mseal works with multiple VMAs with different flags. */ + _dl_mseal_map (l, false, false); } @@ -369,6 +375,51 @@ cannot apply additional memory protection after relocation"); } } +static void +_dl_mseal_map_1 (struct link_map *l, bool force) +{ + /* The 'force' check allow to seal audit with sealing enabled after + they are loader during process startup. */ + if (l->l_seal == lt_seal_dont + || (force + ? l->l_seal != lt_seal_dont_dlopen + : l->l_seal == lt_seal_dont_dlopen)) + return; + + int r = -1; + if (l->l_contiguous) + r = _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start); + else + { + const ElfW(Phdr) *ph; + for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph) + switch (ph->p_type) + { + case PT_LOAD: + { + ElfW(Addr) mapstart = l->l_addr + + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1)); + ElfW(Addr) allocend = l->l_addr + ph->p_vaddr + ph->p_memsz; + r = _dl_mseal ((void *) mapstart, allocend - mapstart); + } + break; + } + } + + if (r == 0) + l->l_seal = lt_seal_sealed; +} + +void +_dl_mseal_map (struct link_map *l, bool dep, bool force) +{ + if (l->l_searchlist.r_list == NULL || !dep) + _dl_mseal_map_1 (l, force); + else + for (unsigned int i = 0; i < l->l_searchlist.r_nlist; ++i) + _dl_mseal_map_1 (l->l_searchlist.r_list[i], force); +} + void __attribute_noinline__ _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt) diff --git a/elf/dl-support.c b/elf/dl-support.c index bd02a77d1a..9fb185fb30 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -46,6 +46,7 @@ #include #include #include +#include extern char *__progname; char **_dl_argv = &__progname; /* This is checked for some error messages. */ @@ -100,6 +101,7 @@ static struct link_map _dl_main_map = .l_used = 1, .l_tls_offset = NO_TLS_OFFSET, .l_serial = 1, + .l_seal = lt_seal_dont, }; /* Namespace information. */ @@ -353,6 +355,11 @@ _dl_non_dynamic_init (void) /* Setup relro on the binary itself. */ if (_dl_main_map.l_relro_size != 0) _dl_protect_relro (&_dl_main_map); + + /* Seal the memory mapping after RELRO setup, we can use the PT_LOAD + segments because even if relro splits the the original RW VMA, + mseal works with multiple VMAs with different flags. */ + _dl_mseal_map (&_dl_main_map, false, false); } #ifdef DL_SYSINFO_IMPLEMENTATION diff --git a/elf/elf.h b/elf/elf.h index 33aea7f743..b9fe2064af 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -1357,6 +1357,8 @@ typedef struct #define GNU_PROPERTY_STACK_SIZE 1 /* No copy relocation on protected data symbol. */ #define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2 +/* No memory sealing. */ +#define GNU_PROPERTY_MEMORY_SEAL 3 /* A 4-byte unsigned integer property: A bit is set if it is set in all relocatable inputs. */ diff --git a/elf/rtld.c b/elf/rtld.c index 0f4b3e9a43..90eb798013 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -53,6 +53,7 @@ #include #include #include +#include #include @@ -478,6 +479,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) GL(dl_rtld_map).l_real = &GL(dl_rtld_map); GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start; GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; + GL(dl_rtld_map).l_seal = lt_seal_dont; /* Copy the TLS related data if necessary. */ #ifndef DONT_USE_BOOTSTRAP_MAP # if NO_TLS_OFFSET != 0 @@ -1042,6 +1044,11 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d); /* Mark the DSO as being used for auditing. */ dlmargs.map->l_auditing = 1; + + /* Audit modules can not be loaded with RTLD_NODELETE, so apply the sealing + again on all dependencies an and ignore any possible missing seal due + dlopen without RTLD_NODELETE. */ + _dl_mseal_map (dlmargs.map, true, true); } /* Load all audit modules. */ @@ -1124,6 +1131,7 @@ rtld_setup_main_map (struct link_map *main_map) /* And it was opened directly. */ ++main_map->l_direct_opencount; main_map->l_contiguous = 1; + main_map->l_seal = lt_seal_dont; /* A PT_LOAD segment at an unexpected address will clear the l_contiguous flag. The ELF specification says that PT_LOAD diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h index 888e1e4897..f115e6eb78 100644 --- a/elf/setup-vdso.h +++ b/elf/setup-vdso.h @@ -66,6 +66,8 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)), /* The vDSO is always used. */ l->l_used = 1; + /* The PT_LOAD may not cover all the vdso mapping. */ + l->l_seal = lt_seal_dont; /* Initialize l_local_scope to contain just this map. This allows the use of dl_lookup_symbol_x to resolve symbols within the vdso. diff --git a/include/link.h b/include/link.h index 5ed445d5a6..e8ee740099 100644 --- a/include/link.h +++ b/include/link.h @@ -214,6 +214,14 @@ struct link_map lt_library map. */ unsigned int l_tls_in_slotinfo:1; /* TLS slotinfo updated in dlopen. */ + enum /* Memory sealing status. */ + { + lt_seal_dont = 0, /* Do not seal the object. */ + lt_seal_dont_dlopen, /* Do not seal from a dlopen. */ + lt_seal_toseal, /* The library is marked to be sealed. */ + lt_seal_sealed /* The library is sealed. */ + } l_seal:2; + /* NODELETE status of the map. Only valid for maps of type lt_loaded. Lazy binding sets l_nodelete_active directly, potentially from signal handlers. Initial loading of an diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h index df05c0211d..c66d9a49f0 100644 --- a/sysdeps/aarch64/dl-prop.h +++ b/sysdeps/aarch64/dl-prop.h @@ -19,6 +19,8 @@ #ifndef _DL_PROP_H #define _DL_PROP_H +#include + extern void _dl_bti_protect (struct link_map *, int) attribute_hidden; extern void _dl_bti_check (struct link_map *, const char *) @@ -45,6 +47,9 @@ static inline int _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type, uint32_t datasz, void *data) { + if (_dl_process_gnu_property_seal (l, fd, type, datasz, data)) + return 0; + if (!GLRO(dl_aarch64_cpu_features).bti) /* Skip note processing. */ return 0; diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h new file mode 100644 index 0000000000..dccf78ae38 --- /dev/null +++ b/sysdeps/generic/dl-mseal.h @@ -0,0 +1,23 @@ +/* Memory sealing. Generic version. + Copyright (C) 2024 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 + . */ + +static inline int +_dl_mseal (void *addr, size_t len) +{ + return 0; +} diff --git a/sysdeps/generic/dl-prop-mseal.h b/sysdeps/generic/dl-prop-mseal.h new file mode 100644 index 0000000000..b1f93a17fb --- /dev/null +++ b/sysdeps/generic/dl-prop-mseal.h @@ -0,0 +1,36 @@ +/* Support for GNU properties. Generic version. + Copyright (C) 2024 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 + . */ + +#ifndef _DL_PROP_MSEAL_H +#define _LD_PROP_MSEAL_H + +#include + +static __always_inline bool +_dl_process_gnu_property_seal (struct link_map *l, int fd, uint32_t type, + uint32_t datasz, void *data) +{ + if (type == GNU_PROPERTY_MEMORY_SEAL && datasz == 0) + { + l->l_seal = lt_seal_toseal; + return true; + } + return false; +} + +#endif diff --git a/sysdeps/generic/dl-prop.h b/sysdeps/generic/dl-prop.h index 1d92920a96..5fac690c81 100644 --- a/sysdeps/generic/dl-prop.h +++ b/sysdeps/generic/dl-prop.h @@ -19,6 +19,8 @@ #ifndef _DL_PROP_H #define _DL_PROP_H +#include + /* The following functions are used by the dynamic loader and the dlopen machinery to process PT_NOTE and PT_GNU_PROPERTY entries in the binary or shared object. The notes can be used to change the @@ -47,6 +49,9 @@ static inline int __attribute__ ((always_inline)) _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type, uint32_t datasz, void *data) { + if (_dl_process_gnu_property_seal (l, fd, type, datasz, data)) + return 0; + /* Continue until GNU_PROPERTY_1_NEEDED is found. */ if (type == GNU_PROPERTY_1_NEEDED) { diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 259ce2e7d6..e528b7ff83 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -1017,6 +1017,14 @@ extern void _dl_relocate_object (struct link_map *map, /* Protect PT_GNU_RELRO area. */ extern void _dl_protect_relro (struct link_map *map) attribute_hidden; +/* Issue memory sealing for the link map MAP. If MAP is contiguous the + whole region is sealed, otherwise iterate over the program headerrs and + seal each PT_LOAD segment.i + The DEP specify whether to seal the dependencies as well, while FORCE + ignores any possible missing seal due dlopen without RTLD_NODELETE. */ +extern void _dl_mseal_map (struct link_map *map, bool dep, bool force) + attribute_hidden; + /* Call _dl_signal_error with a message about an unhandled reloc type. TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R__* value. PLT is nonzero if this was a PLT reloc; it just affects the message. */ diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 887152997f..a49a83f530 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -643,6 +643,10 @@ sysdep-rtld-routines += \ dl-sbrk \ # sysdep-rtld-routines +dl-routines += \ + dl-mseal \ + # dl-routines + others += \ pldd \ # others diff --git a/sysdeps/unix/sysv/linux/dl-mseal.c b/sysdeps/unix/sysv/linux/dl-mseal.c new file mode 100644 index 0000000000..c99fd991cb --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-mseal.c @@ -0,0 +1,41 @@ +/* Memory sealing. Linux version. + Copyright (C) 2024 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 +#include + +int +_dl_mseal (void *addr, size_t len) +{ + int r; +#if __ASSUME_MSEAL + r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0); +#else + r = -ENOSYS; + static int mseal_supported = true; + if (atomic_load_relaxed (&mseal_supported)) + { + r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0); + if (r == -ENOSYS) + atomic_store_relaxed (&mseal_supported, false); + } +#endif + return r; +} diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h new file mode 100644 index 0000000000..25e3f724dc --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-mseal.h @@ -0,0 +1,27 @@ +/* Memory sealing. Linux version. + Copyright (C) 2024 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 + . */ + +/* Seal the ADDR or size LEN to protect against modifications, such as + changes on the permission flags (through mprotect), remap (through + mmap and/or remap), shrink, destruction changes (madvise with + MADV_DONTNEED), or change its size. The input has the same constraints + as the mseal syscall. + + Return 0 in case of success or a negative value otherwise (a negative + errno). */ +int _dl_mseal (void *addr, size_t len) attribute_hidden; diff --git a/sysdeps/x86/dl-prop.h b/sysdeps/x86/dl-prop.h index 08387dfaff..26a687d611 100644 --- a/sysdeps/x86/dl-prop.h +++ b/sysdeps/x86/dl-prop.h @@ -19,6 +19,7 @@ #ifndef _DL_PROP_H #define _DL_PROP_H +#include #include extern void _dl_cet_check (struct link_map *, const char *) @@ -241,6 +242,9 @@ _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type, uint32_t datasz, void *data) { /* This is called on each GNU property. */ + if (_dl_process_gnu_property_seal (l, fd, type, datasz, data)) + return 0; + unsigned int needed_1 = 0; unsigned int feature_1_and = 0; unsigned int isa_1_needed = 0;