From patchwork Fri Dec 6 17:37:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 847845 Delivered-To: patch@linaro.org Received: by 2002:a5d:50c2:0:b0:385:e875:8a9e with SMTP id f2csp912860wrt; Fri, 6 Dec 2024 09:46:05 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCV6v5p0exyJwGf9rimTJWrEQb0UVPBsjBuyxWu2avtVwjvXK1fNxEcr3stSnsn4jwgEmJX6xg==@linaro.org X-Google-Smtp-Source: AGHT+IETukS+MIxlSjdaHNhmKqNF3ktu7FIYeZK2BgN9diQgfQIhw+OTZ0JJHeZW2yr7TnxVeUzL X-Received: by 2002:a05:6214:20a8:b0:6d8:9815:92e2 with SMTP id 6a1803df08f44-6d8e7114c2cmr39994496d6.15.1733507164939; Fri, 06 Dec 2024 09:46:04 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1733507164; cv=pass; d=google.com; s=arc-20240605; b=EEHg1qPsOLn1lSmg+lMl//UkJexdpmj6v7y9+iddQrsYTQS8l9ulzcSffOJrEPjaHi g6+hzQ8pwL/8nlNrbaso+UMwNS/sUtQivgMdL+vZ+dGgiWd1NnHicIHyJPkI4laO154e u7IKL50gmrMDINJ2GwcLXCRlkUR05XAr5smMSuck6RBRFvE1SMbgJA4HlM93elKjzQSD BuxsupePVLx4b7WtiEIlA96IVCXJLAro4YB3O7oKRKQvq5lNrzCO3Kn98d/hPJQZfnLq 0gLZsCCKK5twEzSv4DHY5K2uEXypzab3sb6Ao9T0W6FDY7KuzI9euvRoirqKHVwTA0It En7Q== 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:dkim-filter:arc-filter:dmarc-filter :delivered-to:dkim-filter; bh=r28/8/7vxiDvbH92YDKz8XF+ejZGpNPM7J3OJAXRH+4=; fh=39H2oohB6ZYGhyKHcKf9chiYMCdt+XzUdxuRAxp69qM=; b=S2RWKFAoe5LKZoC2JTnCTnu50a70sgIUYv99+2pvGu1VdRfB1w2QQy2G2WGbWr6S/J 9ZMC3lAIsCXVw3V5zxGZZT9e/5S2LrRb9o93tUiGrNak8o1fu1nTtZZpBRnLWwJX5IBN RGykpzzs2q116BrT2iCCpUdTGly9wtDC6o4XGHnSeJSKmJIxmxZuaOeqQK7OJdZIHe1K C9i7cRRSpIMj33Jsq3C+gffMPJRIDnyOG3S6lzTR5cIINZtLY1Gj8++cr33AR+t2ysjd A+q/KHdHHc/a47CWdd0Uk1sseexThw0hU17MId9Q4ofFFDHv14zhrFzYriKLcqP2S+kO AXmA==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=Hy9Q6ia5; arc=pass (i=1); spf=pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c 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. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id 6a1803df08f44-6d8dac0a15fsi47528876d6.356.2024.12.06.09.46.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Dec 2024 09:46:04 -0800 (PST) Received-SPF: pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=Hy9Q6ia5; arc=pass (i=1); spf=pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c 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 7CB0B385840B for ; Fri, 6 Dec 2024 17:46:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7CB0B385840B Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=Hy9Q6ia5 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pf1-x430.google.com (mail-pf1-x430.google.com [IPv6:2607:f8b0:4864:20::430]) by sourceware.org (Postfix) with ESMTPS id 34CFB385840A for ; Fri, 6 Dec 2024 17:39:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 34CFB385840A 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 34CFB385840A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::430 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1733506750; cv=none; b=AjHYRQhUpi2y0W1kNeSK8jWB7fT/d1BYMTtQ/vqvi5I7bW6Zmbfzm9KsfSquKGpJ38FfDrQvSfGUIlPHq/1lKXOva3cEnUJ9S1qT7Z6kz14gjKF7t46PmWidzRZCD/vARgAk1o4Bh7M4yZrXhdfhijnjfLqvj3GLKHiUDk7kmcE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1733506750; c=relaxed/simple; bh=I9xop5N1cD8pjIt2nuUegx36bYbjA6WxF91wa7mgn8s=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=L0Tsi50PSOhMBHEEY2K0jNKI/5GrGq2qe/HwJpysjcyOoQxowe5rN1T4WxApEkLtkPT6bqddBvWgNP4bKJxhAHAwZk8qkaKUqauiZLBDcRgxBvHVnDnw62dv7yzCjrG68Q0HmD4rupGVTrju5tiN8gFO+BMzdcrgtKqjGoTBeFQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 34CFB385840A Received: by mail-pf1-x430.google.com with SMTP id d2e1a72fcca58-7242f559a9fso2534978b3a.1 for ; Fri, 06 Dec 2024 09:39:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1733506749; x=1734111549; 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=r28/8/7vxiDvbH92YDKz8XF+ejZGpNPM7J3OJAXRH+4=; b=Hy9Q6ia5mcasHp13REtn+l7abM8x8naOlreoVUKeOB8Dd87Kxb422j2OJ3o8Ay/vdR wHtr5NtBTxPqtg5YdeVCrF27jQUcRSnh8atmZqZsqDGrRPdyYKSdyxAvTQVmerG/oU+X oBMZmJ7SEKNyXK1fE1++8DxZ1xqaGkm9sLxHErX2Uf+pW8coSo4pjKm/cCYdra2ZA6p5 +pt15skw4ZJybx+oGLaBP36v20d8n3A9j2pFLrfUk8U5SJqskFEEAJQzm7sdX78+gGyJ +NnjTQp7C7dED/b4GkmYiy8jrKHcGQxwiXfzHF2G6pIVvBL8WqtBhDRidxmUOWip5i7U sMBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1733506749; x=1734111549; 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=r28/8/7vxiDvbH92YDKz8XF+ejZGpNPM7J3OJAXRH+4=; b=arTpAq6Z1c1VAD37X30bWZwJO4wTdDizgct1S2b105MNx9LHfoOEByChe3XukUswYE Vu3ejEPj8sZiSNnLqjocPUSk2JeD7qPqsge5/0E3slPYZ1K/PZlzsYXnHtWwtyQLt51S ZgEjBIhD6TutcDMpEaoZSVti2gD42rObY8AT9sHBU/tU75Qh2slQejDgkXft+1sQ7LF8 bxcdq35eiru8n0zVE96tSNJA5sf+FvKLDMC7gaTUAzvhRoHYHS9ALWnnsRs5ElWSOyij //wphmzAbhABBDfD3xwV5lPOoLuYQeRP3T6UjvomU61tnV4RySio72GcjXfqo/v/gn0J acqQ== X-Gm-Message-State: AOJu0Yw1o4wt4ZiyHwHDHXijYHBz4BAYrS9s+UUVFyL+eD6abF6WmvwO q8kb6IvKSoHKdHib2Ji1TrZtRLOLhvJTqsdh1NpE0iYCMgXXn4FSG2NcoH/jiHhZbFsEntSXx2B 0 X-Gm-Gg: ASbGncvbtoouSUl9P6U8uz2wvnbywpmJqZA7qVmkrA7JHI+38BwfFqCJL7d/HRqxPJF jZL0YpP20NQfzTiF81Zilg3h8k4wkfFOl32UgztNRAyXn1NgVBNGyv5AVOCQuwXuLLaFzzFRLnt uPhXKY5BDfuM/dvIWbxX158SO4Bp8ISZe3tp8iLPBBPCDLKBrS0iF2qx7SlIid+MpVNzhnmQRa5 y0OYYR01qwaMjDdz7W9qVTh9QsCjyak9JivRlAixiK/Jx5l6xewUV6vm9MHqQ== X-Received: by 2002:a17:902:da8a:b0:216:25a2:2ebe with SMTP id d9443c01a7336-21625a22f5fmr17493295ad.19.1733506748539; Fri, 06 Dec 2024 09:39:08 -0800 (PST) Received: from mandiga.. ([2804:1b3:a7c2:2d1:1460:dd43:b597:c3fc]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-215f8f29635sm31409495ad.241.2024.12.06.09.39.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Dec 2024 09:39:08 -0800 (PST) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: Jeff Xu , Florian Weimer , Mike Hommey , "H . J . Lu" Subject: [PATCH v4 6/9] elf: Add support to memory sealing Date: Fri, 6 Dec 2024 14:37:54 -0300 Message-ID: <20241206173850.3766841-7-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20241206173850.3766841-1-adhemerval.zanella@linaro.org> References: <20241206173850.3766841-1-adhemerval.zanella@linaro.org> MIME-Version: 1.0 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 | 16 ++++++++-- elf/dl-minimal-malloc.c | 3 ++ elf/dl-open.c | 4 +++ elf/dl-reloc.c | 50 ++++++++++++++++++++++++++++++ elf/dl-support.c | 3 ++ elf/elf.h | 2 ++ elf/rtld.c | 12 +++++-- 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 | 9 ++++++ 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, 259 insertions(+), 5 deletions(-) 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 d65eaeadf7..723dac1ccc 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,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 e986d7faab..b52c29ccb7 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..51c5286f6e 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. */ @@ -115,11 +116,15 @@ _dl_map_segments (struct link_map *l, int fd, if (__glibc_unlikely (loadcmds[nloadcmds - 1].mapstart < c->mapend)) return N_("ELF load command address/offset not page-aligned"); + + caddr_t hole_start = (caddr_t) (l->l_addr + c->mapend); + size_t hole_size = loadcmds[nloadcmds - 1].mapstart - c->mapend; + if (__glibc_unlikely - (__mprotect ((caddr_t) (l->l_addr + c->mapend), - loadcmds[nloadcmds - 1].mapstart - c->mapend, - PROT_NONE) < 0)) + (__mprotect (hole_start, hole_size, PROT_NONE) < 0)) return DL_MAP_SEGMENTS_ERROR_MPROTECT; + if (l->l_seal) + _dl_mseal (hole_start, hole_size); } l->l_contiguous = 1; @@ -188,6 +193,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 69fc19c1b7..a701ff89d5 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 f283a87144..36ee904eba 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -807,6 +807,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 76d14830dd..f8127cb166 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 @@ -345,6 +346,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], return; _dl_relocate_object_no_relro (l, scope, reloc_mode, consider_profiling); _dl_protect_relro (l); + _dl_mseal_map (l, false, false); } void @@ -369,6 +371,54 @@ 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 + { + /* We can use the PT_LOAD segments because even if relro splits the + original RW VMA, mseal works with multiple VMAs with different + flags. */ + 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 f0b6be07e9..e43b455de4 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. */ @@ -352,6 +354,7 @@ _dl_non_dynamic_init (void) /* Setup relro on the binary itself. */ _dl_protect_relro (&_dl_main_map); + _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 21d282fc87..71902de400 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 @@ -2344,8 +2352,8 @@ dl_main (const ElfW(Phdr) *phdr, at this point. */ __rtld_malloc_init_real (main_map); - if (GL(dl_rtld_map).l_relro_size != 0) - _dl_protect_relro (&GL(dl_rtld_map)); + _dl_protect_relro (&GL(dl_rtld_map)); + _dl_mseal_map (&GL(dl_rtld_map), false, false); rtld_timer_accum (&relocate_time, start); } 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 91447a5e77..dbfa5d7a6a 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -1024,6 +1024,15 @@ void _dl_relocate_object_no_relro (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. + The memory sealing should be done *after* RELRO setup. */ +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 a270b0af4c..8c1389cb0f 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -644,6 +644,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;