From patchwork Tue Mar 11 17:09:52 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 872465 Delivered-To: patch@linaro.org Received: by 2002:a5d:64ce:0:b0:38f:210b:807b with SMTP id f14csp1595914wri; Tue, 11 Mar 2025 10:18:20 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCUxepLyVsMpLayuXxJ6DBzENUkvDHEOS2MqxzcTvIcZ4f4yXl+HwYiohu9C4H5dkpcb0y8gzw==@linaro.org X-Google-Smtp-Source: AGHT+IGIrd6961LJTksd+yVggqRp2QC/j8+3pxFiJRTbdmOjOWhVFHZpU58pwjTYxsaLmNnsp96C X-Received: by 2002:a05:6102:3752:b0:4c1:9288:906c with SMTP id ada2fe7eead31-4c30a5e8ce8mr11265827137.9.1741713500684; Tue, 11 Mar 2025 10:18:20 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1741713500; cv=pass; d=google.com; s=arc-20240605; b=lY3wAgzoi7yrhQGQdkRlEr5w87VqA0N35wXRaAVVAOB5bwLiOyKb6yi0wWe1KBWbjZ Jl3AbXpZCkh9hcO5eMO1sacuAoKFEeT0bYEVy7erb9aA/6AMKfhBOpResJ3HOM20NZND 8tbztOBaDZFDEy37rk3yPMLLAbOCDr/2dmMx1aU1sO5ffEi1YC5JSOgZYo7XZphkVOAy mcyiTsTMtTKw41uaieJ5C/E6bDHnQrU8rOvuikLK7v1r0LtK3pT3RSZcUlB2Hza1hX2c ZKpGiFsqDRQHeW2C79juGP1SQ4NM3OG14MK+VRkYqOByo95ds5NCbEH8wFHWdt+bDkM6 V9/g== 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=wMGcSe5SZydZnvho56iyXDzmNMG4L/TGAYC53A5VlkU=; fh=KcDe9xnl32Q8s5hC3CF4r26ysVeQspjxpbjrMk+PnIQ=; b=innw32NeGw2PYcorczJslk1gidotTt6WEItJ79A7rE5n+MnHQcGbaf/6jpMHk4c4mv O1zySRAZfpJIKkrLB/y+kPhL++U0DKVV39II+orYOw+R+P/23HuQV3dld0IRlUit8eGM owDCvD0BV3cb84aLD7IR7YEUTGfApg4QE/MtNK+hHLBB1/NygeNjpKo5fWUjD5bns4yq G7i5b0XAa8Kn8PgnAQHuuBZnBrovv7mDS87MmMql4ujZBx73EW/cQyRvxMAdvF+PoVKa F1trY7dWOguX9MJhs0jS2Mseh8YiD1NPAAfJSliLJCrjG6CiF0g3lFZNXmV37S5+xolc 8fBg==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=iBpLcPPV; 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 ada2fe7eead31-4c2fbd606casi2802711137.663.2025.03.11.10.18.20 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 Mar 2025 10:18:20 -0700 (PDT) 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=iBpLcPPV; 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 2BABA3857718 for ; Tue, 11 Mar 2025 17:18:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2BABA3857718 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=iBpLcPPV X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pj1-x1031.google.com (mail-pj1-x1031.google.com [IPv6:2607:f8b0:4864:20::1031]) by sourceware.org (Postfix) with ESMTPS id 61727385770D for ; Tue, 11 Mar 2025 17:13:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 61727385770D 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 61727385770D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1031 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741713206; cv=none; b=dXo0CPiQoP1mdbVMSm8akqHLqxaMe9yZRZpiL/mnm5VMZvGSqorgbDCay4VPiEKfjodtkkWsNO0pnNnDoDXOrP7BNaQkTPCSRcot1ei9Jb+mRCayGn0jWlMjkwlCCXDFMmxyvcrzyVhPYUtSmEyHsOXf/HimD4+JmnxgGC9I7Do= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741713206; c=relaxed/simple; bh=z7quWWkQzN7gPgdYwied7VhUjb3IvrTzhRZ9bGSXoV0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=QIM72UL8BQfHxJYllygsZjocaP+PsnJ6wnyUqkUmKcC2wpu5z1ok4sZS4Tn+4xcGD1aD+nHuoPMI03EUdoHfzFSwZOZ2jS8YvpPnRAcQ9cNcnww5ZJRkeXU4kUnFM3GejPTpiLvoEfT+fbs7YBzr8DiiJN/y5R/Bt6lhnItM7sE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 61727385770D Received: by mail-pj1-x1031.google.com with SMTP id 98e67ed59e1d1-2ff6e91cff5so8402135a91.2 for ; Tue, 11 Mar 2025 10:13:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1741713205; x=1742318005; 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=wMGcSe5SZydZnvho56iyXDzmNMG4L/TGAYC53A5VlkU=; b=iBpLcPPVr/mF3FZp2Na6C2L88/HnrtFdeUfZGu8Q0KjzsIFBJv/2XbvKKp+KSA6AvW c/PyUfZxTmRyVZ7YfrodIrinxIjuE7YwMny6S2dp73+Wc/Ogu8zLYSgyGSp6YZbhlimZ i1oM1n0PhEZq613zDd0tms60ZvptbvzlLtigkuFyALCkS4Y3RInBxISflIorzLvWWy+a Ty1ekUDhv7S1Jg//tSSdHnJGgkpPCGygdIkFXDnqXO/b8f6CWqlcm8dIIvL6HmptUPP3 sI4s6ANUZv3Pf7mPbjs0dQPDeTEDw5m5EMrQp76OwOuKuG+Po4AfVlpkGkUscFHrT0xn dWCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741713205; x=1742318005; 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=wMGcSe5SZydZnvho56iyXDzmNMG4L/TGAYC53A5VlkU=; b=P0/9c/brD+H0QaYdNxqjHPPC2bwns48Bwm8FKZLVGcw1jJIz+YzOYLNPHaybZ4+mdT YvilIcvkjmukVfwvvzdxQWy7ekDmY63gD0DMV1Oypyg7Gsfhq8qgHWZcVXYOOXLYRNfr KLViWjTkyd80QvSZFZCkaWO4j0ruo94dMeI+PwT3TWrqD6wlvvfgTySnsWcXU50+m2rF 5u9Ryt8woChEfBcf6rKgjGynfB/WV+EQEEuVLuVNxRSWIp/lZsa8VcSCiMHErCrBZ8Mu LrDRA8g4+KqXfAv9A5jSSEXSmzW5dk9m4gcealG4Ni3hf9cNmP114Ri5BqWOArM+FHyc YWjw== X-Gm-Message-State: AOJu0YxvML0mqlviM6cXVaLNva2wv3tuBOheu+WsolkB6Yw75F7Vc2TO Eh5D7ClyBm/EfGi9x0VT38vOTLSx+YFhMoI8B/NhGtK7H3T8oYnLoaz4yVdOwMSRWcYuQIQfaic / X-Gm-Gg: ASbGnctTFTuBKUPYEHSSeTwX1+YuEvufjB8P8V5At7FIK4DErYIFCMbL0bN9efxIAG1 mbBZlMJt9p0j6egSJ5iGYqNs7AibVL56XtXpCxCCJ0Iib+BozNgGq4gwnYgWAlGhYcJGzQjUn7f s9w1qrcG3T+prmiA9r1Osj0h+etkb26lbFgsX/kzqiFTTPCxJAWz61NMwMNSgwK45hgvBJ5s+Id VJtfUu4lwmilRBdRZKqaU01G2RzMeAIxFI5BJheTfNf6pRzPjVvx9MkVucn07jI/xuO3d3DBo9u pARI3VXgvLrtWb7lsfWBu4BMrEh6HAETF9ex9CaCnwxJt0h/CbV5OPHzkpbtAcm5hA== X-Received: by 2002:a17:90b:4b11:b0:2f1:2fa5:1924 with SMTP id 98e67ed59e1d1-2ff7cf0726cmr23147032a91.26.1741713202920; Tue, 11 Mar 2025 10:13:22 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c1:1ebf:8b5:8f5b:dd39:866]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2ff693f8804sm11438131a91.47.2025.03.11.10.13.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 Mar 2025 10:13:22 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: Jeff Xu , Florian Weimer , "H . J . Lu" , Yury Khrustalev Subject: [PATCH v6 5/9] elf: Add support to memory sealing Date: Tue, 11 Mar 2025 14:09:52 -0300 Message-ID: <20250311171305.89091-6-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250311171305.89091-1-adhemerval.zanella@linaro.org> References: <20250311171305.89091-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 marking a memory mapping to avoidi further changes (such as changing 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 dependencies. * Any library loaded with dlopen with RTLD_NODELETE flag. * Audit modules. The memory sealing is controlled 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. The GNU_PROPERTY_MEMORY_SEAL enforcement depends on whether the kernel supports the mseal syscall and how glibc is configured. On the default configuration that aims to support older kernel releases, the memory sealing attribute is taken as a hint. If glibc is configured with a minimum kernel of 6.10, where mseal is implied to be supported, sealing is enforced. Checked on x86_64-linux-gnu and aarch64-linux-gnu. --- NEWS | 10 ++++++ elf/dl-load.c | 4 +++ elf/dl-map-segments.h | 17 ++++++++-- elf/dl-reloc.c | 54 ++++++++++++++++++++++++++++++ elf/dl-support.c | 7 ++++ elf/elf.h | 2 ++ elf/rtld.c | 13 ++++++- elf/setup-vdso.h | 2 ++ include/link.h | 8 +++++ sysdeps/aarch64/dl-prop.h | 5 +++ sysdeps/generic/dl-mseal.h | 22 ++++++++++++ sysdeps/generic/dl-prop-mseal.h | 34 +++++++++++++++++++ sysdeps/generic/dl-prop.h | 5 +++ sysdeps/generic/ldsodefs.h | 10 ++++++ sysdeps/unix/sysv/linux/Makefile | 4 +++ sysdeps/unix/sysv/linux/dl-mseal.c | 51 ++++++++++++++++++++++++++++ sysdeps/unix/sysv/linux/dl-mseal.h | 31 +++++++++++++++++ sysdeps/x86/dl-prop.h | 4 +++ 18 files changed, 279 insertions(+), 4 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 4732ec2522..fc460ede05 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,16 @@ 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. + + If the GNU_PROPERTY_MEMORY_SEAL gnu attribute it not present on the binary, + memory sealing will not be applied for its dependencies (and even if the + objects has the memory sealing attribute). + Deprecated and removed features, and other changes affecting compatibility: [Add deprecations, removals and changes affecting compatibility here] diff --git a/elf/dl-load.c b/elf/dl-load.c index 4998652adf..f104cc7544 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1447,6 +1447,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 ee68dda550..b154b14836 100644 --- a/elf/dl-map-segments.h +++ b/elf/dl-map-segments.h @@ -19,6 +19,7 @@ #include #include +#include /* Map a segment and align it properly. */ @@ -116,11 +117,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 (GLRO(dl_enable_seal) && l->l_seal == lt_seal_toseal) + _dl_mseal (hole_start, hole_size, l->l_name); } l->l_contiguous = 1; @@ -218,6 +223,12 @@ _dl_map_segments (struct link_map *l, int fd, } __set_vma_name ((void*)zeropage, zeroend - zeropage, bssname); } + + /* 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 (GLRO(dl_enable_seal) && l->l_seal == lt_seal_toseal) + _dl_mseal (mapat, zeroend - zeropage, l->l_name); } } diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index 05bf54bebd..2b37676182 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); } void @@ -369,6 +371,58 @@ cannot apply additional memory protection after relocation"); } } +static void +_dl_mseal_map_1 (struct link_map *l, bool dep) +{ + if (!GLRO(dl_enable_seal)) + return; + + /* The DEP is used internaly to memory seal audit modules after they are + dynamic loaded, since they might be unloaded if some constraints are not + met (for instance, if la_version is not supported). + For dlopen without RTLD_NODELETE, do not apply memory sealing. */ + if (l->l_seal == lt_seal_dont + || (dep + ? l->l_seal != lt_seal_dont_dlopen + : l->l_seal == lt_seal_dont_dlopen)) + return; + + if (l->l_contiguous) + _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start, + l->l_name); + else + { + /* We can use the PT_LOAD segments because even if relro splits the + original RW VMA, mseal works with multiple VMAs with different + protection 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; + _dl_mseal ((void *) mapstart, allocend - mapstart, l->l_name); + } + break; + } + } + + l->l_seal = lt_seal_sealed; +} + +void +_dl_mseal_map (struct link_map *l, bool dep) +{ + if (l->l_searchlist.r_list == NULL || !dep) + _dl_mseal_map_1 (l, dep); + else + for (unsigned int i = 0; i < l->l_searchlist.r_nlist; ++i) + _dl_mseal_map_1 (l->l_searchlist.r_list[i], dep); +} + 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 d8f1dd8ee9..ab74f3b51c 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. */ @@ -164,6 +166,8 @@ uint64_t _dl_hwcap4; enum dso_sort_algorithm _dl_dso_sort_algo; +bool _dl_enable_seal; + /* The value of the FPU control word the kernel will preset in hardware. */ fpu_control_t _dl_fpu_control = _FPU_DEFAULT; @@ -344,6 +348,8 @@ _dl_non_dynamic_init (void) break; } + GLRO(dl_enable_seal) = _dl_main_map.l_seal == lt_seal_toseal; + if ((__glibc_unlikely (GL(dl_stack_flags)) & PF_X) && TUNABLE_GET (glibc, rtld, execstack, int32_t, NULL) == 0) _dl_fatal_printf ("Fatal glibc error: executable stack is not allowed\n"); @@ -352,6 +358,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); } #ifdef DL_SYSINFO_IMPLEMENTATION diff --git a/elf/elf.h b/elf/elf.h index c0f61489ec..f7d38eeffb 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -1359,6 +1359,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 727e16f233..25058cb242 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -52,6 +52,7 @@ #include #include #include +#include #include @@ -478,6 +479,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) _dl_rtld_map.l_real = &_dl_rtld_map; _dl_rtld_map.l_map_start = (ElfW(Addr)) &__ehdr_start; _dl_rtld_map.l_map_end = (ElfW(Addr)) _end; + _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 @@ -1023,6 +1025,10 @@ 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 + after after loading (including its dependencies) */ + _dl_mseal_map (dlmargs.map, true); } /* Load all audit modules. */ @@ -1101,6 +1107,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 @@ -1216,6 +1223,8 @@ rtld_setup_main_map (struct link_map *main_map) break; } + GLRO(dl_enable_seal) = main_map->l_seal == lt_seal_toseal; + /* Adjust the address of the TLS initialization image in case the executable is actually an ET_DYN object. */ if (main_map->l_tls_initimage != NULL) @@ -2318,8 +2327,10 @@ dl_main (const ElfW(Phdr) *phdr, __rtld_malloc_init_real (main_map); } - /* All ld.so initialization is complete. Apply RELRO. */ + /* All ld.so initialization is complete. Apply RELRO and memory + sealing. */ _dl_protect_relro (&_dl_rtld_map); + _dl_mseal_map (&_dl_rtld_map, false); /* Relocation is complete. Perform early libc initialization. This is the initial libc, even if audit modules have been loaded with diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h index 935d9e3baf..5119fa1321 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 518bfd1670..677d82b38b 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 abca2be7fa..888308eed0 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 *) @@ -50,6 +52,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 1; + if (type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { /* Stop if the property note is ill-formed. */ diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h new file mode 100644 index 0000000000..b100a7cb2c --- /dev/null +++ b/sysdeps/generic/dl-mseal.h @@ -0,0 +1,22 @@ +/* Memory sealing. Generic version. + Copyright (C) 2025 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 void +_dl_mseal (void *addr, size_t len, const char *object) +{ +} diff --git a/sysdeps/generic/dl-prop-mseal.h b/sysdeps/generic/dl-prop-mseal.h new file mode 100644 index 0000000000..36a7e71e4b --- /dev/null +++ b/sysdeps/generic/dl-prop-mseal.h @@ -0,0 +1,34 @@ +/* Support for GNU properties. Generic version. + Copyright (C) 2025 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 _DL_PROP_MSEAL_H + +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 6d4e62ea84..ac53061dea 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 1; + /* 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 8465cbaa9b..6c8d6fadba 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -642,6 +642,8 @@ struct rtld_global_ro EXTERN enum dso_sort_algorithm _dl_dso_sort_algo; + EXTERN bool _dl_enable_seal; + #ifdef SHARED /* We add a function table to _rtld_global which is then used to call the function instead of going through the PLT. The result @@ -1021,6 +1023,14 @@ 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, and it is + used for the case where sealing is done after loading (for instance + for audit modules). */ +extern void _dl_mseal_map (struct link_map *map, bool dep) 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 ae46e0726d..a72635e340 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -652,6 +652,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..74ab688ef3 --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-mseal.c @@ -0,0 +1,51 @@ +/* Memory sealing. Linux version. + Copyright (C) 2025 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 +#include + +void +_dl_mseal (void *addr, size_t len, const char *object) +{ + int r = 0; + bool fail = false; +#if __ASSUME_MSEAL + r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0); + fail = r != 0; +#else + static int mseal_supported = true; + /* Avoid issuing mseal again if it is not supported by the kernel. */ + if (atomic_load_relaxed (&mseal_supported)) + { + int r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0); + if (r == -ENOSYS) + atomic_store_relaxed (&mseal_supported, false); + else + fail = r != 0; + } +#endif + if (fail) + { + static const char errstring[] = N_("\ +cannot apply memory sealing"); + _dl_signal_error (-r, DSO_FILENAME (object), NULL, errstring); + } +} diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h new file mode 100644 index 0000000000..b0a9e9523d --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-mseal.h @@ -0,0 +1,31 @@ +/* Memory sealing. Linux version. + Copyright (C) 2025 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_MSEAL_H +#define _DL_MSEAL_H + +/* Seal the ADDR or size LEN to protect against fruthermodifications, 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. + + Any error than than unsupported by the kerneltriggers a _dl_signal_error. */ +void _dl_mseal (void *addr, size_t len, const char *object) attribute_hidden; + +#endif diff --git a/sysdeps/x86/dl-prop.h b/sysdeps/x86/dl-prop.h index 9a5e10821c..e9e1940530 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 *) @@ -243,6 +244,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 1; + unsigned int needed_1 = 0; unsigned int feature_1_and = 0; unsigned int isa_1_needed = 0;