From patchwork Tue Mar 11 17:09:55 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 872470 Delivered-To: patch@linaro.org Received: by 2002:a5d:64ce:0:b0:38f:210b:807b with SMTP id f14csp1598994wri; Tue, 11 Mar 2025 10:25:35 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCXw+BBgzTnjsZdGgPzooz5HJrDrbZV+KSDEnwb0jOEcpRaDbRkwzu74LXiyDlLdzgm8q7Sx3g==@linaro.org X-Google-Smtp-Source: AGHT+IE7L7HjRs/vK24WlNHfV/GNd8jNpbVerm9cB+O2G31TMSzijWRV+nUgoTPuxIlJyfQg/KSF X-Received: by 2002:ac8:5844:0:b0:476:8612:f029 with SMTP id d75a77b69052e-4768612f242mr117775891cf.48.1741713935646; Tue, 11 Mar 2025 10:25:35 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1741713935; cv=pass; d=google.com; s=arc-20240605; b=Omvmy3lR+/i9wmNv0u6ZWDmgKGH8qYvV6t8d+JB0QDUYh1ar5zAYtgWv8VAcLRYgHJ Xh/bLbPM/nRPJS5xjC6H4z+jnhZIiOFlCKCdVduHHRdgXRuRa6sIyV+/LIEgOzWEly0Q XT0IRYX9viwmaOTYBTWvhhkFN3bU8UGaM2GwW8mFENTpGE701DaFR3fzuttbIS9pev/L 6T2ZIgmy75hB7fawwKiGTE3/gFzvt2nTg84wdUFTh0w8qWG2p3x+N6x9SeqwjLZOe6/g kRNwRat+eI+bXpOiLrYR6kxNaktVRVnzc9nE97KFGQatiTbeYmBIMc1y8VBXwmd8kR1D 7Mxg== 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=oVp7RRm19QIFZVtmKrgo8+0Hn6WP4QAZT+lg6tXT1tk=; fh=KcDe9xnl32Q8s5hC3CF4r26ysVeQspjxpbjrMk+PnIQ=; b=DeqSkBH94H34yTTXAbdjUnDtOIx+i2MUS098FVolM9bzxCS+AsFFfVRaxya7OlX9HE bBQx1BAeu5OYsjdXaBY7RK/+GckpHBi3mYfuCn+RaRitq5yrAdYGJ34LcWPuQMSiawOG euK613/im2DW2CJU+Ik1sf9wGNzdVo6cex/mdNZZ+p0fpbdBeZCxSlfjFrTI837phS6x vPoIN6BOOGftS9P3PI03umNA8icRyHBvi7k4dfXpz8TfTAlimzwuOEnDGrFoIExTteue KSEkMrkIGxiEBBKlAjD9aNoh7YTRzo9H3Mh328KUgCZK9K7P45d/ld2pomJk6UuPrhRI IB8g==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=zB0TYWOv; 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 d75a77b69052e-476731ab509si67090191cf.470.2025.03.11.10.25.35 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 Mar 2025 10:25:35 -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=zB0TYWOv; 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 24CE63857737 for ; Tue, 11 Mar 2025 17:25:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 24CE63857737 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=zB0TYWOv X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com [IPv6:2607:f8b0:4864:20::1033]) by sourceware.org (Postfix) with ESMTPS id 975953857C78 for ; Tue, 11 Mar 2025 17:13:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 975953857C78 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 975953857C78 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1033 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741713211; cv=none; b=XJdPZ3Z76V3RFwiGsoQcvxBp79hhgvDapOF1kKyy7RQdcmW9JtEKEGJWUuTvRVxuAHWO56GLc9B+x7cDqy8gBpsdRzTZpMCRl0mIM6rq/2aw3UdFc+Zc6mG5eyXFiySb9Odz1n0ichtKVXDnck3WvS7gG/46qkbPHk/DtAGVIkI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741713211; c=relaxed/simple; bh=VWhdE4tHlSAlHamy2w+K4imthWEy6k56IhF0dF+tbFA=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=iDVUBgt/gYZVBXTMXimNApBIn0/Uln9Kx08WCHXH0AawAVwbnmZY2/FoaIvQ6lcw6wNLZLQWSbu6l4kkaDg7xO+VUbKq3Iz4bc6yQGzdbXO0LBhODX+hSzkZLrSp29p5Mwy63aekCMVZoen7pqG/9//mS9+FKL2D+SbXaNBFSv0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 975953857C78 Received: by mail-pj1-x1033.google.com with SMTP id 98e67ed59e1d1-2ff04f36fd2so9709321a91.1 for ; Tue, 11 Mar 2025 10:13:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1741713210; x=1742318010; 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=oVp7RRm19QIFZVtmKrgo8+0Hn6WP4QAZT+lg6tXT1tk=; b=zB0TYWOviSVv7npGX5qkttpEyKMTD+9FdL4jwOitAOZTG144vUtaGuMUDBqqqrBhN0 LvUptWk1c4rbgdAdRJkzVllRiw45R5H1Yq4ty2QdwyqHPcTUtwHVeUIEq/xQtTkufV7W vo986tOh45AkAK6Weroi8jl6Dc645QvXj1NxnrmRu/iQUbOzNXek34RyUEighwfwgyw4 ksEM1Q4L0BZT4b2ReTkTnDvEjH+e7E1XaN0EBHLdVJ+/xp8I6N7JzLjdpMjKd6jOq514 P2m8EM0KjhLy/CYFQ3K6Ggf4TWwfb9O0KfRhGxJHyz69dlwisIFn/xxwkQHA+AXuRoUM 6oeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741713210; x=1742318010; 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=oVp7RRm19QIFZVtmKrgo8+0Hn6WP4QAZT+lg6tXT1tk=; b=MRJwYeNWA9cDmix4WLb6tUfmlJCrGQOLSQ56oIukZU0d65kHjPh4CWUoEzsFc9/+md 3LNCyHQu02KQnJVBDexVY6Juwt5XVaiRzCjFbQ3ebwvzgT0I/k0giTq5MNH/PlYUn/7G 5ihqyUD35DM15ZvZwd1chrwdTPNjU+qYF6CYniH8JeP3pm8WHJJ4FbrJbOjyBNvxqeM4 1OD2/IRdK5Ufab6PmIcq+KlUOqPAC0P/cXp2FPdadWWxNu2xWm6tVKrjudMmW61pAstb hjcqgcDnlJzoWsPKQjpVklF7jgFuonN4M9JvEDRuL2cr6imU4k1kO/+YIYM2+/0BlegB BlMg== X-Gm-Message-State: AOJu0Yx95blctKnBLUogPFCBiubxD2J+9yaUtwIZO6IHzjSE3bOFtLkL VkGqciMeKhHqJ3x12WkVoZSEydsR+QW0GdJlSm7D752qvVvVi0bg5wim+ED8EjZxbJWqxs6zos8 x X-Gm-Gg: ASbGncvi9f8hKclv1txRjHyjXgzU5HmkcsIuVl+augd8pw7t59l9KbZwj2koq6SPyzL QYsyMlwzqHNEZ6ejuEX1j1tQBnO/uXSW1dsh+TYCDlLIG1/T3TZMwVZfWGjxkc8MnwLXbC6Aj5D TFX5Fa9GBqLTEeG0sPO6ikLqqR6K1rm2ZKH8TFRDqX4IJO6dY0RhJELBPkAx4WIeUHeH672II4V kaaBR820oFLunI/A/40M5HSsXGuCAOKQPAhwIyT2fE+TQrnhTUA96kpoYPEx2V+x+52bTdC7ve0 cnsiQMDqpZs8nrVt64tXjrYJi7BXsHZ6hPYnNOugQ6vidKz9vtKM3pw= X-Received: by 2002:a17:90b:384a:b0:2fe:ba7f:8032 with SMTP id 98e67ed59e1d1-2ff7ce6ec12mr29711271a91.9.1741713210192; Tue, 11 Mar 2025 10:13:30 -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.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 Mar 2025 10:13:29 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: Jeff Xu , Florian Weimer , "H . J . Lu" , Yury Khrustalev Subject: [PATCH v6 8/9] linux: Add support for PT_GNU_MUTABLE Date: Tue, 11 Mar 2025 14:09:55 -0300 Message-ID: <20250311171305.89091-9-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 Since memory sealing is applied for all the PT_LOAD segments, a new program header is also supported so users can define a region where sealing should not be applied (so users can later initialize and change memory protection). The special section, PT_GNU_MUTABLE (reference implemented at [1]), marks a memory region that should not be sealed if the GNU_PROPERTY_MEMORY_SEAL attribute is present. The section name starts with ".gnu.mutable" and has an alignment and size of the defined maximum page size (-z max-page-size linker option). For instance the code: #define GNU_MUTABLE_SECTION_NAME ".gnu.mutable" unsigned char mutable_array1[64] __attribute__ ((section (GNU_MUTABLE_SECTION_NAME))) = { 0 }; unsigned char mutable_array2[32] __attribute__ ((section (GNU_MUTABLE_SECTION_NAME))) = { 0 }; places both 'mutable_array1' and 'mutable_array2' on a page aligned memory region with the size of a page. The linker sets the alignment and size to simplify support for ABIs with multiple page sizes, otherwise user would need to know the maximum page size to correctly define the alignment and size of the variable. Checked on aarch64-linux-gnu and x86_64-linux-gnu. [1] https://sourceware.org/git/?p=binutils-gdb.git;a=shortlog;h=refs/heads/azanella/pt_gnu_mutable --- configure | 33 +++ configure.ac | 19 ++ elf/dl-load.c | 5 + elf/dl-reloc.c | 29 ++- elf/dl-support.c | 5 + elf/elf.h | 2 + elf/rtld.c | 5 + include/link.h | 4 + sysdeps/unix/sysv/linux/Makefile | 23 ++ .../sysv/linux/tst-dl_mseal-mutable-dlopen.c | 1 + .../sysv/linux/tst-dl_mseal-mutable-mod.c | 47 ++++ .../sysv/linux/tst-dl_mseal-mutable-mod.h | 33 +++ .../sysv/linux/tst-dl_mseal-mutable-static.c | 2 + .../unix/sysv/linux/tst-dl_mseal-mutable.c | 242 ++++++++++++++++++ 14 files changed, 446 insertions(+), 4 deletions(-) create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-dlopen.c create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.c create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.h create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-static.c create mode 100644 sysdeps/unix/sysv/linux/tst-dl_mseal-mutable.c diff --git a/configure b/configure index dda60ed91d..83c79147dd 100755 --- a/configure +++ b/configure @@ -7465,6 +7465,39 @@ have-z-memory-seal = $libc_cv_z_memory_seal" config_vars="$config_vars enable-memory-seal = $enable_memory_sealing" +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PT_GNU_MUTABLE support" >&5 +printf %s "checking PT_GNU_MUTABLE support... " >&6; } +if test ${libc_cv_gnu_mutable+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat > conftest.c <&5 + (eval $ac_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then + if $READELF -lW conftest | grep 'GNU_MUTABLE' > /dev/null; then + libc_cv_gnu_mutable=yes + fi +fi +rm -r conftest* ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_gnu_mutable" >&5 +printf "%s\n" "$libc_cv_gnu_mutable" >&6; } +config_vars="$config_vars +have-pt-gnu-mutable = $libc_cv_gnu_mutable" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GLOB_DAT reloc" >&5 printf %s "checking for GLOB_DAT reloc... " >&6; } diff --git a/configure.ac b/configure.ac index d514179e1b..f62ccd4545 100644 --- a/configure.ac +++ b/configure.ac @@ -1378,6 +1378,25 @@ fi LIBC_CONFIG_VAR([have-z-memory-seal], [$libc_cv_z_memory_seal]) LIBC_CONFIG_VAR([enable-memory-seal], [$enable_memory_sealing]) +AC_CACHE_CHECK([PT_GNU_MUTABLE support], + libc_cv_gnu_mutable, [dnl +cat > conftest.c <&AS_MESSAGE_LOG_FD]) +then + if $READELF -lW conftest | grep 'GNU_MUTABLE' > /dev/null; then + libc_cv_gnu_mutable=yes + fi +fi +rm -r conftest*]) +LIBC_CONFIG_VAR([have-pt-gnu-mutable], [$libc_cv_gnu_mutable]) + AC_CACHE_CHECK(for GLOB_DAT reloc, libc_cv_has_glob_dat, [dnl diff --git a/elf/dl-load.c b/elf/dl-load.c index f104cc7544..a0d7d30e58 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1220,6 +1220,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, l->l_relro_addr = ph->p_vaddr; l->l_relro_size = ph->p_memsz; break; + + case PT_GNU_MUTABLE: + l->l_mutable_addr = ph->p_vaddr; + l->l_mutable_size = ph->p_memsz; + break; } if (__glibc_unlikely (nloadcmds == 0)) diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index 2b37676182..d706a57101 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -37,7 +37,6 @@ # define bump_num_cache_relocations() ((void) 0) #endif - /* We are trying to perform a static TLS relocation in MAP, but it was dynamically loaded. This can only work if there is enough surplus in the static TLS area already allocated for each running thread. If this @@ -371,6 +370,29 @@ cannot apply additional memory protection after relocation"); } } +static void +_dl_mseal_map_2 (const struct link_map *l, ElfW(Addr) map_start, + ElfW(Addr) map_end) +{ + ElfW(Addr) mutable_start = 0, mutable_end = 0; + if (l->l_mutable_size != 0) + { + mutable_start = l->l_addr + l->l_mutable_addr; + mutable_end = mutable_start + l->l_mutable_size; + } + + if (mutable_start >= map_start && mutable_end < map_end) + { + size_t seg1_size = mutable_start - map_start; + size_t seg2_size = map_end - mutable_end; + _dl_mseal ((void *) map_start, seg1_size, l->l_name); + if (seg2_size != 0) + _dl_mseal ((void *) mutable_end, seg2_size, l->l_name); + } + else + _dl_mseal ((void *) map_start, map_end - map_start, l->l_name); +} + static void _dl_mseal_map_1 (struct link_map *l, bool dep) { @@ -388,8 +410,7 @@ _dl_mseal_map_1 (struct link_map *l, bool dep) return; if (l->l_contiguous) - _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start, - l->l_name); + _dl_mseal_map_2 (l, l->l_map_start, l->l_map_end); else { /* We can use the PT_LOAD segments because even if relro splits the @@ -404,7 +425,7 @@ _dl_mseal_map_1 (struct link_map *l, bool dep) 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); + _dl_mseal_map_2 (l, mapstart, allocend); } break; } diff --git a/elf/dl-support.c b/elf/dl-support.c index ab74f3b51c..6227397237 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -334,6 +334,11 @@ _dl_non_dynamic_init (void) _dl_main_map.l_relro_addr = ph->p_vaddr; _dl_main_map.l_relro_size = ph->p_memsz; break; + + case PT_GNU_MUTABLE: + _dl_main_map.l_mutable_addr = ph->p_vaddr; + _dl_main_map.l_mutable_size = ph->p_memsz; + break; } /* Process program headers again, but scan them backwards so that PT_NOTE can be skipped if PT_GNU_PROPERTY exits. */ diff --git a/elf/elf.h b/elf/elf.h index f7d38eeffb..efde5bae73 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -729,6 +729,7 @@ typedef struct #define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ #define PT_GNU_PROPERTY 0x6474e553 /* GNU property */ #define PT_GNU_SFRAME 0x6474e554 /* SFrame segment. */ +#define PT_GNU_MUTABLE 0x6474f555 /* Like bss, but not immutable. */ #define PT_LOSUNW 0x6ffffffa #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ @@ -1352,6 +1353,7 @@ typedef struct /* Note section name of program property. */ #define NOTE_GNU_PROPERTY_SECTION_NAME ".note.gnu.property" +#define GNU_MUTABLE_SECTION_NAME ".gnu.mutable" /* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). */ diff --git a/elf/rtld.c b/elf/rtld.c index 25058cb242..c2e2904aad 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -1209,6 +1209,11 @@ rtld_setup_main_map (struct link_map *main_map) main_map->l_relro_addr = ph->p_vaddr; main_map->l_relro_size = ph->p_memsz; break; + + case PT_GNU_MUTABLE: + main_map->l_mutable_addr = ph->p_vaddr; + main_map->l_mutable_size = ph->p_memsz; + break; } /* Process program headers again, but scan them backwards so that PT_NOTE can be skipped if PT_GNU_PROPERTY exits. */ diff --git a/include/link.h b/include/link.h index 677d82b38b..c77fbf10de 100644 --- a/include/link.h +++ b/include/link.h @@ -353,6 +353,10 @@ struct link_map ElfW(Addr) l_relro_addr; size_t l_relro_size; + /* Information used to not memory seal after relocations are done. */ + ElfW(Addr) l_mutable_addr; + size_t l_mutable_size; + unsigned long long int l_serial; }; diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 8fe74be95f..2faa377fd5 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -708,6 +708,8 @@ modules-names += \ tst-dl_mseal-dlopen-2-1 \ tst-dl_mseal-mod-1 \ tst-dl_mseal-mod-2 \ + tst-dl_mseal-mutable-dlopen \ + tst-dl_mseal-mutable-mod \ tst-dl_mseal-preload \ # modules-names @@ -731,6 +733,10 @@ $(objpfx)tst-dl_mseal-noseal.out: \ $(objpfx)tst-dl_mseal-dlopen-2.so \ $(objpfx)tst-dl_mseal-dlopen-2-1.so +$(objpfx)tst-dl_mseal-mutable.out: \ + $(objpfx)tst-dl_mseal-mutable-mod.so \ + $(objpfx)tst-dl_mseal-mutable-dlopen.so + ifeq ($(enable-memory-seal),yes) CFLAGS-tst-dl_mseal.c += -DDEFAULT_MEMORY_SEAL CFLAGS-tst-dl_mseal-noseal.c += -DDEFAULT_MEMORY_SEAL @@ -738,6 +744,8 @@ endif LDFLAGS-tst-dl_mseal = -Wl,--no-as-needed -Wl,-z,memory-seal LDFLAGS-tst-dl_mseal-static = -Wl,--no-as-needed -Wl,-z,memory-seal +LDFLAGS-tst-dl_mseal-mutable = -Wl,--no-as-needed -Wl,-z,memory-seal +LDFLAGS-tst-dl_mseal-mutable-static = -Wl,-z,memory-seal LDFLAGS-tst-dl_mseal-mod-1.so = -Wl,--no-as-needed -Wl,-z,memory-seal LDFLAGS-tst-dl_mseal-mod-2.so = -Wl,-z,memory-seal LDFLAGS-tst-dl_mseal-dlopen-1.so = -Wl,--no-as-needed @@ -763,6 +771,21 @@ tst-dl_mseal-ARGS = -- $(host-test-program-cmd) tst-dl_mseal-static-ARGS = -- $(host-test-program-cmd) tst-dl_mseal-noseal-ARGS = -- $(host-test-program-cmd) tst-dl_mseal-static-noseal-ARGS = -- $(host-test-program-cmd) + +ifeq ($(have-pt-gnu-mutable),yes) +tests-static += \ + tst-dl_mseal-mutable-static \ + # tests-static + +tests += \ + tst-dl_mseal-mutable \ + # tests + +LDFLAGS-tst-dl_mseal-mutable-mod.so = -Wl,-z,memory-seal +LDFLAGS-tst-dl_mseal-mutable-dlopen.so = -Wl,-z,memory-seal + +$(objpfx)tst-dl_mseal-mutable: $(objpfx)tst-dl_mseal-mutable-mod.so +endif # $(have-pt-gnu-mutable) == yes endif endif # $(subdir) == elf diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-dlopen.c b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-dlopen.c new file mode 100644 index 0000000000..325b2004b5 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-dlopen.c @@ -0,0 +1 @@ +#include "tst-dl_mseal-mutable-mod.c" diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.c b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.c new file mode 100644 index 0000000000..fb7cf03925 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.c @@ -0,0 +1,47 @@ +/* Check if PT_OPENBSD_MUTABLE is correctly applied. + 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 "tst-dl_mseal-mutable-mod.h" + +static unsigned char mutable_array1[128] + __attribute__ ((section (GNU_MUTABLE_SECTION_NAME))) + = { 0 }; +static unsigned char mutable_array2[256] + __attribute__ ((section (GNU_MUTABLE_SECTION_NAME))) + = { 0 }; + +static unsigned char immutable_array[256]; + +struct array_t +get_mutable_array1 (void) +{ + return (struct array_t) { mutable_array1, sizeof (mutable_array1) }; +} + +struct array_t +get_mutable_array2 (void) +{ + return (struct array_t) { mutable_array2, sizeof (mutable_array2) }; +} + +struct array_t +get_immutable_array (void) +{ + return (struct array_t) { immutable_array, sizeof (immutable_array) }; +} diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.h b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.h new file mode 100644 index 0000000000..cb0153756b --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-mod.h @@ -0,0 +1,33 @@ +/* Check if PT_OPENBSD_MUTABLE is correctly applied. + 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 + +#define LIB_DLOPEN "tst-dl_mseal-mutable-dlopen.so" + +struct array_t +{ + unsigned char *arr; + size_t size; +}; + +typedef struct array_t (*get_array_t)(void); + +struct array_t get_mutable_array1 (void); +struct array_t get_mutable_array2 (void); +struct array_t get_immutable_array (void); diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-static.c b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-static.c new file mode 100644 index 0000000000..550ef3f056 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable-static.c @@ -0,0 +1,2 @@ +#define TEST_STATIC 1 +#include "tst-dl_mseal-mutable.c" diff --git a/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable.c b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable.c new file mode 100644 index 0000000000..2ea233d4d8 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-dl_mseal-mutable.c @@ -0,0 +1,242 @@ +/* Check if PT_OPENBSD_MUTABLE is correctly applied. + 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tst-dl_mseal-mutable-mod.h" + +static long int pagesize; + +/* To check if the protection flags are correctly set, the thread tries + read/writes on it and checks if a SIGSEGV is generated. */ + +static volatile sig_atomic_t signal_jump_set; +static sigjmp_buf signal_jmp_buf; + +static void +sigsegv_handler (int sig) +{ + if (signal_jump_set == 0) + return; + + siglongjmp (signal_jmp_buf, sig); +} + +static bool +try_access_buf (unsigned char *ptr, bool write) +{ + signal_jump_set = true; + + bool failed = sigsetjmp (signal_jmp_buf, 0) != 0; + if (!failed) + { + if (write) + *(volatile unsigned char *)(ptr) = 'x'; + else + *(volatile unsigned char *)(ptr); + } + + signal_jump_set = false; + return !failed; +} + +struct range_t +{ + const char *name; + unsigned char *start; + size_t size; + bool found; +}; + +static int +callback (struct dl_phdr_info *info, size_t size, void *data) +{ + struct range_t *range = data; + if (strcmp (info->dlpi_name, range->name) != 0) + return 0; + + for (size_t i = 0; i < info->dlpi_phnum; i++) + if (info->dlpi_phdr[i].p_type == PT_GNU_MUTABLE) + { + range->start = (unsigned char *) info->dlpi_phdr[i].p_vaddr; + range->size = info->dlpi_phdr[i].p_memsz; + range->found = true; + break; + } + + return 0; +} + +static bool +find_mutable_range (void *addr, struct range_t *range) +{ + struct dl_find_object dlfo; + if (_dl_find_object (addr, &dlfo) != 0) + return false; + + range->name = dlfo.dlfo_link_map->l_name; + range->found = false; + dl_iterate_phdr (callback, range); + if (range->found) + range->start = dlfo.dlfo_link_map->l_addr + range->start; + + return range->found; +} + +static bool +__attribute_used__ +try_read_buf (unsigned char *ptr) +{ + return try_access_buf (ptr, false); +} + +static bool +__attribute_used__ +try_write_buf (unsigned char *ptr) +{ + return try_access_buf (ptr, true); +} + +/* The GNU_MUTABLE_SECTION_NAME section is page-aligned and with a size + multiple of page size. */ + +unsigned char mutable_array1[64] + __attribute__ ((section (GNU_MUTABLE_SECTION_NAME))) + = { 0 }; +unsigned char mutable_array2[32] + __attribute__ ((section (GNU_MUTABLE_SECTION_NAME))) + = { 0 }; + +unsigned char immutable_array[128]; + +static void +check_array (struct array_t *arr) +{ + TEST_COMPARE (try_write_buf (arr->arr), false); + TEST_COMPARE (try_write_buf (&arr->arr[arr->size/2]), false); + TEST_COMPARE (try_write_buf (&arr->arr[arr->size-1]), false); +} + +static void +check_mutable (struct array_t *mut1, + struct array_t *mut2, + struct array_t *imut) +{ + struct range_t range1; + struct range_t range2; + + TEST_VERIFY_EXIT (find_mutable_range (mut1->arr, &range1)); + TEST_VERIFY (mut1->arr >= range1.start); + TEST_VERIFY (mut1->arr + mut1->size <= range1.start + range1.size); + + TEST_VERIFY_EXIT (find_mutable_range (mut2->arr, &range2)); + TEST_VERIFY (mut2->arr >= range2.start); + TEST_VERIFY (mut2->arr + mut2->size <= range2.start + range2.size); + + /* Assume that both array will be placed in the same page since their + combined size is less than pagesize. */ + TEST_VERIFY (range1.start == range2.start); + TEST_VERIFY (range2.size == range2.size); + + if (test_verbose > 0) + printf ("mutable region: %-30s - %p-%p\n", + range1.name[0] == '\0' ? "main program" : basename (range1.name), + range1.start, + range1.start + range1.size); + + memset (mut1->arr, 0xaa, mut1->size); + memset (mut2->arr, 0xbb, mut2->size); + memset (imut->arr, 0xcc, imut->size); + + /* Sanity check, imut should be immutable. */ + { + void *start = PTR_ALIGN_DOWN (imut->arr, pagesize); + TEST_COMPARE (mprotect (start, pagesize, PROT_READ), -1); + TEST_COMPARE (errno, EPERM); + } + + /* Change permission of mutable region to just allow read. */ + xmprotect ((void *)range1.start, range1.size, PROT_READ); + + check_array (mut1); + check_array (mut2); +} + +static int +do_test (void) +{ + pagesize = xsysconf (_SC_PAGESIZE); + + { + struct sigaction sa = { + .sa_handler = sigsegv_handler, + .sa_flags = SA_NODEFER, + }; + sigemptyset (&sa.sa_mask); + xsigaction (SIGSEGV, &sa, NULL); + } + +#define ARR_TO_RANGE(__arr) \ + &((struct array_t) { __arr, sizeof (__arr) }) + + check_mutable (ARR_TO_RANGE (mutable_array1), + ARR_TO_RANGE (mutable_array2), + ARR_TO_RANGE (immutable_array)); + +#ifndef TEST_STATIC + { + struct array_t mut1 = get_mutable_array1 (); + struct array_t mut2 = get_mutable_array2 (); + struct array_t imut = get_immutable_array (); + check_mutable (&mut1, &mut2, &imut); + } + + { + void *h = xdlopen (LIB_DLOPEN, RTLD_NOW | RTLD_NODELETE); + +#define GET_ARRAY_DLOPEN(__name) \ + ({ \ + get_array_t f = xdlsym (h, __name); \ + f(); \ + }) + + struct array_t mut1 = GET_ARRAY_DLOPEN ("get_mutable_array1"); + struct array_t mut2 = GET_ARRAY_DLOPEN ("get_mutable_array2"); + struct array_t imut = GET_ARRAY_DLOPEN ("get_immutable_array"); + check_mutable (&mut1, &mut2, &imut); + } +#endif + + return 0; +} + +#include