From patchwork Mon Apr 3 09:04:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: stsp X-Patchwork-Id: 67204 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 252D03857007 for ; Mon, 3 Apr 2023 09:08:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 252D03857007 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1680512889; bh=2T38M7MdIEu1JaqqWIPHNSGNebBacpmit7RkKbjjdpM=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=bPHfjwsNTmwkkMQi4t1fLoaGeBOLn+ERiA8W9HUVgHrsz4WFA97iwPnbAS3UCyKzQ rPMoN62FnGLjorX3G5fK6zszfXaBWnWSpt6tn+IoYSjCGsidqQklFJwMEuyfoEhX8w ijfdPngChE8bkKunLlnc/TCxScnkqja//FgivuPA= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from forward200b.mail.yandex.net (forward200b.mail.yandex.net [178.154.239.157]) by sourceware.org (Postfix) with ESMTPS id 71DF838432CB for ; Mon, 3 Apr 2023 09:04:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 71DF838432CB Received: from mail-nwsmtp-smtp-production-main-73.iva.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-73.iva.yp-c.yandex.net [IPv6:2a02:6b8:c0c:a810:0:640:6b9b:0]) by forward200b.mail.yandex.net (Yandex) with ESMTP id 716A3600E1 for ; Mon, 3 Apr 2023 12:04:58 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-73.iva.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id h4J5UKjDcmI0-wyOukZZ7; Mon, 03 Apr 2023 12:04:57 +0300 X-Yandex-Fwd: 1 To: libc-alpha@sourceware.org Cc: Stas Sergeev Subject: [PATCH 11/12] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag Date: Mon, 3 Apr 2023 14:04:20 +0500 Message-Id: <20230403090421.560208-12-stsp2@yandex.ru> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230403090421.560208-1-stsp2@yandex.ru> References: <20230403090421.560208-1-stsp2@yandex.ru> MIME-Version: 1.0 X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, 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.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Stas Sergeev via Libc-alpha From: stsp Reply-To: Stas Sergeev Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" This flag preserves the destination mapping by using memcpy() from the source buffer. It is useful if the backing-store was mapped with MAP_SHARED. This patch adds a test-case named tst-dlmem-shm. It maps solib into shm and checks that dlmem with that flag worked as expected, by resolving the solib symbols. Then it checks the new functionality of creating the library duplicate, that this flag permits. The test-suite was run on x86_64/64 and showed no regressions. Signed-off-by: Stas Sergeev --- dlfcn/Makefile | 5 +- dlfcn/dlfcn.h | 4 + dlfcn/glreflib1.c | 2 + dlfcn/tst-dlmem-shm.c | 169 ++++++++++++++++++++++++++++++++++++++++++ elf/dl-load.c | 36 ++++++++- elf/dl-map-segments.h | 4 +- 6 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 dlfcn/tst-dlmem-shm.c diff --git a/dlfcn/Makefile b/dlfcn/Makefile index 55e5f5fcdf..a71810e941 100644 --- a/dlfcn/Makefile +++ b/dlfcn/Makefile @@ -52,8 +52,10 @@ endif ifeq (yes,$(build-shared)) tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \ bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \ - bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-extfns + bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-extfns \ + tst-dlmem-shm CPPFLAGS-tst-dlmem-extfns.c += -DBUILDDIR=\"$(objpfx)\" +CPPFLAGS-tst-dlmem-shm.c += -DBUILDDIR=\"$(objpfx)\" endif modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \ defaultmod2 errmsg1mod modatexit modcxaatexit \ @@ -110,6 +112,7 @@ $(objpfx)glreflib1.img: $(objpfx)glreflib1.so cat $^ >>$@ dd if=/dev/urandom bs=512 count=1 >>$@ $(objpfx)tst-dlmem-extfns.out: $(objpfx)glreflib1.so $(objpfx)glreflib1.img +$(objpfx)tst-dlmem-shm.out: $(objpfx)glreflib1.so $(objpfx)tst-dlinfo.out: $(objpfx)glreflib3.so LDFLAGS-glreflib3.so = -Wl,-rpath,: diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h index fe5e5a7d09..7aa9d7d3cf 100644 --- a/dlfcn/dlfcn.h +++ b/dlfcn/dlfcn.h @@ -73,6 +73,10 @@ typedef void * (dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign, void *cookie); +/* Do not replace mapping created by premap callback. + dlmem() will then use memcpy(). */ +#define DLMEM_DONTREPLACE 1 + struct dlmem_args { /* Optional name to associate with the loaded object. */ const char *soname; diff --git a/dlfcn/glreflib1.c b/dlfcn/glreflib1.c index f26832fabe..bab3fcd1b0 100644 --- a/dlfcn/glreflib1.c +++ b/dlfcn/glreflib1.c @@ -22,3 +22,5 @@ ref1 (void) { return 42; } + +int bar = 35; diff --git a/dlfcn/tst-dlmem-shm.c b/dlfcn/tst-dlmem-shm.c new file mode 100644 index 0000000000..7899dfc909 --- /dev/null +++ b/dlfcn/tst-dlmem-shm.c @@ -0,0 +1,169 @@ +/* Test for dlmem into shm. + Copyright (C) 2000-2022 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 + +static size_t maplen; + +static void * +premap_dlmem (void *mappref, size_t maplength, size_t mapalign, void *cookie) +{ + int fd = * (int *) cookie; + int prot = PROT_READ | PROT_WRITE; + int err; + + /* See if we support such parameters. */ + if (mappref || mapalign > 4096) + return MAP_FAILED; + + fprintf (stderr, "%s\n", __func__); + + err = ftruncate (fd, maplength); + if (err) + error (EXIT_FAILURE, 0, "ftruncate() failed"); + maplen = maplength; + return mmap (NULL, maplength, prot, MAP_SHARED | MAP_FILE +#ifdef MAP_32BIT + | MAP_32BIT +#endif + , fd, 0); +} + +#define TEST_FUNCTION do_test +extern int do_test (void); + +int +do_test (void) +{ + void *handle; + void *addr; + int (*sym) (void); /* We load ref1 from glreflib1.c. */ + int *bar, *bar2; + unsigned char *addr2; + Dl_info info; + int ret; + int fd; + int num; + off_t len; + struct link_map *lm; + const char *shm_name = "/tst-dlmem"; + int shm_fd; + struct dlmem_args a; + + shm_fd = memfd_create (shm_name, 0); + if (shm_fd == -1) + error (EXIT_FAILURE, 0, "shm_open() failed"); + + fd = open (BUILDDIR "glreflib1.so", O_RDONLY); + if (fd == -1) + error (EXIT_FAILURE, 0, "cannot open: glreflib1.so"); + len = lseek (fd, 0, SEEK_END); + lseek (fd, 0, SEEK_SET); + addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) + error (EXIT_FAILURE, 0, "cannot mmap: glreflib1.so"); + a.soname = "glreflib1.so"; + a.flags = DLMEM_DONTREPLACE; + a.nsid = LM_ID_BASE; + a.premap = premap_dlmem; + a.cookie = &shm_fd; + handle = dlmem (addr, len, RTLD_NOW | RTLD_LOCAL, &a); + if (handle == NULL) + error (EXIT_FAILURE, 0, "cannot load: glreflib1.so"); + munmap (addr, len); + close (fd); + /* Check if premap was called. */ + TEST_VERIFY (maplen != 0); + + sym = dlsym (handle, "ref1"); + if (sym == NULL) + error (EXIT_FAILURE, 0, "dlsym failed"); + + memset (&info, 0, sizeof (info)); + ret = dladdr (sym, &info); + if (ret == 0) + error (EXIT_FAILURE, 0, "dladdr failed"); +#ifdef MAP_32BIT + /* Make sure MAP_32BIT worked. */ + if ((unsigned long) info.dli_fbase >= 0x100000000) + error (EXIT_FAILURE, 0, "premap audit didn't work"); +#endif + ret = dlinfo (handle, RTLD_DI_LINKMAP, &lm); + if (ret != 0) + error (EXIT_FAILURE, 0, "dlinfo failed"); + + printf ("info.dli_fname = %p (\"%s\")\n", info.dli_fname, info.dli_fname); + printf ("info.dli_fbase = %p\n", info.dli_fbase); + printf ("info.dli_sname = %p (\"%s\")\n", info.dli_sname, info.dli_sname); + printf ("info.dli_saddr = %p\n", info.dli_saddr); + printf ("lm->l_addr = %lx\n", lm->l_addr); + + if (info.dli_fname == NULL) + error (EXIT_FAILURE, 0, "dli_fname is NULL"); + if (info.dli_fbase == NULL) + error (EXIT_FAILURE, 0, "dli_fbase is NULL"); + if (info.dli_sname == NULL) + error (EXIT_FAILURE, 0, "dli_sname is NULL"); + if (info.dli_saddr == NULL) + error (EXIT_FAILURE, 0, "dli_saddr is NULL"); + + num = sym (); + if (num != 42) + error (EXIT_FAILURE, 0, "bad return from ref1"); + + /* Now try symbol duplication. */ + bar = dlsym (handle, "bar"); + if (bar == NULL) + error (EXIT_FAILURE, 0, "dlsym failed"); + TEST_COMPARE (*bar, 35); + /* write another value */ +#define TEST_BAR_VAL 48 + *bar = TEST_BAR_VAL; + + /* Create second instance of the solib. */ + addr2 = mmap (NULL, maplen, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_SHARED, shm_fd, 0); + if (addr2 == MAP_FAILED) + error (EXIT_FAILURE, 0, "cannot mmap shm\n"); + /* Find our bar symbol duplicate. */ + ret = dladdr (bar, &info); + if (ret == 0) + error (EXIT_FAILURE, 0, "dladdr failed"); + bar2 = (int *) (addr2 + (info.dli_saddr - info.dli_fbase)); + /* See if we found the right one. */ + TEST_COMPARE (*bar2, TEST_BAR_VAL); + + munmap (addr2, maplen); + close (shm_fd); + dlclose (handle); + + return 0; +} + + +#include diff --git a/elf/dl-load.c b/elf/dl-load.c index fd81a9103e..422c03459b 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -2352,6 +2352,31 @@ _dl_map_object (struct link_map *loader, const char *name, return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid); } +static void * +do_mmapcpy (void *addr, size_t length, int prot, int flags, + void *arg, off_t offset) +{ + const struct dlmem_fbuf *fb = arg; + size_t to_copy = 0; + + assert (flags & MAP_FIXED); + assert ((flags & MAP_ANONYMOUS) || fb); + + if (!(flags & MAP_ANONYMOUS) && offset < fb->len) + { + to_copy = length; + if (offset + to_copy > fb->len) + to_copy = fb->len - offset; + memcpy (addr, fb->buf + offset, to_copy); + } + /* memset the rest. */ + if (length > to_copy) + memset (addr + to_copy, 0, length - to_copy); + if (__mprotect (addr, length, prot) == -1) + return MAP_FAILED; + return addr; +} + static void * do_memremap (void *addr, size_t length, int prot, int flags, void *arg, off_t offset) @@ -2360,6 +2385,11 @@ do_memremap (void *addr, size_t length, int prot, int flags, size_t to_copy = 0; assert (flags & MAP_FIXED); + assert ((flags & MAP_ANONYMOUS) || fb); + + if (flags & MAP_ANONYMOUS) + return __mmap (addr, length, prot, flags, -1, 0); + if (offset < fb->len) { to_copy = length; @@ -2430,6 +2460,10 @@ ___dl_map_object_from_mem (struct link_map *loader, const char *name, struct r_debug *r = _dl_debug_update (nsid); bool make_consistent = false; struct r_file_id id = {}; + const struct dlmem_fbuf *fb = private; + unsigned dlmem_flags = fb->dlm_args ? fb->dlm_args->flags : 0; + __typeof (do_mmap) *m_map = (dlmem_flags & DLMEM_DONTREPLACE) + ? do_mmapcpy : do_memremap; assert (nsid >= 0); assert (nsid < GL(dl_nns)); @@ -2480,7 +2514,7 @@ ___dl_map_object_from_mem (struct link_map *loader, const char *name, void *stack_end = __libc_stack_end; if (_dl_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval, - &errstring, do_memremap, do_dlmem_premap)) + &errstring, m_map, do_dlmem_premap)) goto lose; _dl_map_object_2 (l, mode, id, NULL, nsid); diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h index 7e3c3b6d53..3fb9aac6cb 100644 --- a/elf/dl-map-segments.h +++ b/elf/dl-map-segments.h @@ -187,9 +187,9 @@ _dl_map_segments (struct link_map *l, void *fd, { /* Map the remaining zero pages in from the zero fill FD. */ caddr_t mapat; - mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage, + mapat = m_map ((caddr_t) zeropage, zeroend - zeropage, c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED, - -1, 0); + NULL, 0); if (__glibc_unlikely (mapat == MAP_FAILED)) return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; }