From patchwork Mon Mar 30 17:43:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Vivek Dasmohapatra X-Patchwork-Id: 38670 Return-Path: X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) by sourceware.org (Postfix) with ESMTPS id 2ABA4385E01F for ; Mon, 30 Mar 2020 17:43:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 2ABA4385E01F Received: from noise.collabora.co.uk (unknown [IPv6:2a00:5f00:102:0:c0a7:51ff:feaf:e642]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: vivek) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 4A2C32966DF; Mon, 30 Mar 2020 18:43:57 +0100 (BST) From: =?utf-8?q?Vivek_Das=C2=A0Mohapatra?= To: vivek@etla.org, libc-alpha@sourceware.org Subject: [RFC][PATCH v4 04/15] elf/dl-load.c, elf-dl-open.c: Implement RTLD_SHARED dlmopen proxying Date: Mon, 30 Mar 2020 18:43:38 +0100 Message-Id: <3d6944bfb2cbe997f28e178d5d27b3347852192c.1585588166.git.vivek@collabora.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: MIME-Version: 1.0 In-Reply-To: References: X-Spam-Status: No, score=-27.4 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) 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-List-Received-Date: Mon, 30 Mar 2020 17:43:59 -0000 This uses the new infrastructure to implement RTLD_SHARED object proxying via dlmopen: Instead of opening the specified object in the requested namespace we open it in the main namespace (if it is not already present there) and proxy it to the destination. The following rules apply: If a proxy of the object is already present in the requested namespace, we simply return it (with an incremented direct-open count). If the object is already present in the requested namespace, a dl error is signalled, since we cannot satisfy the user's request. Proxies are never created in the main namespace: RTLD_SHARED has no effect when the requested namespace is LM_ID_BASE. --- elf/dl-load.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ elf/dl-open.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/elf/dl-load.c b/elf/dl-load.c index a6b80f9395..221589e292 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1904,6 +1904,38 @@ open_path (const char *name, size_t namelen, int mode, return -1; } +/* Search for a link map proxy in the given namespace by name. + Consider it to be an error if the found object is not a proxy. */ + +struct link_map * +_dl_find_proxy (Lmid_t nsid, const char *name) +{ + struct link_map *l; + + for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next) + { + if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0)) + continue; + + if (!_dl_name_match_p (name, l)) + continue; + + /* We have a match - stop searching. */ + break; + } + + if (l) + { + if (l->l_proxy) + return l; + + _dl_signal_error (EEXIST, name, NULL, + N_("object cannot be demoted to a proxy")); + } + + return NULL; +} + /* Map in the shared object file NAME. */ struct link_map * @@ -1920,6 +1952,20 @@ _dl_map_object (struct link_map *loader, const char *name, assert (nsid >= 0); assert (nsid < GL(dl_nns)); +#ifdef SHARED + /* Only need to do proxy checks if `nsid' is not LM_ID_BASE. */ + if (__glibc_unlikely ((mode & RTLD_SHARED) && (nsid != LM_ID_BASE))) + { + /* Search the namespace in case the object is already proxied. */ + if((l = _dl_find_proxy (nsid, name)) != NULL) + return l; + + /* Further searches should be in the base ns: We will proxy the + resulting object in dl_open_worker *after* it is initialised. */ + nsid = LM_ID_BASE; + } +#endif + /* Look for this name among those already loaded. */ for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next) { diff --git a/elf/dl-open.c b/elf/dl-open.c index 623c9754eb..92771638ab 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -476,6 +476,8 @@ dl_open_worker (void *a) const char *file = args->file; int mode = args->mode; struct link_map *call_map = NULL; + int want_proxy = mode & RTLD_SHARED; + Lmid_t proxy_ns = LM_ID_BASE; /* Determine the caller's map if necessary. This is needed in case we have a DST, when we don't know the namespace ID we have to put @@ -528,6 +530,24 @@ dl_open_worker (void *a) /* This object is directly loaded. */ ++new->l_direct_opencount; + /* Proxy already existed in the target ns, nothing left to do. */ + if (__glibc_unlikely (new->l_proxy)) + { + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("proxied file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); + return; + } + + /* If we want proxy and we get this far then the entry in ‘new’ will + be in the main namespace: we should revert to the main namespace code + path(s), but remember the namespace we want the proxy to be in. */ + if (__glibc_unlikely (want_proxy)) + { + proxy_ns = args->nsid; + args->nsid = LM_ID_BASE; + } + /* It was already open. */ if (__glibc_unlikely (new->l_searchlist.r_list != NULL)) { @@ -559,6 +579,16 @@ dl_open_worker (void *a) assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); + if (__glibc_unlikely (want_proxy)) + { + args->map = new = _dl_new_proxy (new, mode, proxy_ns); + ++new->l_direct_opencount; + + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); + } + return; } @@ -762,6 +792,14 @@ dl_open_worker (void *a) if (mode & RTLD_GLOBAL) add_to_global_update (new); + if (__glibc_unlikely (want_proxy)) + { + /* args->map is the return slot which the caller sees, but keep + the original value of new hanging around so we can see the + real link map entry (for logging etc). */ + args->map = _dl_new_proxy (new, mode, proxy_ns); + ++args->map->l_direct_opencount; + } #ifndef SHARED /* We must be the static _dl_open in libc.a. A static program that has loaded a dynamic object now has competition. */ @@ -770,10 +808,19 @@ dl_open_worker (void *a) /* Let the user know about the opencount. */ 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); + { + _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); + + if (args->map->l_proxy) + _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n", + args->map->l_name, + args->map->l_ns, + args->map->l_direct_opencount); + } } + void * _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, int argc, char *argv[], char *env[])