From patchwork Sun Jul 28 19:02:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94659 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 6F9C33858C31 for ; Sun, 28 Jul 2024 19:03:08 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id A4984385841E for ; Sun, 28 Jul 2024 19:02:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A4984385841E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org A4984385841E Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193349; cv=none; b=Bs/wXMb4YrLdKc3MyKnrQA9Tpq/biZiIeteGUc/Fl/xasWi6mhCS1+Wz3QUCpB9HbGoEPCg6IWUDbGvrSUBRI7nAXv28WVkEZqrLBB6IQAm9NWKtxH9H2mbkfzDUlwNX76+uchtNKonwuZDaQ6nC6tiqpAVOwxV8Y9tdV8TjDfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193349; c=relaxed/simple; bh=cWe0XiuAiC73VLH7w29SyO2FiAgAWtTUiQ63xI9Xib0=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=KM4XCeVPHn9j0dv72aDhAg5btq+8Wyta98DYLhG39oXFzJtXRCgWwuT7iljmhIDEl7Fr5nCHEtW0Kh+cQLz97dql/8eCx3REsY2O+ylJNRpCBscQyfHDBj5qz66RDcZI7Dc8g0QI0+M/afZCTMXdrel4QBHPbg8aTteHV2jVfqI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193344; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=X1Fr+WEfLiQ8czLGk4cHXPl9+U4vjVqvdz7BEeq5nL4=; b=Ur6NC+WvV95OsWHuur32jPELBJweDCw+s4PdfRb607/ICr4YG/dnmvcEU4FpxiY7tgUP0E WG4h4XYqJ7h08fSkcAfA0JvWIHnu+nqCma7yc7zhswSjetW4Kkxrk5Yn1KKx6vII3cD9Wn oQuTwTVnAqqjnDKoTxskIzPMQ5lRWWw= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-534-VFysGEgIM4qbL1V5J5RxFA-1; Sun, 28 Jul 2024 15:02:22 -0400 X-MC-Unique: VFysGEgIM4qbL1V5J5RxFA-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 77F461955D4B for ; Sun, 28 Jul 2024 19:02:21 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id EA50B300018D for ; Sun, 28 Jul 2024 19:02:19 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 01/13] stdlib: Make getenv thread-safe in more cases In-Reply-To: Message-ID: <2cf26bb3868ea217b6fd3f508ade6a59ae2d6b3c.1722193092.git.fweimer@redhat.com> References: X-From-Line: 2cf26bb3868ea217b6fd3f508ade6a59ae2d6b3c Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:02:16 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Async-signal-safety is preserved, too. --- stdlib/Makefile | 7 ++ stdlib/getenv.c | 138 +++++++++++++++++++-- stdlib/putenv.c | 2 + stdlib/setenv.c | 225 +++++++++++++++++++++++++---------- stdlib/setenv.h | 73 ++++++++++++ stdlib/tst-environ.c | 13 +- stdlib/tst-getenv-signal.c | 94 +++++++++++++++ stdlib/tst-getenv-thread.c | 61 ++++++++++ stdlib/tst-getenv-unsetenv.c | 75 ++++++++++++ 9 files changed, 605 insertions(+), 83 deletions(-) create mode 100644 stdlib/setenv.h create mode 100644 stdlib/tst-getenv-signal.c create mode 100644 stdlib/tst-getenv-thread.c create mode 100644 stdlib/tst-getenv-unsetenv.c diff --git a/stdlib/Makefile b/stdlib/Makefile index 8b0ac63ddb..697b547540 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -275,6 +275,9 @@ tests := \ tst-canon-bz26341 \ tst-cxa_atexit \ tst-environ \ + tst-getenv-signal \ + tst-getenv-thread \ + tst-getenv-unsetenv \ tst-getrandom \ tst-labs \ tst-limits \ @@ -623,3 +626,7 @@ $(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3 $(evaluate-test) $(objpfx)tst-qsort5: $(libm) + +$(objpfx)tst-getenv-signal: $(shared-thread-library) +$(objpfx)tst-getenv-thread: $(shared-thread-library) +$(objpfx)tst-getenv-unsetenv: $(shared-thread-library) diff --git a/stdlib/getenv.c b/stdlib/getenv.c index bea69e0f40..ad7f0d00c8 100644 --- a/stdlib/getenv.c +++ b/stdlib/getenv.c @@ -15,24 +15,140 @@ License along with the GNU C Library; if not, see . */ -#include +#include +#include #include #include +struct environ_array *__environ_array_list; +environ_counter __environ_counter; + char * getenv (const char *name) { - if (__environ == NULL || name[0] == '\0') - return NULL; - - size_t len = strlen (name); - for (char **ep = __environ; *ep != NULL; ++ep) + while (true) { - if (name[0] == (*ep)[0] - && strncmp (name, *ep, len) == 0 && (*ep)[len] == '=') - return *ep + len + 1; - } + /* Used to deal with concurrent unsetenv. */ + environ_counter start_counter = atomic_load_acquire (&__environ_counter); + + /* We use relaxed MO here because we assume that loads carry a + dependency, matching the release MO store to __environ in + __add_to_environ. Objects pointed to by pointers stored in the + __environ array are never deallocated (except perhaps if putenv + is used, but then synchronization is the responsibility of the + applications). The backing store for __environ is allocated + zeroed. In summary, we can assume that the pointers we observe + are either valid or null. String stores into the environment + array use release MO, so we can assume the loads here see the + initialized contents (againt except for putenv). */ + char **start_environ = atomic_load_relaxed (&__environ); + if (start_environ == NULL || name[0] == '\0') + return NULL; + + size_t len = strlen (name); + for (char **ep = start_environ; ; ++ep) + { + char *entry = atomic_load_relaxed (ep); + if (entry == NULL) + break; + + /* If there is a match, return that value. It was valid at + one point, so we can return it. */ + if (name[0] == entry[0] + && strncmp (name, entry, len) == 0 && entry[len] == '=') + return entry + len + 1; + } + + /* The variable was not found. This might be a false negative + because unsetenv has shuffled around entries. Check if it is + necessary to retry. */ + + /* See Hans Boehm, Can Seqlocks Get Along with Programming Language + Memory Models?, Section 4. This is necessary so that loads in + the loop above are not ordered past the counter check below. */ + atomic_thread_fence_acquire (); + + if (atomic_load_acquire (&__environ_counter) == start_counter) + /* If we reach this point and there was a concurrent + unsetenv call which removed the key we tried to find, the + NULL return value is valid. We can also try again, not + find the value, and then return NULL (assuming there are + no further concurrent unsetenv calls). + + However, if getenv is called to find a value that is + present originally and not removed by any of the + concurrent unsetenv calls, we must not return NULL here. + + If the counter did not change, there was at most one + write to the array in unsetenv while the scanning loop + above was running. This means that there are at most two + different versions of the array to consider. For the + sake of argument, we assume that each load can make an + independent choice which version to use. An arbitrary + number of unsetenv and setenv calls may have happened + since start of getenv. Lets write E[0], E[1], ... for + the original environment elements, a(0) < (1) < ... for a + sequence of increasing integers that are the indices of + the environment variables remaing after the removals, and + N[0], N[1], ... for the new variables added by setenv or + putenv. Then at the start of the last unsetenv call, the + environment contains + + E[a(0)], E[a(1)], ..., N[0], N[1], ... - return NULL; + (the N[k] are optional.) Let's assume that + we are looking for the value E[j]. Then one of the + a(i) == j (otherwise we may return NULL here because + of a unsetenv for the value we are looking for). In the + discussion below it will become clear that the N[k] do + not actually matter. + + The two versions of array we can choose from differ only + in one element, say E[a(i)]. There are two cases: + + Case (A): E[a(i)] is an element being removed by unsetenv + (the target of the first write). We can see the original + version: + + ..., E[a(i-1)], E[a(i)], E[a(i+1)], ..., N[0], ... + ------- + And the overwritten version: + + ..., E[a(i-1)], E[a(i+1)], E[a(i+1)], ..., N[0], ... + --------- + + (The valueE[a(i+1)] can be the terminating NULL.) + As discussed, we are not considering the removal of the + variable being searched for, so a(i) != j, and the + variable getenv is looking for is available in either + version, and we would have found it above. + + Case (B): E[a(i)] is an element that has already been + moved forward and is now itself being overwritten with + its sucessor value E[a(i+1)]. The two versions of the + array look like this: + + ..., E[a(i-1)], E[a(i)], E[a(i)], E[a(i+1)], ..., N[0], ... + ------- + And with the overwrite in place: + + ..., E[a(i-1)], E[a(i)], E[a(i+1)], E[a(i+1)], ..., N[0], ... + --------- + + The key observation here is that even in the second + version with the overwrite present, the scanning loop + will still encounter the overwritten value E[a(i)] in the + previous array element. This means that as long as the + E[j] is still present among the initial E[a(...)] (as we + assumed because there is no concurrent unsetenv for + E[j]), we encounter it while scanning here in getenv. + + In summary, if there was at most one write, a negative + result is a true negative, and we can return NULL. */ + return NULL; + /* Otherwise retry. This will never happen in a signal handler + that interrupted unsetenv because the suspended unsetenv call + cannot change the counter value. */ + } } libc_hidden_def (getenv) diff --git a/stdlib/putenv.c b/stdlib/putenv.c index b234ce0612..c61beb226f 100644 --- a/stdlib/putenv.c +++ b/stdlib/putenv.c @@ -23,6 +23,8 @@ # include #endif +#include + #if _LIBC || HAVE_STDLIB_H # include #endif diff --git a/stdlib/setenv.c b/stdlib/setenv.c index e2164371ad..6bd0596b79 100644 --- a/stdlib/setenv.c +++ b/stdlib/setenv.c @@ -19,6 +19,9 @@ # include #endif +#include +#include + /* Pacify GCC; see the commentary about VALLEN below. This is needed at least through GCC 4.9.2. Pacify GCC for the entire file, as there seems to be no way to pacify GCC selectively, only for the @@ -100,25 +103,51 @@ static void *known_values; #endif +/* Allocate a new environment array and put it o the + __environ_array_list. Returns NULL on memory allocation + failure. */ +static struct environ_array * +__environ_new_array (size_t required_size) +{ + /* No backing array yet, or insufficient room. */ + size_t new_size; + if (__environ_array_list == NULL + || __environ_array_list->allocated * 2 < required_size) + /* Add some unused space for future growth. */ + new_size = required_size + 16; + else + new_size = __environ_array_list->allocated * 2; + + size_t new_size_in_bytes; + if (__builtin_mul_overflow (new_size, sizeof (char *), + &new_size_in_bytes) + || __builtin_add_overflow (new_size_in_bytes, + offsetof (struct environ_array, + array), + &new_size_in_bytes)) + { + __set_errno (ENOMEM); + return NULL; + } -/* If this variable is not a null pointer we allocated the current - environment. */ -static char **last_environ; - + /* Zero-initialize everything, so that getenv can only + observe valid or null pointers. */ + struct environ_array *target_array = calloc (1, new_size_in_bytes); + if (target_array == NULL) + return NULL; + target_array->allocated = new_size; + assert (new_size >= target_array->allocated); + + /* Put it onto the list. */ + target_array->next = __environ_array_list; + __environ_array_list = target_array; + return target_array; +} -/* This function is used by `setenv' and `putenv'. The difference between - the two functions is that for the former must create a new string which - is then placed in the environment, while the argument of `putenv' - must be used directly. This is all complicated by the fact that we try - to reuse values once generated for a `setenv' call since we can never - free the strings. */ int __add_to_environ (const char *name, const char *value, const char *combined, int replace) { - char **ep; - size_t size; - /* Compute lengths before locking, so that the critical section is less of a performance bottleneck. VALLEN is needed only if COMBINED is null (unfortunately GCC is not smart enough to deduce @@ -133,45 +162,80 @@ __add_to_environ (const char *name, const char *value, const char *combined, LOCK; /* We have to get the pointer now that we have the lock and not earlier - since another thread might have created a new environment. */ - ep = __environ; + since another thread might have created a new environment. */ + char **start_environ = atomic_load_relaxed (&__environ); + char **ep = start_environ; - size = 0; + /* This gets written to __environ in the end. */ + char **result_environ = start_environ; + + /* Size of the environment if *ep == NULL. */ if (ep != NULL) - { - for (; *ep != NULL; ++ep) - if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=') - break; - else - ++size; - } + for (; *ep != NULL; ++ep) + if (strncmp (*ep, name, namelen) == 0 && (*ep)[namelen] == '=') + break; - if (ep == NULL || __builtin_expect (*ep == NULL, 1)) + if (ep == NULL || __glibc_likely (*ep == NULL)) { - char **new_environ; - - /* We allocated this space; we can extend it. Avoid using the raw - reallocated pointer to avoid GCC -Wuse-after-free. */ - uintptr_t ip_last_environ = (uintptr_t)last_environ; - new_environ = (char **) realloc (last_environ, - (size + 2) * sizeof (char *)); - if (new_environ == NULL) + /* The scanning loop above reached the end of the environment. + Add a new string to it. */ + replace = true; + + /* + 2 for the new entry and the terminating NULL. */ + size_t required_size = (ep - start_environ) + 2; + if (__environ_is_from_array_list (start_environ) + && required_size <= __environ_array_list->allocated) + /* The __environ array is ours, and we have room in it. We + can use ep as-is. Add a null terminator in case current + usage is less than previous usage. */ + ep[1] = NULL; + else { - UNLOCK; - return -1; - } - - if ((uintptr_t)__environ != ip_last_environ) - memcpy ((char *) new_environ, (char *) __environ, - size * sizeof (char *)); - - new_environ[size] = NULL; - new_environ[size + 1] = NULL; - ep = new_environ + size; + /* We cannot use __environ as is and need to copy over the + __environ contents into an array managed via + __environ_array_list. */ + + struct environ_array *target_array; + if (__environ_array_list != NULL + && required_size <= __environ_array_list->allocated) + /* Existing array has enough room. Contents is copied below. */ + target_array = __environ_array_list; + else + { + /* Allocate a new array. */ + target_array = __environ_new_array (required_size); + if (target_array == NULL) + { + UNLOCK; + return -1; + } + } - last_environ = __environ = new_environ; + /* Copy over the __environ array contents. This is a + backwards copy if __environ points into + target_array->array, and it does not clobber values still + needing copying. This handles the case start_environ + == ep == NULL, too. */ + size_t i; + for (i = 0; start_environ + i < ep; ++i) + /* Regular store because unless there has been direct + manipulation of the environment, target_array is still + a private copy. */ + target_array->array[i] = atomic_load_relaxed (start_environ + i); + + /* This is the new place where we should add the element. */ + ep = target_array->array + i; + + /* Add the null terminator in case there was a pointer there + previously. */ + ep[1] = NULL; + + /* And __environ should be repointed to our array. */ + result_environ = &target_array->array[0]; + } } - if (*ep == NULL || replace) + + if (replace || *ep == NULL) { char *np; @@ -213,9 +277,15 @@ __add_to_environ (const char *name, const char *value, const char *combined, #endif } - *ep = np; + /* Use release MO so that loads are sufficient to observe the + pointer contents because the CPU carries the dependency for + us. This also acts as a thread fence, making getenv + async-signal-safe. */ + atomic_store_release (ep, np); + atomic_store_release (&__environ, result_environ); } + UNLOCK; return 0; @@ -249,18 +319,40 @@ unsetenv (const char *name) LOCK; - ep = __environ; + ep = atomic_load_relaxed (&__environ); if (ep != NULL) - while (*ep != NULL) + while (true) { - if (!strncmp (*ep, name, len) && (*ep)[len] == '=') + char *entry = atomic_load_relaxed (ep); + if (entry == NULL) + break; + if (!strncmp (entry, name, len) && entry[len] == '=') { /* Found it. Remove this pointer by moving later ones back. */ char **dp = ep; - do - dp[0] = dp[1]; - while (*dp++); + while (true) + { + char *next_value = atomic_load_relaxed (dp + 1); + /* This store overwrites a value that has been + removed, or that has already been written to a + previous value. Release MO so that this store does + not get reordered before the counter update in the + previous loop iteration. */ + atomic_store_release (dp, next_value); + /* Release store synchronizes with acquire loads in + getenv. Non-atomic update because there is just + one writer due to the lock. + + See discussion of the counter check in getenv for + an explanation why this is sufficient synchronization. */ + atomic_store_release (&__environ_counter, + atomic_load_relaxed (&__environ_counter) + + 1); + if (next_value == NULL) + break; + ++dp; + } /* Continue the loop in case NAME appears again. */ } else @@ -279,19 +371,20 @@ int clearenv (void) { LOCK; - - if (__environ == last_environ && __environ != NULL) + char **start_environ = atomic_load_relaxed (&__environ); + if (__environ_is_from_array_list (start_environ)) { - /* We allocated this environment so we can free it. */ - free (__environ); - last_environ = NULL; + /* Store null pointers to avoid strange effects when the array + is reused in setenv. */ + for (char **ep = start_environ; *ep != NULL; ++ep) + atomic_store_relaxed(ep, NULL); + /* Update the counter similar to unsetenv, so that the writes in + setenv do not need to update the counter. */ + atomic_store_release (&__environ_counter, + atomic_load_relaxed (&__environ_counter) + 1); } - - /* Clear the environment pointer removes the whole environment. */ - __environ = NULL; - + atomic_store_relaxed (&__environ, NULL); UNLOCK; - return 0; } #ifdef _LIBC @@ -301,6 +394,14 @@ __libc_setenv_freemem (void) /* Remove all traces. */ clearenv (); + /* Clear all backing arrays. */ + while (__environ_array_list != NULL) + { + void *ptr = __environ_array_list; + __environ_array_list = __environ_array_list->next; + free (ptr); + } + /* Now remove the search tree. */ __tdestroy (known_values, free); known_values = NULL; diff --git a/stdlib/setenv.h b/stdlib/setenv.h new file mode 100644 index 0000000000..3919943f87 --- /dev/null +++ b/stdlib/setenv.h @@ -0,0 +1,73 @@ +/* Common declarations for the setenv/getenv family of functions. + 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 _SETENV_H +#define _SETENV_H + +#include +#include + +/* We use an exponential sizing policy for environment arrays. The + arrays are not deallocating during the lifetime of the process. + This adds between one and two additional pointers per active + environemnt entry, on top of what is used by setenv to keep track + of environment values used before. */ +struct environ_array +{ + struct environ_array *next; /* Previously used environment array. */ + size_t allocated; /* Number of allocated array elments. */ + char *array[]; /* The actual environment array. */ +}; + +/* After initialization, and until the user resets environ (perhaps by + calling clearenv), &__environ[0] == &environ_array_list->array[0]. */ +extern struct environ_array *__environ_array_list attribute_hidden; + +/* Returns true if EP (which should be an __environ value) is a + pointer managed by setenv. */ +static inline _Bool +__environ_is_from_array_list (char **ep) +{ + struct environ_array *eal = atomic_load_relaxed (&__environ_array_list); + return eal != NULL && &eal->array[0] == ep; +} + +/* Counter for detecting concurrent modification in unsetenv. + Ideally, this should be a 64-bit counter that cannot wrap around, + but given that counter wrapround is probably impossible to hit + (2**32 operations in unsetenv concurrently with getenv), using + seems unnecessary. */ +#if __HAVE_64B_ATOMICS +typedef uint64_t environ_counter; +#else +typedef uint32_t environ_counter; +#endif + +/* Updated by unsetenv to detect multiple overwrites in getenv. */ +extern environ_counter __environ_counter attribute_hidden; + +/* This function is used by `setenv' and `putenv'. The difference between + the two functions is that for the former must create a new string which + is then placed in the environment, while the argument of `putenv' + must be used directly. This is all complicated by the fact that we try + to reuse values once generated for a `setenv' call since we can never + free the strings. */ +int __add_to_environ (const char *name, const char *value, + const char *combines, int replace) attribute_hidden; + +#endif /* _SETENV_H */ diff --git a/stdlib/tst-environ.c b/stdlib/tst-environ.c index bab8873f08..aff191bb4d 100644 --- a/stdlib/tst-environ.c +++ b/stdlib/tst-environ.c @@ -20,6 +20,7 @@ #include #include #include +#include #define VAR "FOOBAR" @@ -50,11 +51,7 @@ do_test (void) /* Getting this value should now be possible. */ valp = getenv (VAR); - if (valp == NULL || strcmp (valp, "one") != 0) - { - puts ("getenv #2 failed"); - result = 1; - } + TEST_COMPARE_STRING (valp, "one"); /* Try to replace without the replace flag set. This should fail. */ if (setenv (VAR, "two", 0) != 0) @@ -65,11 +62,7 @@ do_test (void) /* The value shouldn't have changed. */ valp = getenv (VAR); - if (valp == NULL || strcmp (valp, "one") != 0) - { - puts ("getenv #3 failed"); - result = 1; - } + TEST_COMPARE_STRING (valp, "one"); /* Now replace the value using putenv. */ if (putenv (putenv_val) != 0) diff --git a/stdlib/tst-getenv-signal.c b/stdlib/tst-getenv-signal.c new file mode 100644 index 0000000000..bd35f886b5 --- /dev/null +++ b/stdlib/tst-getenv-signal.c @@ -0,0 +1,94 @@ +/* Test getenv from a signal handler interrupting environent updates. + 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 +#include +#include +#include +#include + +/* Set to false by the main thread after doing all the setenv + calls. */ +static bool running = true; + +/* Used to synchronize the start of signal sending. */ +static pthread_barrier_t barrier; + +/* Identity of the main thread. */ +static pthread_t main_thread; + +/* Send SIGUSR1 signals to main_thread. */ +static void * +signal_thread (void *ignored) +{ + xpthread_barrier_wait (&barrier); + while (__atomic_load_n (&running, __ATOMIC_RELAXED)) + xpthread_kill (main_thread, SIGUSR1); + return NULL; +} + +/* Call getenv from a signal handler. */ +static void +signal_handler (int signo) +{ + TEST_COMPARE_STRING (getenv ("unset_variable"), NULL); + char *value = getenv ("set_variable"); + TEST_VERIFY (strncmp (value, "value", strlen ("value")) == 0); +} + +static int +do_test (void) +{ + /* Added to the environment using putenv. */ + char *variables[30]; + for (int i = 0; i < array_length (variables); ++i) + variables[i] = xasprintf ("v%d=%d", i, i); + + xsignal (SIGUSR1, signal_handler); + TEST_COMPARE (setenv ("set_variable", "value", 1), 0); + xraise (SIGUSR1); + main_thread = pthread_self (); + xpthread_barrier_init (&barrier, NULL, 2); + pthread_t thr = xpthread_create (NULL, signal_thread, NULL); + xpthread_barrier_wait (&barrier); + + for (int i = 0; i < array_length (variables); ++i) + { + char buf[30]; + TEST_COMPARE (setenv ("temporary_variable", "1", 1), 0); + snprintf (buf, sizeof (buf), "V%d", i); + TEST_COMPARE (setenv (buf, buf + 1, 1), 0); + TEST_COMPARE (putenv (variables[i]), 0); + snprintf (buf, sizeof (buf), "value%d", i); + TEST_COMPARE (setenv ("set_variable", buf, 1), 0); + TEST_COMPARE (unsetenv ("temporary_variable"), 0); + } + + __atomic_store_n (&running, false, __ATOMIC_RELAXED); + xpthread_join (thr); + xpthread_barrier_destroy (&barrier); + + for (int i = 0; i < array_length (variables); ++i) + free (variables[i]); + return 0; +} + +#include diff --git a/stdlib/tst-getenv-thread.c b/stdlib/tst-getenv-thread.c new file mode 100644 index 0000000000..e5a79c5227 --- /dev/null +++ b/stdlib/tst-getenv-thread.c @@ -0,0 +1,61 @@ +/* Test getenv with concurrent setenv. + 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 + +/* Set to false by the main thread after doing all the setenv + calls. */ +static bool running = true; + +/* Used to synchronize the start of the getenv thread. */ +static pthread_barrier_t barrier; + +/* Invoke getenv for a nonexisting environment variable in a loop. + This checks that concurrent setenv does not invalidate the + environment array while getenv reads it. */ +static void * +getenv_thread (void *ignored) +{ + xpthread_barrier_wait (&barrier); + while (__atomic_load_n (&running, __ATOMIC_RELAXED)) + TEST_VERIFY (getenv ("unset_variable") == NULL); + return NULL; +} + +static int +do_test (void) +{ + xpthread_barrier_init (&barrier, NULL, 2); + pthread_t thr = xpthread_create (NULL, getenv_thread, NULL); + xpthread_barrier_wait (&barrier); + for (int i = 0; i < 1000; ++i) + { + char buf[30]; + snprintf (buf, sizeof (buf), "V%d", i); + TEST_COMPARE (setenv (buf, buf + 1, 1), 0); + } + __atomic_store_n (&running, false, __ATOMIC_RELAXED); + xpthread_join (thr); + xpthread_barrier_destroy (&barrier); + return 0; +} + +#include diff --git a/stdlib/tst-getenv-unsetenv.c b/stdlib/tst-getenv-unsetenv.c new file mode 100644 index 0000000000..4d42b5fd69 --- /dev/null +++ b/stdlib/tst-getenv-unsetenv.c @@ -0,0 +1,75 @@ +/* Test getenv with concurrent unsetenv. + 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 +#include +#include + +/* Used to synchronize the start of each test iteration. */ +static pthread_barrier_t barrier; + +/* Number of iterations. */ +enum { iterations = 10000 }; + +/* Check that even with concurrent unsetenv, a variable that is known + to be there is found. */ +static void * +getenv_thread (void *ignored) +{ + for (int i = 0; i < iterations; ++i) + { + xpthread_barrier_wait (&barrier); + TEST_COMPARE_STRING (getenv ("variable"), "value"); + xpthread_barrier_wait (&barrier); + } + return NULL; +} + +static int +do_test (void) +{ + xpthread_barrier_init (&barrier, NULL, 2); + pthread_t thr = xpthread_create (NULL, getenv_thread, NULL); + + char *variables[50]; + for (int i = 0; i < array_length (variables); ++i) + variables[i] = xasprintf ("V%d", i); + + for (int i = 0; i < iterations; ++i) + { + clearenv (); + for (int j = 0; j < array_length (variables); ++j) + TEST_COMPARE (setenv (variables[j], variables[j] + 1, 1), 0); + TEST_COMPARE (setenv ("variable", "value", 1), 0); + xpthread_barrier_wait (&barrier); + /* Test runs. */ + for (int j = 0; j < array_length (variables); ++j) + TEST_COMPARE (unsetenv (variables[j]), 0); + xpthread_barrier_wait (&barrier); + } + xpthread_join (thr); + xpthread_barrier_destroy (&barrier); + for (int i = 0; i < array_length (variables); ++i) + free (variables[i]); + return 0; +} + +#include From patchwork Sun Jul 28 19:02:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94660 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 BD91E385C6D3 for ; Sun, 28 Jul 2024 19:04:14 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 47591385841D for ; Sun, 28 Jul 2024 19:02:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 47591385841D Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 47591385841D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193351; cv=none; b=VcnMKc2f7trScvXqitrDnIUmJVjc+8z4y+c34+NHttv5Cnli/zAQ5t0gP4GhF87Fw8h35Y9JqS1cp2w3L6RjsZh7NwScAvRfAb+c+GJRwAg9Z9jwWO1EldvjRYs1GKTh/esYTK9+i1trMR8av/AdKshGKYg/KMWQJD7hjADvnKg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193351; c=relaxed/simple; bh=g2jz+QEw4YnZKU1uGbVf3BfWAd7pNE0u7a8fQC6pXJY=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=KJHnnkUacM6kwKYp9K9aUI/nJ+WwR26/A0dbnf2N4SMTH3aJDLSBNogSUZpSo7EjlK3rGGY9QnqPwbbyB/yqXJMOANgX4QZwqPbXhnZBm90EYH3ITqCIrdWVXB2xnQae/8vSjHkPJkRfRRTdeWEwo4CvOjrdfN/GrfZHIL4vuxo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193350; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Jo1IfYINkwNfwQHkVG+7P1LsVQKaeCHsi5/+Rlj52cg=; b=PZ5R6s52fPeo+0NubzBOttrJODXZOlghik+ywFEyiQ6EDgdCV+LCZmbQyc3M+W3LwkpULD fug7Vc2bjEHBCXCBCyHMaMSKrSEBA4b9BpJ52GvEyGWSjDGV6fzEHnQSHGxbAIgOzddnTK HNTjtioZj71bX9Xbk7oVdvlzKNM7Pfc= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-656-_K3Y7Jv_NRm5d8BUjgm7VQ-1; Sun, 28 Jul 2024 15:02:28 -0400 X-MC-Unique: _K3Y7Jv_NRm5d8BUjgm7VQ-1 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 638881955F45 for ; Sun, 28 Jul 2024 19:02:27 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 3FEF019560AE for ; Sun, 28 Jul 2024 19:02:25 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 02/13] Linux: Assume __NR_execveat is defined in fexecve In-Reply-To: Message-ID: <3e77d5b97883fb9c2d37e65f0b7416ce3b3e150c.1722193092.git.fweimer@redhat.com> References: X-From-Line: 3e77d5b97883fb9c2d37e65f0b7416ce3b3e150c Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:02:23 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org It is available on all architectures. --- sysdeps/unix/sysv/linux/fexecve.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sysdeps/unix/sysv/linux/fexecve.c b/sysdeps/unix/sysv/linux/fexecve.c index a62a757ec3..e0e5566fd7 100644 --- a/sysdeps/unix/sysv/linux/fexecve.c +++ b/sysdeps/unix/sysv/linux/fexecve.c @@ -39,13 +39,11 @@ fexecve (int fd, char *const argv[], char *const envp[]) return -1; } -#ifdef __NR_execveat /* Avoid implicit array coercion in syscall macros. */ INLINE_SYSCALL (execveat, 5, fd, "", &argv[0], &envp[0], AT_EMPTY_PATH); -# ifndef __ASSUME_EXECVEAT +#ifndef __ASSUME_EXECVEAT if (errno != ENOSYS) return -1; -# endif #endif #ifndef __ASSUME_EXECVEAT From patchwork Sun Jul 28 19:02:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94662 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 79246385843B for ; Sun, 28 Jul 2024 19:04:36 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 3D3083857022 for ; Sun, 28 Jul 2024 19:02:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3D3083857022 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 3D3083857022 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193369; cv=none; b=b2AhuHwzuAkIyheRIUlqivibikK2cpieIcRKS/eF15rhQ067902UaOKUeTs8tjLjw1UPgYBvE1AQwujrZwl2te9xc+pjalpZBe5giC0MOa1NDiwd2tOBbYRzXSqvdGqLEOi4DCl3dRm7btsI636RjcvmXqnGJDaWsLhlWBMMu/I= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193369; c=relaxed/simple; bh=3NXOXpiCFgRmtjoBBUw8AEE75jRjNkqLFRgsWXw11xw=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=vhFc8k6TKVB9brkirVZIFK1dY0B8ATyOd+azZb7mGPUByUkrt36GvzZRp8E56qvifP6Ye1uvOQ8SexE7JtHHzCPyebu/qX6mXZFdEbDM4pgW/ZGok/SCp8WMhs4Mf+USjqe2i45F+nVUUTZvROs90VsRM894MRTJRplfvW8mCvo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193355; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=cnD5Bjnrt/De1Em2DJlNQb8sXf3HK0LwosPRBU8d5wo=; b=gHVtrZCqq3R6dSToImCFBIyL8jQ91Ff9y+P0mQWyo/BpuJKu/bFAaES8F9Ne1L/qWnzcKb lywa8jsLbYSkTbnQ18Q757SusbllQxvbQmzTKm0y1rylsNx+TyOAUOTL8uB8oe1g6KC1KL jt5i4rjjWdtwU7z20Avx+0eTGr2BEAI= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-641-3O0twUd-MAWY6JohPN_wnw-1; Sun, 28 Jul 2024 15:02:33 -0400 X-MC-Unique: 3O0twUd-MAWY6JohPN_wnw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 229C71955F0D for ; Sun, 28 Jul 2024 19:02:33 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 048BC300018D for ; Sun, 28 Jul 2024 19:02:31 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 03/13] Linux: Introduce __do_fexecve in fexecve In-Reply-To: Message-ID: <49be319a6cc60341a437ba186ef701b9095e00d7.1722193092.git.fweimer@redhat.com> References: X-From-Line: 49be319a6cc60341a437ba186ef701b9095e00d7 Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:02:28 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Call the execve system call directly, in preparation of wrapping environ access in the *execve* family of function. Make handling of errno more explicit on the fallback path by using INTERNAL_SYSCALL_CALL. --- sysdeps/unix/sysv/linux/fexecve.c | 53 ++++++++++++++++--------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/sysdeps/unix/sysv/linux/fexecve.c b/sysdeps/unix/sysv/linux/fexecve.c index e0e5566fd7..46c8170092 100644 --- a/sysdeps/unix/sysv/linux/fexecve.c +++ b/sysdeps/unix/sysv/linux/fexecve.c @@ -27,6 +27,33 @@ #include #include +static int +__do_fexecve (int fd, char *const argv[], char *const envp[]) +{ + /* Avoid implicit array coercion in syscall macros. */ + int err = -INTERNAL_SYSCALL_CALL (execveat, fd, "", &argv[0], &envp[0], + AT_EMPTY_PATH); + +#ifndef __ASSUME_EXECVEAT + if (err == ENOSYS) + { + /* We use the /proc filesystem to get the information. If it is not + mounted we fail. We do not need the return value. */ + struct fd_to_filename filename; + const char *path = __fd_to_filename (fd, &filename); + err = -INTERNAL_SYSCALL_CALL (execve, path, &argv[0], &envp[0]); + + /* We come here only if the 'execve' call fails. Determine whether + /proc is mounted. If not we return ENOSYS. */ + if (INTERNAL_SYSCALL_CALL (faccessat, AT_FDCWD, "/proc/self/fd", 0) + == -ENOENT) + err = ENOSYS; + } +#endif /* __ASSUME_EXECVEAT */ + + __set_errno (err); + return -1; +} /* Execute the file FD refers to, overlaying the running program image. ARGV and ENVP are passed to the new program, as for `execve'. */ @@ -39,29 +66,5 @@ fexecve (int fd, char *const argv[], char *const envp[]) return -1; } - /* Avoid implicit array coercion in syscall macros. */ - INLINE_SYSCALL (execveat, 5, fd, "", &argv[0], &envp[0], AT_EMPTY_PATH); -#ifndef __ASSUME_EXECVEAT - if (errno != ENOSYS) - return -1; -#endif - -#ifndef __ASSUME_EXECVEAT - /* We use the /proc filesystem to get the information. If it is not - mounted we fail. We do not need the return value. */ - struct fd_to_filename filename; - __execve (__fd_to_filename (fd, &filename), argv, envp); - - int save = errno; - - /* We come here only if the 'execve' call fails. Determine whether - /proc is mounted. If not we return ENOSYS. */ - struct __stat64_t64 st; - if (__stat64_time64 ("/proc/self/fd", &st) != 0 && errno == ENOENT) - save = ENOSYS; - - __set_errno (save); -#endif - - return -1; + return __do_fexecve (fd, argv, envp); } From patchwork Sun Jul 28 19:02:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94666 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 43C1F385C6CB for ; Sun, 28 Jul 2024 19:05:45 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id EB820385840F for ; Sun, 28 Jul 2024 19:02:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EB820385840F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org EB820385840F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193369; cv=none; b=GyWXy/Rw+WVNyvS0jbnrLuC9iJqh216m8oN11LneG9qc5i1ITz05TbOmhd0Ga3E3U0JiH+TXArwjhR/wa1ANz3nok+m3HJkFD14wN/1l+m/17uSHULCvX55CI4894jnoC8NUSyahhwAbNCzi/6fZZ+LN5nQrv9bLO+dgFfnnEF8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193369; c=relaxed/simple; bh=cxueXK3VJnfKTRcER4XY1x0/veTohxrSMnkUyyk/xw0=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=eJlTETptArk6q7Hd3EVwBqzRXqbHQ+vycvFaFOv+U6pZXrKCZtH/PWBGtFrUOoruWw5OJO6unNALF84tvBNTeBW3ido5qfwyaDzBdmvfi7wjdG1CUBo4BfhZP/pIVene+7IlODKnRVzRhEo041KQD7lx6cZCqkMG36IZRExhSwU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193363; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=ncASNpycvhO7ajiW2q/17sN+/LBIosmmS0Z//AExRn0=; b=PllZW5KoGVyR1N66wH4V0aia5S2kFGqiEgC/sM8QWjCX78meCSqr5wSG6jwhJ3nHr5bPMx b94g1z7GlBq9IpB032NgK0hvUV4Gk0F9KwSSijP9wh6snF5MoWYVyEqDuDo4kk2uwANGqg HAI/bepTx96pwp5QNlXOP3Y8D5W5xnI= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-153-WmTUiHGsNB6Rt0RVydOfwA-1; Sun, 28 Jul 2024 15:02:39 -0400 X-MC-Unique: WmTUiHGsNB6Rt0RVydOfwA-1 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 298681956080 for ; Sun, 28 Jul 2024 19:02:39 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 103EA1955D42 for ; Sun, 28 Jul 2024 19:02:37 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 04/13] Linux: Error handling fixes for fexecve In-Reply-To: Message-ID: <2142860d3f3a6a56fc393f1c0ccc3f859601e06f.1722193092.git.fweimer@redhat.com> References: X-From-Line: 2142860d3f3a6a56fc393f1c0ccc3f859601e06f Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:02:34 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Do not check argv and envp for null, for consistency with execveat and execve. An invalid descriptor should result in EBADF, not EINVAL. In the fallback code, if the file descriptor is not valid, return EBADF, not ENOENT. --- posix/tst-fexecve.c | 14 ++++++++++++++ sysdeps/unix/sysv/linux/fexecve.c | 20 +++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/posix/tst-fexecve.c b/posix/tst-fexecve.c index 9c528fe8d9..c8e772afb6 100644 --- a/posix/tst-fexecve.c +++ b/posix/tst-fexecve.c @@ -82,6 +82,20 @@ do_test (void) close (fd); #endif + /* Now fd is not open, so fexecve is expected to fail with EBADF. */ + static const char *const argv[] = { + "/bin/sh", "-c", "false", NULL + }; + errno = -1; + TEST_COMPARE (fexecve (fd, (char **const) argv, environ), -1); + TEST_COMPARE (errno, EBADF); + + /* Likewise for negative descriptors. */ + TEST_VERIFY (AT_FDCWD != -1); + errno = -1; + TEST_COMPARE (fexecve (-1, (char **const) argv, environ), -1); + TEST_COMPARE (errno, EBADF); + return ret; } diff --git a/sysdeps/unix/sysv/linux/fexecve.c b/sysdeps/unix/sysv/linux/fexecve.c index 46c8170092..c2303735de 100644 --- a/sysdeps/unix/sysv/linux/fexecve.c +++ b/sysdeps/unix/sysv/linux/fexecve.c @@ -45,9 +45,18 @@ __do_fexecve (int fd, char *const argv[], char *const envp[]) /* We come here only if the 'execve' call fails. Determine whether /proc is mounted. If not we return ENOSYS. */ - if (INTERNAL_SYSCALL_CALL (faccessat, AT_FDCWD, "/proc/self/fd", 0) - == -ENOENT) - err = ENOSYS; + if (err == ENOENT) + { + if (INTERNAL_SYSCALL_CALL (faccessat, AT_FDCWD, "/proc/self/fd", 0) + == -ENOENT) + err = ENOSYS; + else if (INTERNAL_SYSCALL_CALL (faccessat, AT_FDCWD, path, 0) + == -ENOENT) + /* The file descriptor does not exist, but /proc is mounted. */ + err = EBADF; + /* Otherwise ENOENT comes from an invalid program + interpreter or some other property of the file. */ + } } #endif /* __ASSUME_EXECVEAT */ @@ -60,9 +69,10 @@ __do_fexecve (int fd, char *const argv[], char *const envp[]) int fexecve (int fd, char *const argv[], char *const envp[]) { - if (fd < 0 || argv == NULL || envp == NULL) + /* Avoid unexpected behavior of execveat with AT_FDCWD. */ + if (fd < 0) { - __set_errno (EINVAL); + __set_errno (EBADF); return -1; } From patchwork Sun Jul 28 19:02:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94669 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 08355385DDC1 for ; Sun, 28 Jul 2024 19:06:40 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id DDCD0385828E for ; Sun, 28 Jul 2024 19:02:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DDCD0385828E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DDCD0385828E Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193370; cv=none; b=Ne0/QwdsVuAcYJpSB+syM3GT26oHbicMwZe8Q1N4Ar3c1CVP7o7YGT+wuWfdgBJb5TNW4cNXvl67oMYjXh+cmSerI6zIhS3QsPHM7WpfSWJzFSvhi0N3MomksDod5nZPy5veWacwMljnZsc8e5o2vu3nE+qwPfmVODVoQovWfjo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193370; c=relaxed/simple; bh=eZHfP82rgv6HHYJunK5WL9J3BoxIn43few3k30aax3E=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=SXMOpKJSEeqy3D56GPfHMo+YOZLri66DvB76E4QqFJSxeAnBRUYJSXci/4NtFIqcQ10al3ZkfN6ElBmgpWCJICheb5xAO4U9vuUF96/4r/AK9LvNSYNodvfwYzuEuEc0WObEU5TYbWo1ETb5WD2n4tXqAQGB3ysqgMwFxAEz0TA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193367; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=vOtKWt5ldCaNuPXDsjijTBUuTlC4GtEeTo7BbCXjP3o=; b=HbmvMZnlumExwicDCRAdwTMZsCGLsQhSlvMSpL72k7IOt4foi1lkOzhahAvuII68syAo8/ IxTB3VMaekPtCZvf4VMlEbEH2FWC8AVzqyO+M+0cr1ht3CiZjGROOzlcE3tbccaxcznfna QJoa+Q/xawaAKBKlZQSlYaRN+uKILjE= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-164-MVzI0jELPn2-KYyd4wzJ_A-1; Sun, 28 Jul 2024 15:02:45 -0400 X-MC-Unique: MVzI0jELPn2-KYyd4wzJ_A-1 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 19AC41955F3B for ; Sun, 28 Jul 2024 19:02:45 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 19F4419560AE for ; Sun, 28 Jul 2024 19:02:43 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 05/13] posix: Consolidate execve family of functions In-Reply-To: Message-ID: References: X-From-Line: c73086c71d3deb6598029f3a72810c985a148ac7 Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:02:41 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Ports only need to implement __arch_execveat going forward. Generic code implements execve, fexecve, execveat on top of it. The __arch_execveat/execveat split makes it easy to add generic code to the execveat implementation, e.g., for environ handling. The Linux implementation prefers the execve system call if available and keeps emulation for fexecve-style execveat, for maximum compatibility. --- posix/execve.c | 13 +---- posix/execveat.c | 12 ++--- posix/fexecve.c | 13 ++--- .../hurd/execve.c => generic/arch-execveat.h} | 21 +++------ .../mach/hurd/{execveat.c => arch-execveat.h} | 11 ++--- sysdeps/mach/hurd/fexecve.c | 47 ------------------- .../sysv/linux/{fexecve.c => arch-execveat.h} | 38 +++++++-------- sysdeps/unix/sysv/linux/execveat.c | 32 ------------- sysdeps/unix/sysv/linux/syscalls.list | 1 - 9 files changed, 40 insertions(+), 148 deletions(-) rename sysdeps/{mach/hurd/execve.c => generic/arch-execveat.h} (61%) rename sysdeps/mach/hurd/{execveat.c => arch-execveat.h} (90%) delete mode 100644 sysdeps/mach/hurd/fexecve.c rename sysdeps/unix/sysv/linux/{fexecve.c => arch-execveat.h} (73%) delete mode 100644 sysdeps/unix/sysv/linux/execveat.c diff --git a/posix/execve.c b/posix/execve.c index c8f08658cb..6d6cefaebe 100644 --- a/posix/execve.c +++ b/posix/execve.c @@ -16,24 +16,15 @@ . */ #include -#include +#include #include - /* Replace the current process, executing PATH with arguments ARGV and environment ENVP. ARGV and ENVP are terminated by NULL pointers. */ int __execve (const char *path, char *const argv[], char *const envp[]) { - if (path == NULL || argv == NULL || envp == NULL) - { - __set_errno (EINVAL); - return -1; - } - - __set_errno (ENOSYS); - return -1; + return __execveat (AT_FDCWD, path, argv, envp, 0); } -stub_warning (execve) weak_alias (__execve, execve) diff --git a/posix/execveat.c b/posix/execveat.c index bddc4d5fb3..110177ea54 100644 --- a/posix/execveat.c +++ b/posix/execveat.c @@ -20,6 +20,8 @@ #include #include +#include + /* Replace the current process, executing PATH relative to difrd with arguments argv and environment envp. argv and envp are terminated by NULL pointers. */ @@ -27,15 +29,7 @@ int __execveat (int dirfd, const char *path, char *const argv[], char *const envp[], int flags) { - if (path == NULL || argv == NULL || envp == NULL) - { - __set_errno (EINVAL); - return -1; - } - - __set_errno (ENOSYS); - return -1; + return __arch_execveat (dirfd, path, argv, envp, flags); } -stub_warning (execveat) weak_alias (__execveat, execveat) diff --git a/posix/fexecve.c b/posix/fexecve.c index 04e59f97ad..3f632b3ea3 100644 --- a/posix/fexecve.c +++ b/posix/fexecve.c @@ -16,23 +16,20 @@ . */ #include -#include +#include #include - /* Execute the file FD refers to, overlaying the running program image. ARGV and ENVP are passed to the new program, as for `execve'. */ int fexecve (int fd, char *const argv[], char *const envp[]) { - if (fd < 0 || argv == NULL || envp == NULL) + /* Avoid unexpected behavior of execveat with AT_FDCWD. */ + if (fd < 0) { - __set_errno (EINVAL); + __set_errno (EBADF); return -1; } - __set_errno (ENOSYS); - return -1; + return __execveat (fd, "", argv, envp, AT_EMPTY_PATH); } - -stub_warning (fexecve) diff --git a/sysdeps/mach/hurd/execve.c b/sysdeps/generic/arch-execveat.h similarity index 61% rename from sysdeps/mach/hurd/execve.c rename to sysdeps/generic/arch-execveat.h index 11e3f34b39..cd064df28f 100644 --- a/sysdeps/mach/hurd/execve.c +++ b/sysdeps/generic/arch-execveat.h @@ -1,4 +1,5 @@ -/* Copyright (C) 1991-2024 Free Software Foundation, Inc. +/* Architecture-specific execveat implementation. Stub 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 @@ -15,18 +16,10 @@ License along with the GNU C Library; if not, see . */ -#include -#include -#include -#include -#include - -/* Replace the current process, executing FILE_NAME with arguments ARGV and - environment ENVP. ARGV and ENVP are terminated by NULL pointers. */ -int -__execve (const char *file_name, char *const argv[], char *const envp[]) +static int +__arch_execveat (int dirfd, const char *path, char *const argv, + char *const envp, int flags) { - return __execveat (AT_FDCWD, file_name, argv, envp, 0); + __set_errno (ENOSYS); + return -1; } - -weak_alias (__execve, execve) diff --git a/sysdeps/mach/hurd/execveat.c b/sysdeps/mach/hurd/arch-execveat.h similarity index 90% rename from sysdeps/mach/hurd/execveat.c rename to sysdeps/mach/hurd/arch-execveat.h index f24dd7b62f..999779cbd5 100644 --- a/sysdeps/mach/hurd/execveat.c +++ b/sysdeps/mach/hurd/arch-execveat.h @@ -1,4 +1,5 @@ -/* Copyright (C) 1991-2024 Free Software Foundation, Inc. +/* Execute program relative to a directory file descriptor. Hurd version. + Copyright (C) 1991-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 @@ -25,9 +26,9 @@ /* Replace the current process, executing FILE_NAME with arguments ARGV and environment ENVP. ARGV and ENVP are terminated by NULL pointers. */ -int -__execveat (int dirfd, const char *file_name, char *const argv[], - char *const envp[], int flags) +static int +__arch_execveat (int dirfd, const char *file_name, char *const argv[], + char *const envp[], int flags) { error_t err; char *concat_name = NULL; @@ -88,5 +89,3 @@ __execveat (int dirfd, const char *file_name, char *const argv[], return __hurd_fail (err); } - -weak_alias (__execveat, execveat) diff --git a/sysdeps/mach/hurd/fexecve.c b/sysdeps/mach/hurd/fexecve.c deleted file mode 100644 index 50472d5ca5..0000000000 --- a/sysdeps/mach/hurd/fexecve.c +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (C) 1993-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 - -/* Execute the file FD refers to, overlaying the running program image. */ - -int -fexecve (int fd, char *const argv[], char *const envp[]) -{ - file_t file; - error_t err; - enum retry_type doretry; - char retryname[1024]; - - err = HURD_DPORT_USE (fd, - __dir_lookup (port, "", O_EXEC, 0, &doretry, retryname, &file)); - - if (! err && (doretry != FS_RETRY_NORMAL || retryname[0] != '\0')) - err = EGRATUITOUS; - if (err) - return __hurd_fail(err); - - err = _hurd_exec_paths (__mach_task_self (), file, NULL, NULL, argv, envp); - if (! err) - err = EGRATUITOUS; - - __mach_port_deallocate (__mach_task_self (), file); - return __hurd_fail (err); -} diff --git a/sysdeps/unix/sysv/linux/fexecve.c b/sysdeps/unix/sysv/linux/arch-execveat.h similarity index 73% rename from sysdeps/unix/sysv/linux/fexecve.c rename to sysdeps/unix/sysv/linux/arch-execveat.h index c2303735de..b6bc286829 100644 --- a/sysdeps/unix/sysv/linux/fexecve.c +++ b/sysdeps/unix/sysv/linux/arch-execveat.h @@ -1,4 +1,5 @@ -/* Copyright (C) 1994-2024 Free Software Foundation, Inc. +/* Execute program relative to a directory file descriptor. Linux version. + Copyright (C) 1994-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 @@ -15,17 +16,11 @@ License along with the GNU C Library; if not, see . */ -#include -#include -#include -#include #include -#include - #include -#include -#include #include +#include +#include static int __do_fexecve (int fd, char *const argv[], char *const envp[]) @@ -64,17 +59,20 @@ __do_fexecve (int fd, char *const argv[], char *const envp[]) return -1; } -/* Execute the file FD refers to, overlaying the running program image. - ARGV and ENVP are passed to the new program, as for `execve'. */ -int -fexecve (int fd, char *const argv[], char *const envp[]) +static int +__arch_execveat (int dirfd, const char *path, char *const *argv, + char *const *envp, int flags) { - /* Avoid unexpected behavior of execveat with AT_FDCWD. */ - if (fd < 0) - { - __set_errno (EBADF); - return -1; - } + /* Use execve if possible, to preserve the historic system call + profile. */ + if (dirfd == AT_FDCWD && flags == 0) + return INLINE_SYSCALL_CALL (execve, path, argv, envp, flags); + + /* Provide emulation for an fexecve-style execveat call. */ + if (dirfd >= 0 && flags == AT_EMPTY_PATH && path[0] == '\0') + return __do_fexecve (dirfd, argv, envp); - return __do_fexecve (fd, argv, envp); + /* Otherwise perform the system call without emulation. */ + return INLINE_SYSCALL_CALL (execveat, dirfd, path, &argv[0], &envp[0], + flags); } diff --git a/sysdeps/unix/sysv/linux/execveat.c b/sysdeps/unix/sysv/linux/execveat.c deleted file mode 100644 index e1d68b9043..0000000000 --- a/sysdeps/unix/sysv/linux/execveat.c +++ /dev/null @@ -1,32 +0,0 @@ -/* Execute program relative to a directory file descriptor. - Copyright (C) 2021-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 - -/* Execute the file FD refers to, overlaying the running program image. - ARGV and ENVP are passed to the new program, as for 'execve'. */ -int -execveat (int dirfd, const char *path, char *const argv[], char *const envp[], - int flags) -{ - /* Avoid implicit array coercion in syscall macros. */ - return INLINE_SYSCALL_CALL (execveat, dirfd, path, &argv[0], &envp[0], - flags); -} diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list index 9ac42c3436..14bbb5fe6e 100644 --- a/sysdeps/unix/sysv/linux/syscalls.list +++ b/sysdeps/unix/sysv/linux/syscalls.list @@ -9,7 +9,6 @@ delete_module EXTRA delete_module 3 delete_module epoll_create1 EXTRA epoll_create1 i:i epoll_create1 epoll_ctl EXTRA epoll_ctl i:iiip epoll_ctl eventfd EXTRA eventfd2 i:ii eventfd -execve - execve i:spp __execve execve flock - flock i:ii __flock flock fsconfig EXTRA fsconfig i:iUsNi fsconfig fsmount EXTRA fsmount i:iUU fsmount From patchwork Sun Jul 28 19:02:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94661 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 04C3F385C6CD for ; Sun, 28 Jul 2024 19:04:20 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 5D8B13858417 for ; Sun, 28 Jul 2024 19:02:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5D8B13858417 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5D8B13858417 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193377; cv=none; b=VzA/3Nyr2hP+CgPkEbMMZVLsdoG1Hoa+iRrguwI5aCTPjJgd136lvV5+Pyokzollkn4tre8kN5+Hi92YJCoeb+EOkrCUXP161FJ4B3HZAbk5cIVmASibZqsrY5Hg6aecTHXQEaJOp+ebR5sJVx6lSYpo2RqHrCsfqpDeN3N9TlA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193377; c=relaxed/simple; bh=FybevqZpXWgIdeyTpIylMgJljsEULH4XZaOUhiHho6Q=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=b4UqakbIjsCqIIGadLdqK9P4QsXSBHs4qAwmnQt2Ybr+fkbtTN7OiMbeBPCIETYgkuwwJwdkRzKkCG0OE/eHWcU7KstnpAP2QJGOz4qiY8JWaSKyfdmmImHxk5IAV2qiGmiZgvSRohH5ukcZ88TOfgi9ZStzeggsoITuFrVwo6Y= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193375; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=tvZDkF6QorHesoQXlXfReQwLcsFEV0Cax0jsqurioSY=; b=iK4dJgARPhTupXmIFrxHuXvcost0NtR4a9cURZrDJ6mOjtCdbIZZ9OqTwLdlw+peJcIKqD en4Bv4x/13mbqaLTAAlGX7HfVrF9+lDWo6003oI1U5zzbnHLCjSa5pKk6nA00am1/mw6zA x+vKiAvEOFrgjXKG1dS0d4b08vgiS6E= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-35-REWkVU0kOyCSHypBGqt1vQ-1; Sun, 28 Jul 2024 15:02:53 -0400 X-MC-Unique: REWkVU0kOyCSHypBGqt1vQ-1 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 763A6195609F for ; Sun, 28 Jul 2024 19:02:52 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5C7671955D42 for ; Sun, 28 Jul 2024 19:02:51 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 06/13] posix: Introduce the __arch_spawni function In-Reply-To: Message-ID: <465f313f9ae9e74fd610ca63f4afc02992ce68c0.1722193092.git.fweimer@redhat.com> References: X-From-Line: 465f313f9ae9e74fd610ca63f4afc02992ce68c0 Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:02:48 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org And move the definition of SPAWN_ERROR to spawn_int.h. The separate __arch_spawni function allows centralized implementation of preparatory code in posix/spawni.c, which is no longer a stub after this commit. --- posix/spawn_int.h | 7 +++++ posix/spawni.c | 15 ++--------- sysdeps/generic/arch-spawni.h | 27 +++++++++++++++++++ sysdeps/mach/hurd/{spawni.c => arch-spawni.h} | 12 ++++----- .../sysv/linux/{spawni.c => arch-spawni.h} | 18 ++++--------- 5 files changed, 47 insertions(+), 32 deletions(-) create mode 100644 sysdeps/generic/arch-spawni.h rename sysdeps/mach/hurd/{spawni.c => arch-spawni.h} (99%) rename sysdeps/unix/sysv/linux/{spawni.c => arch-spawni.h} (96%) diff --git a/posix/spawn_int.h b/posix/spawn_int.h index 2cdb55b4d6..9d0de483fe 100644 --- a/posix/spawn_int.h +++ b/posix/spawn_int.h @@ -78,6 +78,13 @@ struct __spawn_action #define SPAWN_XFLAGS_TRY_SHELL 0x2 #define SPAWN_XFLAGS_RET_PIDFD 0x4 +/* The Unix standard contains a long explanation of the way to signal + an error after the fork() was successful. Since no new wait status + was wanted there is no way to signal an error using one of the + available methods. The committee chose to signal an error by a + normal program exit with the exit code 127. */ +#define SPAWN_ERROR 127 + extern int __posix_spawn_file_actions_realloc (posix_spawn_file_actions_t * file_actions) attribute_hidden; diff --git a/posix/spawni.c b/posix/spawni.c index 52ff5f1966..ef47dd9ea4 100644 --- a/posix/spawni.c +++ b/posix/spawni.c @@ -19,15 +19,7 @@ #include #include #include "spawn_int.h" - - -/* The Unix standard contains a long explanation of the way to signal - an error after the fork() was successful. Since no new wait status - was wanted there is no way to signal an error using one of the - available methods. The committee chose to signal an error by a - normal program exit with the exit code 127. */ -#define SPAWN_ERROR 127 - +#include /* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ @@ -37,8 +29,5 @@ __spawni (pid_t *pid, const char *file, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], int xflags) { - __set_errno (ENOSYS); - return -1; + return __arch_spawni (pid, file, file_actions, attrp, argv, envp, xflags); } - -stub_warning (__spawni) diff --git a/sysdeps/generic/arch-spawni.h b/sysdeps/generic/arch-spawni.h new file mode 100644 index 0000000000..ad225d642d --- /dev/null +++ b/sysdeps/generic/arch-spawni.h @@ -0,0 +1,27 @@ +/* Architecture-specific __spawni implementation. Stub 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 int +__arch_spawni (pid_t *pid, const char *file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[], int xflags) +{ + __set_errno (ENOSYS); + return -1; +} diff --git a/sysdeps/mach/hurd/spawni.c b/sysdeps/mach/hurd/arch-spawni.h similarity index 99% rename from sysdeps/mach/hurd/spawni.c rename to sysdeps/mach/hurd/arch-spawni.h index a0d2e28c8e..6c1b9e98ad 100644 --- a/sysdeps/mach/hurd/spawni.c +++ b/sysdeps/mach/hurd/arch-spawni.h @@ -36,12 +36,12 @@ /* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ -int -__spawni (pid_t *pid, const char *file, - const posix_spawn_file_actions_t *file_actions, - const posix_spawnattr_t *attrp, - char *const argv[], char *const envp[], - int xflags) +static int +__arch_spawni (pid_t *pid, const char *file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, + char *const argv[], char *const envp[], + int xflags) { pid_t new_pid; char *path, *p, *name; diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/arch-spawni.h similarity index 96% rename from sysdeps/unix/sysv/linux/spawni.c rename to sysdeps/unix/sysv/linux/arch-spawni.h index f57e92815e..8caf74f7f5 100644 --- a/sysdeps/unix/sysv/linux/spawni.c +++ b/sysdeps/unix/sysv/linux/arch-spawni.h @@ -47,14 +47,6 @@ child has either exec'ed successfully or exited. */ -/* The Unix standard contains a long explanation of the way to signal - an error after the fork() was successful. Since no new wait status - was wanted there is no way to signal an error using one of the - available methods. The committee chose to signal an error by a - normal program exit with the exit code 127. */ -#define SPAWN_ERROR 127 - - struct posix_spawn_args { internal_sigset_t oldmask; @@ -483,11 +475,11 @@ __spawnix (int *pid, const char *file, /* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ -int -__spawni (pid_t * pid, const char *file, - const posix_spawn_file_actions_t * acts, - const posix_spawnattr_t * attrp, char *const argv[], - char *const envp[], int xflags) +static int +__arch_spawni (pid_t * pid, const char *file, + const posix_spawn_file_actions_t * acts, + const posix_spawnattr_t * attrp, char *const argv[], + char *const envp[], int xflags) { /* It uses __execvpex to avoid run ENOEXEC in non compatibility mode (it will be handled by maybe_script_execute). */ From patchwork Sun Jul 28 19:02:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94665 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 1F505385B508 for ; Sun, 28 Jul 2024 19:05:23 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 2F97F3858283 for ; Sun, 28 Jul 2024 19:03:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2F97F3858283 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2F97F3858283 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193389; cv=none; b=RXF4bZdfRRzQqHa87AKyTgnO4RNpJtqwGw2mNdK7AUHz6lm9vvLi0nG1yRed68oCOVFBzldZUJdbyviZ1JcuMtjXmOePmTBJCyAmFJ8rL3pXe9Keu8/6gTmg0heNoAj61M6YsdG53gAKAiv4f21UEWCiqIGMAJWDKcdCSgyHPjU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193389; c=relaxed/simple; bh=UN/4H/Bv9710ZHZiryGfUj+sTbMJ0Fe+HrqEXMD6CFc=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=jAb0mk6ukFy+S7Xz+y1OCiMmAUSTkhl6BEXJ5sGrAsd/3SorJp2H0P9oVKEAHuULGk+0t6NmtzRRvu3vLcbulcYNYzA16WitB1B5zrQ68iJgC9blfVWgpcPj1J9GM5gV4CggG973z1hQJEaGc9ueEY5zBrunR+7kOp7r3xFypiU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193380; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=m84xgeIsfbHXrn/XoVCDWJbvmq1fwvFgNQ8NubReKrw=; b=CAlhc1YXOOfP6Gth04ZTq7wv0cPB+NaaocLP7Mgv2vtjJWVZ99PZV4R9hnxYu9CNw/FPwV 7l4wd9pDs0sJPk1BK5lyK+X/8+oBLqOrLakjRBxzfZOe3nxxjc71rI7rtcqInY56tREsJ0 awF8exMR/C3HLnwy+2ADN4DZ8ATHNo4= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-470-6nhLUpIdOO6VQH2sW-Z34w-1; Sun, 28 Jul 2024 15:02:59 -0400 X-MC-Unique: 6nhLUpIdOO6VQH2sW-Z34w-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 69C611955F43 for ; Sun, 28 Jul 2024 19:02:58 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4C3AD1955F3B for ; Sun, 28 Jul 2024 19:02:57 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 07/13] stdlib: Add internal __getenvarray function In-Reply-To: Message-ID: References: X-From-Line: d1036d65c3f18dbe2e0a78cdec0bdee449e7c7a9 Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:02:54 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org --- stdlib/Makefile | 1 + stdlib/getenvarray.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ stdlib/setenv.h | 7 +++++ 3 files changed, 72 insertions(+) create mode 100644 stdlib/getenvarray.c diff --git a/stdlib/Makefile b/stdlib/Makefile index 697b547540..e1f1848439 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -79,6 +79,7 @@ routines := \ getcontext \ getentropy \ getenv \ + getenvarray \ getrandom \ getsubopt \ jrand48 \ diff --git a/stdlib/getenvarray.c b/stdlib/getenvarray.c new file mode 100644 index 0000000000..cac308fb32 --- /dev/null +++ b/stdlib/getenvarray.c @@ -0,0 +1,64 @@ +/* Obtain a consistent copy of the environ array. + 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 + +size_t +__getenvarray (char **result, size_t length) +{ + while (true) + { + /* The snapshotting approach follows getenv. Duplicates are + filtered out explicitly. */ + environ_counter start_counter = atomic_load_acquire (&__environ_counter); + char **start_environ = atomic_load_relaxed (&__environ); + if (start_environ == NULL) + return 0; + + const char *previous = NULL; + size_t count = 0; + for (char *const *ep = start_environ; ; ++ep) + { + char *entry = atomic_load_relaxed (ep); + if (entry == NULL) + break; + if (entry == previous) + /* Filter out a duplicate created by concurrent unsetenv. */ + continue; + if (count < length) + result[count] = entry; + ++count; + previous = entry; + } + + atomic_thread_fence_acquire (); + + if (atomic_load_acquire (&__environ_counter) != start_counter) + /* Retry if there has been more than one write to the array + from within unsetenv. Writes in setenv/putenv do not + matter because they do not change the key, so they cannot + introduce duplicates. */ + continue; + + if (count < length) + result[count] = NULL; + return count; + } +} diff --git a/stdlib/setenv.h b/stdlib/setenv.h index 3919943f87..69fa9367ed 100644 --- a/stdlib/setenv.h +++ b/stdlib/setenv.h @@ -47,6 +47,13 @@ __environ_is_from_array_list (char **ep) return eal != NULL && &eal->array[0] == ep; } +/* Make a copy of the environ array. Writes up to LENGTH pointers to + *RESULT, including a terminating null pointer if there is room. + *Returns the total number of non-null elements in the environ + *array. This number can be larger than LENGTH. */ +size_t __getenvarray (char **result, size_t length) + __attr_access ((__write_only__, 1, 2)) attribute_hidden; + /* Counter for detecting concurrent modification in unsetenv. Ideally, this should be a 64-bit counter that cannot wrap around, but given that counter wrapround is probably impossible to hit From patchwork Sun Jul 28 19:03:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94668 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 5ED60385843B for ; Sun, 28 Jul 2024 19:06:19 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 76DC8385840A for ; Sun, 28 Jul 2024 19:03:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 76DC8385840A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 76DC8385840A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193390; cv=none; b=TIxapyMqVTlocZ1SuYmHvFXA63aprpK9LXJBKkQB5Q7ry7ULHhO0z94HsxG2fS8uMYvy077qmSZrm8630TlA6I0bHDO61HZ8o34GtvidRN8R2/TBYOke3degwbCQrRP1ojsOZ0kjzzJ7oN9dWmAtZyeJ7Z+IUtggFvu0s6t0fsw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193390; c=relaxed/simple; bh=vhLAmO6a7XLCt6pqtJc5RIc0IaOqPvEi+IWXguJ8dFo=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=YaY7cEmAyE4GV5FTUzLGMW7K8ulO4b7Kut0CboRv2bxzI36jmI0wW05GzluWjLkoD4vcNKb1bvcmD15cKrNgY23Rrl+2oxtrcGTshW2Q9rJESxoZN0akw+tljW4IaWt9OWgUBM6pTsCD3BDZid5O8zUYnfgR2ivvrXTx5wXwRUE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193388; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=3+ojhHIHBFbUvbzKL0a9MJ+Iv0u0NU/0XEEaPqGv+cA=; b=ZfkvPIiC4Og+THIf9pbT56bONn335X7jTtI3zZNv8muDZmnp3xnV6l6iliqOyNPm3QjHJn H37msA3YoX9vZHvfMlB08gY4Qsm9I/9eL+zPCx/p6bWbsaZMtZFOuLgnhMY0XiD77QAov2 vZqkHiF5g95/9UGu6zU4tBjlYwC5x6Q= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-413-jbud1DlPPXqmE7XmVBwV0g-1; Sun, 28 Jul 2024 15:03:06 -0400 X-MC-Unique: jbud1DlPPXqmE7XmVBwV0g-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D724D19560B3 for ; Sun, 28 Jul 2024 19:03:05 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id DADF0300018D for ; Sun, 28 Jul 2024 19:03:04 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 08/13] support: Add support_count_maps In-Reply-To: Message-ID: References: X-From-Line: e3d2b6c46386cbc4a2442186403e4951b1e2814c Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:03:01 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org --- support/Makefile | 2 ++ support/support.h | 5 +++ support/support_count_maps.c | 51 +++++++++++++++++++++++++++++++ support/tst-support_count_maps.c | 52 ++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 support/support_count_maps.c create mode 100644 support/tst-support_count_maps.c diff --git a/support/Makefile b/support/Makefile index aa57207bdc..bc35faa325 100644 --- a/support/Makefile +++ b/support/Makefile @@ -53,6 +53,7 @@ libsupport-routines = \ support_chroot \ support_copy_file \ support_copy_file_range \ + support_count_maps \ support_create_timer \ support_descriptor_supports_holes \ support_descriptors \ @@ -319,6 +320,7 @@ tests = \ tst-support-process_state \ tst-support_blob_repeat \ tst-support_capture_subprocess \ + tst-support_count_maps \ tst-support_descriptors \ tst-support_format_dns_packet \ tst-support_quote_blob \ diff --git a/support/support.h b/support/support.h index ba21ec9b5a..5769e8c5f3 100644 --- a/support/support.h +++ b/support/support.h @@ -96,6 +96,11 @@ int support_descriptor_supports_holes (int fd); WHY_MSG as part of the diagnostic. */ void support_need_proc (const char *why_msg); +/* Counts the number of mappings in the process address space. + Currently implemented using /proc/self/maps on Linux, so it needs + that to exist. */ +size_t support_count_maps (void); + /* Error-checking wrapper functions which terminate the process on error. */ diff --git a/support/support_count_maps.c b/support/support_count_maps.c new file mode 100644 index 0000000000..2a6dd9fee3 --- /dev/null +++ b/support/support_count_maps.c @@ -0,0 +1,51 @@ +/* Count the number of mappings. + 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 + +size_t +support_count_maps (void) +{ + FILE *fp = fopen ("/proc/self/maps", "r"); + if (fp == NULL) + { + if (errno == ENOENT) + FAIL_UNSUPPORTED ("support_count_maps: test needs /proc/self/maps"); + else + FAIL_EXIT1 ("cannot open /proc/self/maps: %m"); + } + size_t count = 0; + while (true) + { + switch (getc_unlocked (fp)) + { + case EOF: + /* Error will be reported by xfclose below. */ + goto out; + case '\n': + ++count; + break; + } + } + out: + xfclose (fp); + return count; +} diff --git a/support/tst-support_count_maps.c b/support/tst-support_count_maps.c new file mode 100644 index 0000000000..e904a00cd4 --- /dev/null +++ b/support/tst-support_count_maps.c @@ -0,0 +1,52 @@ +/* Test counting the number of mappings. + 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 +#include +#include + +static int +do_test (void) +{ + long int page_size = xsysconf (_SC_PAGESIZE); + + size_t initial = support_count_maps (); + printf ("info: initial number of maps: %zu\n", initial); + + void *ptr = xmmap (NULL, 2 * page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + size_t one_mapping = support_count_maps (); + printf ("info: new number of maps: %zu\n", one_mapping); + TEST_COMPARE (initial + 1, one_mapping); + + /* The mapping gets split due to the protection flags change. */ + xmprotect (ptr, page_size, PROT_READ); + size_t two_mappings = support_count_maps (); + TEST_COMPARE (initial + 2, two_mappings); + + xmunmap (ptr, 2 * page_size); + size_t final = support_count_maps (); + TEST_COMPARE (initial, final); + + return 0; +} + +#include From patchwork Sun Jul 28 19:03:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94663 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 EC339385AC29 for ; Sun, 28 Jul 2024 19:05:05 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 5A7133858C3A for ; Sun, 28 Jul 2024 19:03:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5A7133858C3A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5A7133858C3A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193403; cv=none; b=eN+hc1GYfiNwzoJvUdQqbJQ0Yk8gieUuUC9wgsOQI5RlKnqLsViS2c2+cgHqL8FcC9zZJVEZ9cy72rVvPz8M5xCkXfScJDI5TidWiKz4wiPtLG0J3qD0LYIC49KxoW2PS9qkkElNM3SHP0g9GD0oRYKZz0iYA2Brs4VKr36qC0Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193403; c=relaxed/simple; bh=HQFBqX8twvZwG7JUFdPzJ+cPUnsdW0cTsXC3iIgCrAU=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=KaExbbcIljWE78sOLgV+JQP1M+PycsceM1R9WY8f0PBKOHYvacVnztA4Qtp4eMZKAxtB1ZC8uBTAI6o+MmC2bIKqleh484b8wrTmi0WC+TnysjiGIc1DpZDR0KjsgWjpQtXQ49U861vsp08yiPAzZOQH+xDBUludUarKizOzG1w= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193397; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Zof1q5mIePtim8bhKekmJ7ZOPePzaeLsc4gWqjUST8w=; b=AmRNEB7u4gS2c+AVkpvo8oo7JRqob543y07su1r/q8kKbsSuonO2MJ8V2HSlRCqQMEZD5t BK86OKUsaj5AX+otSx8uLmucNk9+ODLzh97ADaMv6XhrNjChKmPcdTUTOVKPi8adcGZO4f rmQX8o1UNfkwm4rbBc8KlvkxXE8aEEA= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-73-l5e4T5R3NR-J3z2q9bhMEg-1; Sun, 28 Jul 2024 15:03:13 -0400 X-MC-Unique: l5e4T5R3NR-J3z2q9bhMEg-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E6E331955D45 for ; Sun, 28 Jul 2024 19:03:12 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0F5B8195605F for ; Sun, 28 Jul 2024 19:03:11 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 09/13] posix: New tst-vfork-mappings test case In-Reply-To: Message-ID: <37178c0361a4e8d2be00daeef8241f0028488bfd.1722193092.git.fweimer@redhat.com> References: X-From-Line: 37178c0361a4e8d2be00daeef8241f0028488bfd Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:03:08 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org It checks if vfork followed by execve leaves additional memory mappings behind. --- posix/Makefile | 2 + posix/tst-vfork-mappings.c | 195 +++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 posix/tst-vfork-mappings.c diff --git a/posix/Makefile b/posix/Makefile index 2c598cd20a..ed3c9d0659 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -319,6 +319,7 @@ tests := \ tst-sysconf-empty-chroot \ tst-truncate \ tst-truncate64 \ + tst-vfork-mappings \ tst-vfork1 \ tst-vfork2 \ tst-wait3 \ @@ -635,6 +636,7 @@ $(objpfx)runptests.o: $(objpfx)ptestcases.h $(objpfx)tst-getopt-cancel: $(shared-thread-library) $(objpfx)tst-_Fork: $(shared-thread-library) +$(objpfx)tst-vfork-mappings: $(shared-thread-library) test-xfail-annexc = yes $(objpfx)annexc.out: $(objpfx)annexc diff --git a/posix/tst-vfork-mappings.c b/posix/tst-vfork-mappings.c new file mode 100644 index 0000000000..fcab423383 --- /dev/null +++ b/posix/tst-vfork-mappings.c @@ -0,0 +1,195 @@ +/* Test that vfork, *exec* does not accumulate mappings. + 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 +#include +#include +#include +#include +#include +#include + +/* 0 for direct calls, 1 for using an empheral thread. */ +static int use_thread; + +/* Wrapper used by test_one to invoke the callback on a new thread. */ +static void * +thread_wrapper (void *action) +{ + ((void (*) (void)) action) (); + return NULL; +} + +static void +test_one (const char *label, void (*action) (void)) +{ + printf ("info: testing %s%s\n", + label, use_thread ? " (threading mode)" : ""); + size_t before = support_count_maps (); + + /* Create mappings which should prevent merging of any persistant + new mappings, so that the count actually goes up in case of a + leak. */ + void *gap_maps[50]; + + for (int i = 0; i < array_length (gap_maps); ) + { + gap_maps[i++] = xmmap (NULL, 1, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + gap_maps[i++] = xmmap (NULL, 1, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + if (use_thread) + xpthread_join (xpthread_create (NULL, thread_wrapper, action)); + else + action (); + } + + for (int i = 0; i < array_length (gap_maps); ++i) + xmunmap (gap_maps[i], 1); + + size_t afterwards = support_count_maps (); + if (afterwards > before + 1) + { + printf ("error: %s: additional maps created: %zu (%zu -> %zu)\n", + label, afterwards - before, before, afterwards); + support_record_failure (); + } +} + +static void * +dummy_thread (void *ignored) +{ + return NULL; +} + +static void +do_system (void) +{ + TEST_COMPARE (system ("exit"), 0); +} + +static void +do_vfork_execl (void) +{ + pid_t ret = vfork (); + if (ret == 0) + { + execl ("/bin/true", "true", NULL); + _exit (1); + } + if (ret < 0) + FAIL_EXIT1 ("vfork"); + int status; + xwaitpid (ret, &status, 0); + TEST_COMPARE (status, 0); +} + +static void +do_vfork_execlp (void) +{ + pid_t ret = vfork (); + if (ret == 0) + { + execlp ("true", "true", NULL); + _exit (1); + } + if (ret < 0) + FAIL_EXIT1 ("vfork"); + int status; + xwaitpid (ret, &status, 0); + TEST_COMPARE (status, 0); +} + +static void +do_posix_spawn (void) +{ + pid_t pid; + char *const argv[] = { NULL }; + TEST_COMPARE (posix_spawn (&pid, "/bin/true", NULL, NULL, argv, environ), 0); + int status; + xwaitpid (pid, &status, 0); + TEST_COMPARE (status, 0); +} + +static void +do_posix_spawnp (void) +{ + pid_t pid; + char *const argv[] = { NULL }; + TEST_COMPARE (posix_spawnp (&pid, "true", NULL, NULL, argv, environ), 0); + int status; + xwaitpid (pid, &status, 0); + TEST_COMPARE (status, 0); +} + +static int +do_test (void) +{ + xpthread_join (xpthread_create (NULL, dummy_thread, NULL)); + + for (int do_pass = 0; do_pass < 4; ++do_pass) + { + printf ("info: pass %d\n", do_pass); + switch (do_pass) + { + case 1: + /* Switch on multi-threaded mode. */ + xpthread_join (xpthread_create (NULL, dummy_thread, NULL)); + break; + case 2: + /* Stuff the environment with some values. Aim for + consuming at least one page of pointers. */ + for (int i = 0; i <= 4096 / sizeof (char *) ; ++i) + { + char buf[20]; + snprintf (buf, sizeof (buf), "V%d", i); + TEST_COMPARE (setenv (buf, buf + 1, 1), 0); + } + break; + case 3: + /* Stuff the environment with even more values. Aim for three + pages (for small page sizes). */ + for (int i = 0; i <= 2 * 4096 / sizeof (char *); ++i) + { + char buf[20]; + snprintf (buf, sizeof (buf), "U%d", i); + TEST_COMPARE (setenv (buf, buf + 1, 1), 0); + } + break; + } + + for (use_thread = 0; use_thread < 2; ++use_thread) + { + if (do_pass >= 3) + /* With many environment variables, bash becomes rather + slow, so skip testing it. */ + test_one ("system", do_system); + test_one ("vfork/execl", do_vfork_execl); + test_one ("vfork/execlp", do_vfork_execlp); + test_one ("posix_spawn", do_posix_spawn); + test_one ("posix_spawnp", do_posix_spawnp); + } + } + + return 0; +} + +#include From patchwork Sun Jul 28 19:03:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94664 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 37BF0385828B for ; Sun, 28 Jul 2024 19:05:18 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id B62FE385B50B for ; Sun, 28 Jul 2024 19:03:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B62FE385B50B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B62FE385B50B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193408; cv=none; b=hgr1kXAuVmK0wJl+DtPUD0ZqKPOR7Lnbb0HQPw3G3hDbCXWet/LtXCIet04bTumCKrPwq4SDPD1UtA9XNEco32hLSPeghDkua6d5ouBssNuHTcBfzghz2+KGTRcDv2PQjvurLHru2+naKI1lsNuybmaZirNcH1TQt62dVpEAPTw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193408; c=relaxed/simple; bh=KNGyLcUbRfNXZqRop+z9ClFY0J9tyCXqPDDlngol320=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=ZJQaO4jPFeqmNsigK2SOp/HOwWooeBQEP2q/NqMJ2dmBsTFYsFgvgVQnsbF+sTaVp3kq3Hw5DASJuUqfsZITSCHlWAr9r5H1KuUoTO2vvdVeq+gCV8wS6EhqQf05W1nZGuByM5n0P+k1zFwftsDOT3y5Bvg+wsHDnUIq48hpM3o= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193405; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=EnQTNeKGIbH7vYu+douLX5soc+cpeUF2sULccW1wwWA=; b=Z6OnJx7HtRaAvAlPqfQN+uOhMhI5cwtND3nfwUZb+aaG+M04ki2NOGgu/2Lxprbp/OGVW0 FNd4fzcR5R1T1KyW8rrPDMrv5CMKG7uBg4jJEafBhbI2chVlnUcCvQCsuwMeASbtLH1Gun CYGSudHe3KXEtANYo6wHDSWvBwL8yNM= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-528-9Mu1PC67M4mehz-W3Dq5LA-1; Sun, 28 Jul 2024 15:03:20 -0400 X-MC-Unique: 9Mu1PC67M4mehz-W3Dq5LA-1 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 22D901956095 for ; Sun, 28 Jul 2024 19:03:20 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 057D719560AE for ; Sun, 28 Jul 2024 19:03:18 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 10/13] posix: Improve thread safety of functions using environ with execveat In-Reply-To: Message-ID: <9e2afd3526205a8bb40f3f6e9d16d671da3db59a.1722193092.git.fweimer@redhat.com> References: X-From-Line: 9e2afd3526205a8bb40f3f6e9d16d671da3db59a Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:03:15 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org The environment snapshot is stored on the stack, for easy memory management if vfork is involved. --- posix/execveat.c | 27 ++- stdlib/Makefile | 18 +- stdlib/environ_single_threaded_no_snapshot.c | 40 ++++ stdlib/setenv.h | 24 +- stdlib/tst-environ-execl.c | 35 +++ stdlib/tst-environ-snapshot-skeleton.c | 218 +++++++++++++++++++ 6 files changed, 354 insertions(+), 8 deletions(-) create mode 100644 stdlib/environ_single_threaded_no_snapshot.c create mode 100644 stdlib/tst-environ-execl.c create mode 100644 stdlib/tst-environ-snapshot-skeleton.c diff --git a/posix/execveat.c b/posix/execveat.c index 110177ea54..2cd275d02a 100644 --- a/posix/execveat.c +++ b/posix/execveat.c @@ -21,15 +21,38 @@ #include #include +#include /* Replace the current process, executing PATH relative to difrd with arguments argv and environment envp. argv and envp are terminated by NULL pointers. */ int -__execveat (int dirfd, const char *path, char *const argv[], char *const envp[], +__execveat (int dirfd, const char *path, char *const *argv, char *const *envp, int flags) { - return __arch_execveat (dirfd, path, argv, envp, flags); + if (envp == NULL || envp != __environ + || !__environ_is_from_array_list ((char **) envp) + || __environ_single_threaded_no_snapshot (envp)) + return __arch_execveat (dirfd, path, argv, envp, flags); + + size_t env_size = ENVIRON_STACK_SNAPSHOT_SIZE; + + while (true) + { + /* Using an on-stack buffer is not ideal, but it is the most + practical way to avoid memory leaks with vfork: New mappings + created before the execveat call will be preserved in the + original process before the vfork call even if the execve at + call succeeds and replaces the current process. */ + ++env_size; /* Make room for the null terminator. */ + char *env_copy[env_size]; + size_t env_count = __getenvarray (env_copy, env_size); + if (env_count < env_size) + return __arch_execveat (dirfd, path, argv, env_copy, flags); + + /* Retry with larger buffer. */ + env_size = env_count; + } } weak_alias (__execveat, execveat) diff --git a/stdlib/Makefile b/stdlib/Makefile index e1f1848439..498ec15e91 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -72,6 +72,7 @@ routines := \ drand48 \ drand48-iter \ drand48_r \ + environ_single_threaded_no_snapshot \ erand48 \ erand48_r \ exit \ @@ -374,11 +375,20 @@ tests-container := \ tst-system \ #tests-container +# These tests re-exec themselves, so they need to be linked +# statically, or use harcoded paths. +tests-with-reexec := \ + tst-environ-execl \ + # tests-with-reexec +tests += $(tests-with-reexec) + ifeq ($(build-hardcoded-path-in-tests),yes) tests += \ tst-empty-env \ # tests -endif +else # !$(build-hardcoded-path-in-tests) +tests-static += $(tests-with-reexec) +endif # !$(build-hardcoded-path-in-tests) LDLIBS-test-atexit-race = $(shared-thread-library) LDLIBS-test-at_quick_exit-race = $(shared-thread-library) @@ -631,3 +641,9 @@ $(objpfx)tst-qsort5: $(libm) $(objpfx)tst-getenv-signal: $(shared-thread-library) $(objpfx)tst-getenv-thread: $(shared-thread-library) $(objpfx)tst-getenv-unsetenv: $(shared-thread-library) +# See above for $(tests-with-reexec). +ifeq ($(build-hardcoded-path-in-tests),yes) +$(objpfx)tst-environ-execl: $(shared-thread-library) +else +$(objpfx)tst-environ-execl: $(static-thread-library) +endif diff --git a/stdlib/environ_single_threaded_no_snapshot.c b/stdlib/environ_single_threaded_no_snapshot.c new file mode 100644 index 0000000000..6dc1d22ea2 --- /dev/null +++ b/stdlib/environ_single_threaded_no_snapshot.c @@ -0,0 +1,40 @@ +/* Check for environ snapshots in single-threaded case. + 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 + +bool +__environ_single_threaded_no_snapshot (char *const *envp) +{ + if (!SINGLE_THREAD_P) + return false; + + const char *previous = NULL; + for (char *const *ep = envp; *ep != NULL; ++ep) + { + if (*ep == previous) + /* Must perform a snapshot to remove the duplicate. */ + return false; + previous = *ep; + } + /* No duplicates, no snapshot needed because no interference + from other threads is possible. */ + return true; +} diff --git a/stdlib/setenv.h b/stdlib/setenv.h index 69fa9367ed..3e530f0bc0 100644 --- a/stdlib/setenv.h +++ b/stdlib/setenv.h @@ -19,8 +19,10 @@ #ifndef _SETENV_H #define _SETENV_H -#include -#include +/* Not actually usable in rtld, but it is required for symbol discovery. */ +#if IS_IN (libc) || IS_IN (rtld) +# include +# include /* We use an exponential sizing policy for environment arrays. The arrays are not deallocating during the lifetime of the process. @@ -59,11 +61,11 @@ size_t __getenvarray (char **result, size_t length) but given that counter wrapround is probably impossible to hit (2**32 operations in unsetenv concurrently with getenv), using seems unnecessary. */ -#if __HAVE_64B_ATOMICS +# if __HAVE_64B_ATOMICS typedef uint64_t environ_counter; -#else +# else typedef uint32_t environ_counter; -#endif +# endif /* Updated by unsetenv to detect multiple overwrites in getenv. */ extern environ_counter __environ_counter attribute_hidden; @@ -77,4 +79,16 @@ extern environ_counter __environ_counter attribute_hidden; int __add_to_environ (const char *name, const char *value, const char *combines, int replace) attribute_hidden; +/* Returns true if the environ pointer ENVP (which should be equal to + environ) does not need snapshotting because execution is + single-threaded and there are no duplicates in the array. */ +bool __environ_single_threaded_no_snapshot (char *const *envp) + attribute_hidden; + +#endif /* IS_IN (libc) */ + +/* This seems to cover the majority of situations on the first + pass and requires only a moderate amount of stack size. */ +enum { ENVIRON_STACK_SNAPSHOT_SIZE = 120 }; + #endif /* _SETENV_H */ diff --git a/stdlib/tst-environ-execl.c b/stdlib/tst-environ-execl.c new file mode 100644 index 0000000000..b028a94c7d --- /dev/null +++ b/stdlib/tst-environ-execl.c @@ -0,0 +1,35 @@ +/* Test environment snapshots with execl. + 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 + . */ + +#define PROGRAM "tst-environ-execl" +#include "tst-environ-snapshot-skeleton.c" + +static pid_t +create_process (void) +{ + pid_t pid = vfork (); + if (pid < 0) + FAIL_EXIT1 ("vfork: %m"); + if (pid == 0) + { + execl (self_path, self_path, "verify", NULL); + printf ("error: execl failed: %m\n"); + _exit (17); + } + return pid; +} diff --git a/stdlib/tst-environ-snapshot-skeleton.c b/stdlib/tst-environ-snapshot-skeleton.c new file mode 100644 index 0000000000..8548e0a9e0 --- /dev/null +++ b/stdlib/tst-environ-snapshot-skeleton.c @@ -0,0 +1,218 @@ +/* Test skeleton for environment snapshots. + 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 + . */ + +/* The actual test including this file needs to define the macro + PROGRAM as a string literal, the name of the test program, before + including this file. + + It laso needs to define a function + + static pid_t create_process (void); + + after including this file, describing how the subprocess with the + environment snapshot is created. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pid_t create_process (void); + +/* Absolute path to the executable. */ +static char *self_path; + +/* Used to synchronize the start of each test iteration. */ +static pthread_barrier_t barrier; + +/* These are the variables set by the main thread. This needs to + exceed the stack reservation for the snapshot, to exercise that + code path. Variables before start_variable are not removed in the + unsetenv loop. */ +enum + { + on_stack_count = ENVIRON_STACK_SNAPSHOT_SIZE, + start_variable = 50, + }; +static char *variables[start_variable + on_stack_count]; + +/* Number of iterations. */ +enum { iterations = 5000 }; + +/* Check that even with concurrent unsetenv, a variable that is known + to be there is found. */ +static void * +test_thread (void *ignored) +{ + for (int i = 0; i < iterations; ++i) + { + xpthread_barrier_wait (&barrier); + pid_t pid = create_process (); + int status; + xwaitpid (pid, &status, 0); + TEST_COMPARE (status, 0); + xpthread_barrier_wait (&barrier); + } + return NULL; +} + +/* Called to verify the contents of the environment. */ +static void +verify_environ (void) +{ + TEST_COMPARE_STRING (getenv ("variable"), "value"); + char *changing = getenv ("changing"); + if (strcmp (changing, "value-1") != 0) + TEST_COMPARE_STRING (changing, "value-2"); + + bool seen[array_length (variables)] = { 0, }; + bool seen_changing = false; + bool seen_variable = false; + char **ep = environ; + for (int i = 0; i < start_variable; ++i) + { + char expected[20]; + snprintf (expected, sizeof (expected), "V%d=%d", i, i); + TEST_COMPARE_STRING (ep[i], expected); + if (ep[i] == NULL) + break; + } + + if (*ep == NULL) + return; + + for (ep += start_variable; *ep != NULL; ++ep) + { + if (**ep == 'V') + { + char *eq = strchr (*ep, '='); + TEST_VERIFY (eq != NULL); + if (eq != NULL) + { + int idx = atoi (eq + 1); + TEST_VERIFY (idx >= 0); + TEST_VERIFY (idx < array_length (variables)); + if (idx >= 0 && idx < array_length (variables)) + { + char expected[20]; + snprintf (expected, sizeof (expected), "V%d=%d", idx, idx); + TEST_COMPARE_STRING (*ep, expected); + if (seen[idx]) + { + printf ("error: duplicate: %s\n", *ep); + support_record_failure (); + } + seen[idx] = true; + } + } + } + else if (strcmp (*ep, "changing=value-1") == 0 + || strcmp (*ep, "changing=value-2") == 0) + { + if (seen_changing) + { + printf ("error: duplicate: %s\n", *ep); + support_record_failure (); + } + seen_changing = true; + } + else + { + TEST_COMPARE_STRING (*ep, "variable=value"); + if (seen_variable) + { + printf ("error: duplicate: %s\n", *ep); + support_record_failure (); + } + seen_variable = true; + } + } +} + +/* In the prepare function, verify the environment contents after a + self-exec. */ +#define PREPARE do_prepare +static void +do_prepare (int argc, char *argv[]) +{ + if (argc == 2 && strcmp (argv[1], "verify") == 0) + { + verify_environ (); + + /* Pass errors through to the other process. */ + _exit (support_record_failure_is_failed () ? 1 : 0); + } +} + +static int +do_test (void) +{ + self_path = xasprintf ("%s/stdlib/%s", support_objdir_root, PROGRAM); + xpthread_barrier_init (&barrier, NULL, 2); + pthread_t thr = xpthread_create (NULL, test_thread, NULL); + + for (int i = 0; i < array_length (variables); ++i) + variables[i] = xasprintf ("V%d", i); + + for (int i = 0; i < iterations; ++i) + { + int variable_count = array_length (variables); + if ((iterations % 2) == 0) + /* Use the on-stack code path in __environ_snapshot_get. */ + variable_count = on_stack_count - 1; + + clearenv (); + for (int j = 0; j < variable_count; ++j) + { + /* Add the changing variable somewhere in the middle. */ + if (j == start_variable + 5) + TEST_COMPARE (setenv ("changing", "value-2", 1), 0); + TEST_COMPARE (setenv (variables[j], variables[j] + 1, 1), 0); + } + TEST_COMPARE (setenv ("variable", "value", 1), 0); + xpthread_barrier_wait (&barrier); + + /* Leave a certain amount of variables in place. */ + for (int j = start_variable; j < variable_count; ++j) + { + /* Every few iterations, change the variable. */ + if ((j % 4) == 0) + TEST_COMPARE (setenv ("changing", + ((j / 4) % 2) == 0 ? "value-1" : "value-2", + 1), 0); + TEST_COMPARE (unsetenv (variables[j]), 0); + } + + xpthread_barrier_wait (&barrier); + } + xpthread_join (thr); + xpthread_barrier_destroy (&barrier); + for (int i = 0; i < array_length (variables); ++i) + free (variables[i]); + free (self_path); + return 0; +} + +#include From patchwork Sun Jul 28 19:03:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94671 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 BE4273858C52 for ; Sun, 28 Jul 2024 19:07:32 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id B7FBE385C6CA for ; Sun, 28 Jul 2024 19:03:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B7FBE385C6CA Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B7FBE385C6CA Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193414; cv=none; b=CgkL5R399xd6EREvquk8Vi/8JUsXPhkNF9chx5xlp0aby7v0VsROniTF42C0MjJzjqTTz/3bdX90z6GneqJds1CkDsck41fmKTYBRDNGLG36W7rtJsCqB+mJWHPv/EhX7ZBaRz3nt4k5khBPVbGOXVsOwYYBnlh1k0cvfV5QCfU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193414; c=relaxed/simple; bh=5chBUYmcr+mJBOn9KElLZsJZpZJDKbl82bb4UG1YLJk=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=GEL0J1Ec+mOtGRY+wDyJXGp8AnOhZrlkatrQ/L3Bzni8tzpehdLJRzM+mEp0aM4NHZWTq0Ec5hPYlJ9lx4jVMI+WxswwA+a7stag3Mb7GGzZ87oVBVFTj6ieWFdrk/NIKVpJXfJGtmCzyiqWODWSSEUg2Xy8WC+jVhfpFg/pQAk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193409; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=xsmVM/rsoJrmjcYWayPIXtyiZTqzjg5scmSIRp8jNFI=; b=KVXbNA6eMWkQVzTKY1TqN0GHI4V2yZbTCgRPgmLfiHBjGbV12r0SNvVUFHR6gLynDbN9HT 2/VxZ1XqC9joTY43YRNaGjCkyeBkHh90v31NQkeNSbROiws6vcZIyH6Mvvk0zSQ1rVOCVy 1/vIkaPiwXefAkj4FgXXqd7GtYAzh/k= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-657-fUOeHMyCMx6XkEyRukpPgg-1; Sun, 28 Jul 2024 15:03:26 -0400 X-MC-Unique: fUOeHMyCMx6XkEyRukpPgg-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2AB201955F3B for ; Sun, 28 Jul 2024 19:03:26 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0F5D1300018D for ; Sun, 28 Jul 2024 19:03:24 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 11/13] posix: Implement environ snapshotting in __spawni In-Reply-To: Message-ID: References: X-From-Line: b5927afe9408f9118035c45202b83e3a70e47f4a Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:03:21 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org This keeps stack usage bounded if environ is used with the functions in the posix_spawn family. --- posix/spawni.c | 94 +++++++++++++++++++++++++++++++- stdlib/Makefile | 3 + stdlib/tst-environ-posix_spawn.c | 31 +++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 stdlib/tst-environ-posix_spawn.c diff --git a/posix/spawni.c b/posix/spawni.c index ef47dd9ea4..02e3ad0c8d 100644 --- a/posix/spawni.c +++ b/posix/spawni.c @@ -20,6 +20,29 @@ #include #include "spawn_int.h" #include +#include +#include +#include + +/* Used to keep track of the memory mapping for the environment + snapshot. */ +struct spawni_environ_snapshot +{ + char **data; + size_t size; /* In bytes. */ +}; + +static void +__spawni_environ_snapshot_unmap (void *arg) +{ + struct spawni_environ_snapshot *ses = arg; + if (ses->data != NULL) + { + int save_errno = errno; + __munmap (ses->data, ses->size); + __set_errno (save_errno); + } +} /* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ @@ -29,5 +52,74 @@ __spawni (pid_t *pid, const char *file, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], int xflags) { - return __arch_spawni (pid, file, file_actions, attrp, argv, envp, xflags); + if (envp == NULL || envp != __environ + || !__environ_is_from_array_list ((char **) envp) + || __environ_single_threaded_no_snapshot (envp)) + return __arch_spawni (pid, file, file_actions, attrp, argv, envp, xflags); + + /* Perform the environment snapshot. It is beneficial to do this + here, and not in execveat, because it is possible to use an + mmap-backed array to avoid a huge stack allocation. Memory + management is easier because the allocation happens before vfork + and can be undone completely before the function returns. */ + + size_t env_size; + { + char *on_stack[ENVIRON_STACK_SNAPSHOT_SIZE]; + env_size = __getenvarray (on_stack, ENVIRON_STACK_SNAPSHOT_SIZE); + if (env_size < ENVIRON_STACK_SNAPSHOT_SIZE) + return __arch_spawni (pid, file, file_actions, attrp, argv, + on_stack, xflags); + } + + int ret; + + /* Use mmap for async-signal-safety. Async-cancel-safety has + historically not been provided, so do not disable asynchronous + cancellation. As a result, there are memory leaks. */ + struct spawni_environ_snapshot ses = { NULL, 0 }; + __libc_cleanup_push (__spawni_environ_snapshot_unmap, &ses); + + while (true) + { + /* Make room for the null terminator. */ + ++env_size; + + /* Create the allocation. */ + { + size_t size = env_size * sizeof (char *); + void *ptr = __mmap (NULL, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) + { + ret = -1; + break; + } + ses.data = ptr; + ses.size = size; + } + + /* Try to get a snapshot. */ + size_t env_count = __getenvarray (ses.data, env_size); + if (env_count < env_size) + { + ret = __arch_spawni (pid, file, file_actions, attrp, argv, + ses.data, xflags); + break; + } + + /* The environment grew. Unmap the allocation that turned out + to be too small. Make a copy to avoid spurious unmaps with + asynchronous cancellation. */ + struct spawni_environ_snapshot copy = ses; + ses.data = NULL; + __spawni_environ_snapshot_unmap (©); + + /* Try again with the updated size. */ + env_size = env_count; + } + + __libc_cleanup_pop (1); + return ret; } diff --git a/stdlib/Makefile b/stdlib/Makefile index 498ec15e91..5934b683b1 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -379,6 +379,7 @@ tests-container := \ # statically, or use harcoded paths. tests-with-reexec := \ tst-environ-execl \ + tst-environ-posix_spawn \ # tests-with-reexec tests += $(tests-with-reexec) @@ -644,6 +645,8 @@ $(objpfx)tst-getenv-unsetenv: $(shared-thread-library) # See above for $(tests-with-reexec). ifeq ($(build-hardcoded-path-in-tests),yes) $(objpfx)tst-environ-execl: $(shared-thread-library) +$(objpfx)tst-environ-posix_spawn: $(shared-thread-library) else $(objpfx)tst-environ-execl: $(static-thread-library) +$(objpfx)tst-environ-posix_spawn: $(static-thread-library) endif diff --git a/stdlib/tst-environ-posix_spawn.c b/stdlib/tst-environ-posix_spawn.c new file mode 100644 index 0000000000..4791e19f1f --- /dev/null +++ b/stdlib/tst-environ-posix_spawn.c @@ -0,0 +1,31 @@ +/* Test environment snapshots with posix_spawn. + 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 + . */ + +#define PROGRAM "tst-environ-posix_spawn" +#include "tst-environ-snapshot-skeleton.c" + +#include + +static pid_t +create_process (void) +{ + char *argv[] = { self_path, (char *) "verify", NULL }; + pid_t pid; + return posix_spawn (&pid, self_path, NULL, NULL, argv, environ); + return pid; +} From patchwork Sun Jul 28 19:03:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94667 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 AE39B385841D for ; Sun, 28 Jul 2024 19:06:14 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 5B9FB385842A for ; Sun, 28 Jul 2024 19:03:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5B9FB385842A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5B9FB385842A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193417; cv=none; b=piZaiiv3u8A7p/b2dTQazy6VLWK9x/uaIYB2XTnOvhtuqfjI4/BA+UQscf2J/9hSYqWXSzLIHw5kHt88ATLohycMMAzSzTwUmeh1A5v9JbF0dote01bdCRN1biKNGGQGS7KQaqAi76YJ44ovKqTRkaiJ8RztPFMsBTPb0BDL6r0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193417; c=relaxed/simple; bh=1HSA3xATEnNNS402giZmTNYscXmDzmEHF1P2vhuxji4=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=sGlt2xoPZAqJmghdvWuGxnK9ez3mljCbtQERwvuC7oH4vp4pRHse7LHQb1LevgCNvA/f/Sk1Dt7LcZmNjS2DMuvFaAEGXK4JJePaXD4DPK+DDDr5caTm/IbhYXbi2iKtoPvAHlt4qaZfA+ADUxJqMjTGPacv2ZrxRYD848qhRNM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193415; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=D/SARHqkLmt7r/W0RZUy0k9xjdht7+Q20DZTyStvT6c=; b=ZL0gcd7Tjs/gnB+vfJ8rCgHqM5My9LIIxyIokUX7dShHJjphYVNLZLhy6RlwZiJzU6iBm8 RclERXIArTF/G8qhtmWaVv1JUHyiwj1eAv/6PRKHwwUJTc4hdjoXWuDPVlVfQHNndye5jU LD3kF1QiL1s1icMImvTDonNxi8zPZ/4= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-638-hgQ3-VadMFWS-uuZKl1U1w-1; Sun, 28 Jul 2024 15:03:33 -0400 X-MC-Unique: hgQ3-VadMFWS-uuZKl1U1w-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 942EB19560A2 for ; Sun, 28 Jul 2024 19:03:32 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 32DB71955F40 for ; Sun, 28 Jul 2024 19:03:30 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 12/13] posix: Acquire the environment lock during fork In-Reply-To: Message-ID: <4896ad435ba63221db22c6430fbe99405e7753de.1722193092.git.fweimer@redhat.com> References: X-From-Line: 4896ad435ba63221db22c6430fbe99405e7753de Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:03:28 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org This makes setenv, unsetenv etc. usable after fork with the builtin-malloc (avoid deadlocks). Deadlocks may still occur with replacement mallocs. If this turns out to be a problem, a bit of refactoring should allow calling malloc without the environment lock in unsetenv. --- posix/fork.c | 9 +++++++++ stdlib/Makefile | 2 ++ stdlib/getenv.c | 2 ++ stdlib/setenv.c | 7 ++----- stdlib/setenv.h | 4 ++++ stdlib/tst-environ-fork.c | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 stdlib/tst-environ-fork.c diff --git a/posix/fork.c b/posix/fork.c index 298765a1ff..75d0910a94 100644 --- a/posix/fork.c +++ b/posix/fork.c @@ -25,6 +25,7 @@ #include #include #include +#include static void fresetlockfiles (void) @@ -64,6 +65,10 @@ __libc_fork (void) _IO_list_lock (); + /* The setenv function acquires this lock, and then the malloc + locks. Use the same lock order here. */ + __libc_lock_lock (__environ_lock); + /* Acquire malloc locks. This needs to come last because fork handlers may use malloc, and the libio list lock has an indirect malloc dependency as well (via the getdelim @@ -87,6 +92,8 @@ __libc_fork (void) /* Release malloc locks. */ call_function_static_weak (__malloc_fork_unlock_child); + __libc_lock_unlock (__environ_lock); + /* Reset the file list. These are recursive mutexes. */ fresetlockfiles (); @@ -119,6 +126,8 @@ __libc_fork (void) /* Release malloc locks, parent process variant. */ call_function_static_weak (__malloc_fork_unlock_parent); + __libc_lock_unlock (__environ_lock); + /* We execute this even if the 'fork' call failed. */ _IO_list_unlock (); } diff --git a/stdlib/Makefile b/stdlib/Makefile index 5934b683b1..5613c34bcb 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -277,6 +277,7 @@ tests := \ tst-canon-bz26341 \ tst-cxa_atexit \ tst-environ \ + tst-environ-fork \ tst-getenv-signal \ tst-getenv-thread \ tst-getenv-unsetenv \ @@ -639,6 +640,7 @@ $(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3 $(objpfx)tst-qsort5: $(libm) +$(objpfx)tst-environ-fork: $(shared-thread-library) $(objpfx)tst-getenv-signal: $(shared-thread-library) $(objpfx)tst-getenv-thread: $(shared-thread-library) $(objpfx)tst-getenv-unsetenv: $(shared-thread-library) diff --git a/stdlib/getenv.c b/stdlib/getenv.c index ad7f0d00c8..c581ecf951 100644 --- a/stdlib/getenv.c +++ b/stdlib/getenv.c @@ -19,7 +19,9 @@ #include #include #include +#include +__libc_lock_define_initialized (, __environ_lock); struct environ_array *__environ_array_list; environ_counter __environ_counter; diff --git a/stdlib/setenv.c b/stdlib/setenv.c index 6bd0596b79..a97f6de4d4 100644 --- a/stdlib/setenv.c +++ b/stdlib/setenv.c @@ -55,11 +55,8 @@ extern char **environ; #endif #if _LIBC -/* This lock protects against simultaneous modifications of `environ'. */ -# include -__libc_lock_define_initialized (static, envlock) -# define LOCK __libc_lock_lock (envlock) -# define UNLOCK __libc_lock_unlock (envlock) +# define LOCK __libc_lock_lock (__environ_lock) +# define UNLOCK __libc_lock_unlock (__environ_lock) #else # define LOCK # define UNLOCK diff --git a/stdlib/setenv.h b/stdlib/setenv.h index 3e530f0bc0..1a184594ce 100644 --- a/stdlib/setenv.h +++ b/stdlib/setenv.h @@ -22,8 +22,12 @@ /* Not actually usable in rtld, but it is required for symbol discovery. */ #if IS_IN (libc) || IS_IN (rtld) # include +# include # include +/* This lock protects against simultaneous modifications of `environ'. */ +__libc_lock_define (extern, __environ_lock attribute_hidden); + /* We use an exponential sizing policy for environment arrays. The arrays are not deallocating during the lifetime of the process. This adds between one and two additional pointers per active diff --git a/stdlib/tst-environ-fork.c b/stdlib/tst-environ-fork.c new file mode 100644 index 0000000000..0e25b324e7 --- /dev/null +++ b/stdlib/tst-environ-fork.c @@ -0,0 +1,38 @@ +/* Test environment snapshots with fork. + 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 + . */ + +/* This test uses the process-creation skeleton, but does not performa + re-exec, so the use of PROGRAM is a bit misleading here. */ + +#define PROGRAM "tst-environ-fork" +#include "tst-environ-snapshot-skeleton.c" + +static pid_t +create_process (void) +{ + pid_t pid = xfork (); + if (pid == 0) + { + verify_environ (); + /* Verify that it is possible to obtain the environment lock. */ + TEST_COMPARE (setenv ("unrelated", "unrelated-set", 1), 0); + TEST_COMPARE_STRING (getenv ("unrelated"), "unrelated-set"); + _exit (0); + } + return pid; +} From patchwork Sun Jul 28 19:03:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 94670 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 704DA385842A for ; Sun, 28 Jul 2024 19:07:16 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 0157D385841D for ; Sun, 28 Jul 2024 19:03:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0157D385841D Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 0157D385841D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193430; cv=none; b=GYqChxkEor+eoneiHQBHKqu8lBUjUnXdkpYisjD1yrSQTE28k36gpv8wnu1EUdDH5W0OvKA5Jtdkjyhqo1Dq3I0OGDBtpO6gOmFLORVEcSBTpgW47DpzwLbjjntLEuVt0FoCpqMUX1puxhKXJW4VvSkCcVWAy4fcQcLb5gCzIHA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722193430; c=relaxed/simple; bh=oiR9oSGRUBsyZ/ez+uY+/ZLx4xsHSfOrCoUbElXQz1s=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=MjXxcGvi0CJyFo0AwbQecm3ZGTIS3e+IEnS5ME1wTMag9WsAt2Omy9i0/KbO9Pn1A7Ug5XfUwJ0dPAah2D8MqU2z70y9hozKR3MinQ89Lfs8y2jzCBeKQUiBLvHuaZ4gxAIA6rtsLFwOQZRbKb0lCzBG0WoDTW9b+Ljjznj41oU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722193423; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=CLBQt6AfgYUhIpOxOHYUH28uf3YPnFp1Yd+9JyhV1jA=; b=jTI9dw6RoAu2N9dAf1pv5fbYAIBy14kouPPxkq7NXDB0bGOieTmVlaogblZ9Qfa2NhESon W6IcNr/6jFkdDb5suhBX0l3I8FNlWAgFN3BT8dwM+Y0oqk5rGo5wHZaGHOV5he+6q3oEHk 2X3FeIUCQREpPKTTh4PwFHoOIXWbI0c= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-161-e8uhNSZ9OFy6XgEb2Ms97Q-1; Sun, 28 Jul 2024 15:03:40 -0400 X-MC-Unique: e8uhNSZ9OFy6XgEb2Ms97Q-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 76D721955D44 for ; Sun, 28 Jul 2024 19:03:39 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.24]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 02CB83000193 for ; Sun, 28 Jul 2024 19:03:37 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 13/13] manual: Updates for thread-safe getenv In-Reply-To: Message-ID: <7b639a0c6ed7b5b4bc0f4f5e027eb93d8781e250.1722193092.git.fweimer@redhat.com> References: X-From-Line: 7b639a0c6ed7b5b4bc0f4f5e027eb93d8781e250 Mon Sep 17 00:00:00 2001 Date: Sun, 28 Jul 2024 21:03:34 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org --- NEWS | 5 ++ manual/argp.texi | 20 +++--- manual/conf.texi | 2 +- manual/filesys.texi | 4 +- manual/getopt.texi | 6 +- manual/intro.texi | 17 +---- manual/locale.texi | 9 +-- manual/macros.texi | 4 -- manual/memory.texi | 18 +++--- manual/message.texi | 37 +++++------ manual/pattern.texi | 64 +++++++++---------- manual/process.texi | 4 +- manual/socket.texi | 145 +++++++++++++++++++++--------------------- manual/startup.texi | 122 ++++++++++++++++++++++++++++------- manual/sysinfo.texi | 2 +- manual/syslog.texi | 18 +++--- manual/time.texi | 150 +++++++++++++++++++++----------------------- 17 files changed, 333 insertions(+), 294 deletions(-) diff --git a/NEWS b/NEWS index d488874aba..026be0c167 100644 --- a/NEWS +++ b/NEWS @@ -89,6 +89,11 @@ Major new features: of large writes. This behaviour is controlled by a new tunable x86_memset_non_temporal_threshold. +* The getenv function is now thread-safe and async-signal-safe, provided + that the environ array is only manipulated using the function setenv, + unsetenv, and clearenv. If putenv is used, the safety properties of + getenv depend on how the string into the environment is updated. + Deprecated and removed features, and other changes affecting compatibility: * Architectures which use a 32-bit seconds-since-epoch field in struct diff --git a/manual/argp.texi b/manual/argp.texi index 0023441812..b09e0b9880 100644 --- a/manual/argp.texi +++ b/manual/argp.texi @@ -35,7 +35,7 @@ needed in @code{main}. @deftypefun {error_t} argp_parse (const struct argp *@var{argp}, int @var{argc}, char **@var{argv}, unsigned @var{flags}, int *@var{arg_index}, void *@var{input}) @standards{GNU, argp.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtslocale{} @mtsenv{}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} @c Optionally alloca()tes standard help options, initializes the parser, @c then parses individual args in a loop, and then finalizes. @c parser_init @@ -56,12 +56,12 @@ needed in @code{main}. @c group_parse dup @c parser_parse_opt @c group_parse dup -@c argp_error dup @mtasurace:argpbuf @mtsenv @mtslocale @ascuheap @ascuintl @asucorrupt @acsmem @acucorrupt @aculock -@c dgettext (bad key error) dup @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c argp_error dup @mtasurace:argpbuf @mtslocale @ascuheap @ascuintl @asucorrupt @acsmem @acucorrupt @aculock +@c dgettext (bad key error) dup @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem @c parser_finalize @c group_parse @c fprintf dup @mtslocale @asucorrupt @aculock @acucorrupt [no @ascuheap @acsmem] -@c dgettext dup @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c dgettext dup @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem @c arg_state_help @c free dup @ascuhelp @acsmem The @code{argp_parse} function parses the arguments in @var{argv}, of @@ -666,7 +666,7 @@ parser function. @xref{Argp Parsing State}. @cindex usage messages, in argp @deftypefun void argp_usage (const struct argp_state *@var{state}) @standards{GNU, argp.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} @c Just calls argp_state_help with stderr and ARGP_HELP_STD_USAGE. Outputs the standard usage message for the argp parser referred to by @var{state} to @code{@var{state}->err_stream} and terminates the program @@ -676,7 +676,7 @@ with @code{exit (argp_err_exit_status)}. @xref{Argp Global Variables}. @cindex syntax error messages, in argp @deftypefun void argp_error (const struct argp_state *@var{state}, const char *@var{fmt}, @dots{}) @standards{GNU, argp.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} @c Lock stream, vasprintf the formatted message into a buffer, print the @c buffer prefixed by the short program name (in libc, @c argp_short_program_name is a macro that expands to @@ -713,7 +713,7 @@ for options, bad phase of the moon, etc. @deftypefun void argp_state_help (const struct argp_state *@var{state}, FILE *@var{stream}, unsigned @var{flags}) @standards{GNU, argp.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} @c Just calls _help with the short program name and optionally exit. @c The main problems in _help, besides the usual issues with stream I/O @c and translation, are the use of a static buffer (uparams, thus @@ -721,11 +721,11 @@ for options, bad phase of the moon, etc. @c from the environment for ARGP_HELP_FMT, accessing the locale object @c multiple times. -@c _help @mtsenv @mtasurace:argpbuf @mtslocale @ascuheap @ascuintl @asucorrupt @acsmem @acucorrupt @aculock +@c _help @mtasurace:argpbuf @mtslocale @ascuheap @ascuintl @asucorrupt @acsmem @acucorrupt @aculock @c dgettext @ascuintl @c flockfile @aculock @c funlockfile @aculock -@c fill_in_uparams @mtsenv @mtasurace:argpbuf @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem +@c fill_in_uparams @mtasurace:argpbuf @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem @c argp_failure dup (status = errnum = 0) @c atoi dup @mtslocale @c argp_hol @ascuheap @acsmem @@ -1066,7 +1066,7 @@ program options, argp offers the @code{argp_help} interface. @deftypefun void argp_help (const struct argp *@var{argp}, FILE *@var{stream}, unsigned @var{flags}, char *@var{name}) @standards{GNU, argp.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:argpbuf} @mtslocale{}}@asunsafe{@ascuheap{} @ascuintl{} @asucorrupt{}}@acunsafe{@acsmem{} @acucorrupt{} @aculock{}}} @c Just calls _help. This outputs a help message for the argp parser @var{argp} to @var{stream}. The type of messages printed will be determined by diff --git a/manual/conf.texi b/manual/conf.texi index be680e0692..c522d56955 100644 --- a/manual/conf.texi +++ b/manual/conf.texi @@ -272,7 +272,7 @@ constants are declared in the header file @file{unistd.h}. @deftypefun {long int} sysconf (int @var{parameter}) @standards{POSIX.1, unistd.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} @c Some parts of the implementation open /proc and /sys files and dirs @c to collect system details, using fd and stream I/O depending on the @c case. The returned max value may change over time for NPROCS, diff --git a/manual/filesys.texi b/manual/filesys.texi index 47d929744e..40c2e53e06 100644 --- a/manual/filesys.texi +++ b/manual/filesys.texi @@ -150,7 +150,7 @@ this function is deprecated. @vindex PWD @deftypefun {char *} get_current_dir_name (void) @standards{GNU, unistd.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{} @acsfd{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{} @acsfd{}}} @c Besides getcwd, which this function calls as a fallback, it calls @c getenv, with the potential thread-safety issues that brings about. The @code{get_current_dir_name} function is basically equivalent to @@ -3443,7 +3443,7 @@ never less than @code{25}. @deftypefun {char *} tempnam (const char *@var{dir}, const char *@var{prefix}) @standards{SVID, stdio.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} @c There's no way (short of being setuid) to avoid getenv("TMPDIR"), @c even with a non-NULL dir. @c diff --git a/manual/getopt.texi b/manual/getopt.texi index b4c0b15ac2..5c87100a46 100644 --- a/manual/getopt.texi +++ b/manual/getopt.texi @@ -55,7 +55,7 @@ option argument, for those options that accept arguments. @deftypefun int getopt (int @var{argc}, char *const *@var{argv}, const char *@var{options}) @standards{POSIX.2, unistd.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:getopt} @mtsenv{}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:getopt}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} @c Swapping elements of passed-in argv may be partial in case of @c cancellation. Gettext brings about a whole lot of AS and AC safety @c issues. The getopt API involves returning values in the @@ -237,7 +237,7 @@ was seen. @deftypefun int getopt_long (int @var{argc}, char *const *@var{argv}, const char *@var{shortopts}, const struct option *@var{longopts}, int *@var{indexptr}) @standards{GNU, getopt.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:getopt} @mtsenv{}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:getopt}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} @c Same issues as getopt. Decode options from the vector @var{argv} (whose length is @var{argc}). The argument @var{shortopts} describes the short options to accept, just as @@ -292,7 +292,7 @@ getopt functionality there is one more function available. @deftypefun int getopt_long_only (int @var{argc}, char *const *@var{argv}, const char *@var{shortopts}, const struct option *@var{longopts}, int *@var{indexptr}) @standards{GNU, getopt.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:getopt} @mtsenv{}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:getopt}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} @c Same issues as getopt. The @code{getopt_long_only} function is equivalent to the diff --git a/manual/intro.texi b/manual/intro.texi index 879c1b38d9..2a0f882184 100644 --- a/manual/intro.texi +++ b/manual/intro.texi @@ -433,7 +433,7 @@ Functions marked with @code{i18n} may call internationalization functions of the @code{gettext} family and will be only as safe as those functions. This note is thus equivalent to: -@sampsafety{@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @ascudlopen{}}@acunsafe{@acucorrupt{}}} +@sampsafety{@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @ascudlopen{}}@acunsafe{@acucorrupt{}}} @item @code{timer} @@ -732,21 +732,6 @@ constant in these contexts, which makes the former safe. @c because of the unexpected locale changes. -@item @code{env} -@cindex env - -Functions marked with @code{env} as an MT-Safety issue access the -environment with @code{getenv} or similar, without any guards to ensure -safety in the presence of concurrent modifications. - -We do not mark these functions as MT- or AS-Unsafe, however, because -functions that modify the environment are all marked with -@code{const:env} and regarded as unsafe. Being unsafe, the latter are -not to be called when multiple threads are running or asynchronous -signals are enabled, and so the environment can be considered -effectively constant in these contexts, which makes the former safe. - - @item @code{hostid} @cindex hostid diff --git a/manual/locale.texi b/manual/locale.texi index 1b3f97839b..9e20295fc3 100644 --- a/manual/locale.texi +++ b/manual/locale.texi @@ -222,23 +222,21 @@ The symbols in this section are defined in the header file @file{locale.h}. @deftypefun {char *} setlocale (int @var{category}, const char *@var{locale}) @standards{ISO, locale.h} -@safety{@prelim{}@mtunsafe{@mtasuconst{:@mtslocale{}} @mtsenv{}}@asunsafe{@asuinit{} @asulock{} @ascuheap{} @asucorrupt{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} +@safety{@prelim{}@mtunsafe{@mtasuconst{:@mtslocale{}}}@asunsafe{@asuinit{} @asulock{} @ascuheap{} @asucorrupt{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} @c Uses of the global locale object are unguarded in functions that @c ought to be MT-Safe, so we're ruling out the use of this function @c once threads are started. It takes a write lock itself, but it may @c return a pointer loaded from the global locale object after releasing @c the lock, or before taking it. -@c setlocale @mtasuconst:@mtslocale @mtsenv @asuinit @ascuheap @asulock @asucorrupt @acucorrupt @acsmem @acsfd @aculock +@c setlocale @mtasuconst:@mtslocale @asuinit @ascuheap @asulock @asucorrupt @acucorrupt @acsmem @acsfd @aculock @c libc_rwlock_wrlock @asulock @aculock @c libc_rwlock_unlock @aculock -@c getenv LOCPATH @mtsenv @c malloc @ascuheap @acsmem @c free @ascuheap @acsmem @c new_composite_name ok @c setdata ok @c setname ok -@c _nl_find_locale @mtsenv @asuinit @ascuheap @asulock @asucorrupt @acucorrupt @acsmem @acsfd @aculock -@c getenv LC_ALL and LANG @mtsenv +@c _nl_find_locale @asuinit @ascuheap @asulock @asucorrupt @acucorrupt @acsmem @acsfd @aculock @c _nl_load_locale_from_archive @ascuheap @acucorrupt @acsmem @acsfd @c sysconf _SC_PAGE_SIZE ok @c _nl_normalize_codeset @ascuheap @acsmem @@ -301,7 +299,6 @@ The symbols in this section are defined in the header file @file{locale.h}. @c (libc_once-initializes gconv_cache and gconv_path_envvar; they're @c never modified afterwards) @c __gconv_load_cache @ascuheap @acsmem @acsfd -@c getenv GCONV_PATH @mtsenv @c open_not_cancel @acsfd @c __fxstat64 ok @c close_not_cancel_no_status ok diff --git a/manual/macros.texi b/manual/macros.texi index 579da3fb81..584f26602f 100644 --- a/manual/macros.texi +++ b/manual/macros.texi @@ -125,10 +125,6 @@ const\comments\ @macro mtslocale {comments} locale\comments\ @end macro -@c Function accesses the assumed-constant environment. -@macro mtsenv {comments} -env\comments\ -@end macro @c Function accesses the assumed-constant hostid. @macro mtshostid {comments} hostid\comments\ diff --git a/manual/memory.texi b/manual/memory.texi index 3710d7ec66..c444f0f0e5 100644 --- a/manual/memory.texi +++ b/manual/memory.texi @@ -415,14 +415,12 @@ this function is in @file{stdlib.h}. @c fastbin_index ok @c fastbin ok @c catomic_compare_and_exhange_val_acq ok -@c malloc_printerr dup @mtsenv +@c malloc_printerr dup @c if we get to it, we're toast already, undefined behavior must have @c been invoked before -@c libc_message @mtsenv [no leaks with cancellation disabled] +@c libc_message [no leaks with cancellation disabled] @c FATAL_PREPARE ok @c pthread_setcancelstate disable ok -@c libc_secure_getenv @mtsenv -@c getenv @mtsenv @c open_not_cancel_2 dup @acsfd @c strchrnul ok @c WRITEV_FOR_FATAL ok @@ -1096,7 +1094,7 @@ systems that do not support @w{ISO C11}. @standards{BSD, stdlib.h} @safety{@prelim{}@mtunsafe{@mtuinit{}}@asunsafe{@asuinit{} @asulock{}}@acunsafe{@acuinit{} @aculock{} @acsfd{} @acsmem{}}} @c __libc_valloc @mtuinit @asuinit @asulock @aculock @acsfd @acsmem -@c ptmalloc_init (once) @mtsenv @asulock @aculock @acsfd @acsmem +@c ptmalloc_init (once) @asulock @aculock @acsfd @acsmem @c _dl_addr @asucorrupt? @aculock @c __rtld_lock_lock_recursive (dl_load_lock) @asucorrupt? @aculock @c _dl_find_dso_for_object ok, iterates over dl_ns and its _ns_loaded objs @@ -1105,7 +1103,6 @@ systems that do not support @w{ISO C11}. @c _dl_addr_inside_object ok @c determine_info ok @c __rtld_lock_unlock_recursive (dl_load_lock) @aculock -@c *_environ @mtsenv @c next_env_entry ok @c strcspn dup ok @c __libc_mallopt dup @mtasuconst:mallopt [setting mp_] @@ -1147,7 +1144,7 @@ interface, defined in @file{malloc.h}. @deftypefun int mallopt (int @var{param}, int @var{value}) @safety{@prelim{}@mtunsafe{@mtuinit{} @mtasuconst{:mallopt}}@asunsafe{@asuinit{} @asulock{}}@acunsafe{@acuinit{} @aculock{}}} @c __libc_mallopt @mtuinit @mtasuconst:mallopt @asuinit @asulock @aculock -@c ptmalloc_init (once) dup @mtsenv @asulock @aculock @acsfd @acsmem +@c ptmalloc_init (once) dup @asulock @aculock @acsfd @acsmem @c mutex_lock (main_arena->mutex) @asulock @aculock @c malloc_consolidate dup ok @c set_max_fast ok @@ -1456,7 +1453,7 @@ space's data segment). @c that collect the possibly inconsistent data. @c __libc_mallinfo2 @mtuinit @mtasuconst:mallopt @asuinit @asulock @aculock -@c ptmalloc_init (once) dup @mtsenv @asulock @aculock @acsfd @acsmem +@c ptmalloc_init (once) dup @asulock @aculock @acsfd @acsmem @c mutex_lock dup @asulock @aculock @c int_mallinfo @mtasuconst:mallopt [mp_ access on main_arena] @c malloc_consolidate dup ok @@ -1556,13 +1553,12 @@ penalties for the program if the debugging mode is not enabled. @deftypefun void mtrace (void) @standards{GNU, mcheck.h} -@safety{@prelim{}@mtunsafe{@mtsenv{} @mtasurace{:mtrace} @mtuinit{}}@asunsafe{@asuinit{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:mtrace} @mtuinit{}}@asunsafe{@asuinit{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acuinit{} @acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Like the mcheck hooks, these are not designed with thread safety in @c mind, because the hook pointers are temporarily modified without @c regard to other threads, signals or cancellation. -@c mtrace @mtuinit @mtasurace:mtrace @mtsenv @asuinit @ascuheap @asucorrupt @acuinit @acucorrupt @aculock @acsfd @acsmem -@c __libc_secure_getenv dup @mtsenv +@c mtrace @mtuinit @mtasurace:mtrace @asuinit @ascuheap @asucorrupt @acuinit @acucorrupt @aculock @acsfd @acsmem @c malloc dup @ascuheap @acsmem @c fopen dup @ascuheap @asulock @aculock @acsmem @acsfd @c fcntl dup ok diff --git a/manual/message.texi b/manual/message.texi index ef68693fd1..2cff798346 100644 --- a/manual/message.texi +++ b/manual/message.texi @@ -85,11 +85,10 @@ are defined/declared in the @file{nl_types.h} header file. @deftypefun nl_catd catopen (const char *@var{cat_name}, int @var{flag}) @standards{X/Open, nl_types.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} -@c catopen @mtsenv @ascuheap @acsmem +@safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} +@c catopen @ascuheap @acsmem @c strchr ok @c setlocale(,NULL) ok -@c getenv @mtsenv @c strlen ok @c alloca ok @c stpcpy ok @@ -831,7 +830,7 @@ not part of the C library they can be found in a separate library named @deftypefun {char *} gettext (const char *@var{msgid}) @standards{GNU, libintl.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Wrapper for dcgettext. The @code{gettext} function searches the currently selected message catalogs for a string which is equal to @var{msgid}. If there is such a @@ -879,7 +878,7 @@ information. @deftypefun {char *} dgettext (const char *@var{domainname}, const char *@var{msgid}) @standards{GNU, libintl.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Wrapper for dcgettext. The @code{dgettext} function acts just like the @code{gettext} function. It only takes an additional first argument @var{domainname} @@ -894,9 +893,9 @@ anachronism. The returned string must never be modified. @deftypefun {char *} dcgettext (const char *@var{domainname}, const char *@var{msgid}, int @var{category}) @standards{GNU, libintl.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c dcgettext @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem -@c dcigettext @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c dcgettext @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c dcigettext @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem @c libc_rwlock_rdlock @asulock @aculock @c current_locale_name ok [protected from @mtslocale] @c tfind ok @@ -911,16 +910,15 @@ anachronism. The returned string must never be modified. @c strchr ok @c stpcpy ok @c category_to_name ok -@c guess_category_value @mtsenv -@c getenv @mtsenv +@c guess_category_value @c current_locale_name dup ok [protected from @mtslocale by dcigettext] @c strcmp ok @c ENABLE_SECURE ok -@c _nl_find_domain @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c _nl_find_domain @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem @c libc_rwlock_rdlock dup @asulock @aculock @c _nl_make_l10nflist dup @ascuheap @acsmem @c libc_rwlock_unlock dup ok -@c _nl_load_domain @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c _nl_load_domain @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock_recursive @aculock @c libc_lock_unlock_recursive @aculock @c open->open_not_cancel_2 @acsfd @@ -937,7 +935,7 @@ anachronism. The returned string must never be modified. @c hash_string dup ok @c free dup @ascuheap @acsmem @c libc_rwlock_init ok -@c _nl_find_msg dup @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c _nl_find_msg dup @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem @c libc_rwlock_fini ok @c EXTRACT_PLURAL_EXPRESSION @ascuheap @acsmem @c strstr dup ok @@ -952,16 +950,15 @@ anachronism. The returned string must never be modified. @c _nl_explode_name dup @ascuheap @acsmem @c libc_rwlock_wrlock dup @asulock @aculock @c free dup @asulock @aculock @acsfd @acsmem -@c _nl_find_msg @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem -@c _nl_load_domain dup @mtsenv @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c _nl_find_msg @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem +@c _nl_load_domain dup @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsfd @acsmem @c strlen ok @c hash_string ok @c W ok @c SWAP ok @c bswap_32 ok @c strcmp ok -@c get_output_charset @mtsenv @ascuheap @acsmem -@c getenv dup @mtsenv +@c get_output_charset @ascuheap @acsmem @c strlen dup ok @c malloc dup @ascuheap @acsmem @c memcpy dup ok @@ -1272,7 +1269,7 @@ purpose. @deftypefun {char *} ngettext (const char *@var{msgid1}, const char *@var{msgid2}, unsigned long int @var{n}) @standards{GNU, libintl.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Wrapper for dcngettext. The @code{ngettext} function is similar to the @code{gettext} function as it finds the message catalogs in the same way. But it takes two @@ -1296,7 +1293,7 @@ Please note that the numeric value @var{n} has to be passed to the @deftypefun {char *} dngettext (const char *@var{domain}, const char *@var{msgid1}, const char *@var{msgid2}, unsigned long int @var{n}) @standards{GNU, libintl.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Wrapper for dcngettext. The @code{dngettext} is similar to the @code{dgettext} function in the way the message catalog is selected. The difference is that it takes @@ -1306,7 +1303,7 @@ parameters are handled in the same way @code{ngettext} handles them. @deftypefun {char *} dcngettext (const char *@var{domain}, const char *@var{msgid1}, const char *@var{msgid2}, unsigned long int @var{n}, int @var{category}) @standards{GNU, libintl.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Wrapper for dcigettext. The @code{dcngettext} is similar to the @code{dcgettext} function in the way the message catalog is selected. The difference is that it takes diff --git a/manual/pattern.texi b/manual/pattern.texi index 250fa1e265..9de0842c14 100644 --- a/manual/pattern.texi +++ b/manual/pattern.texi @@ -27,29 +27,25 @@ declared in @file{fnmatch.h}. @deftypefun int fnmatch (const char *@var{pattern}, const char *@var{string}, int @var{flags}) @standards{POSIX.2, fnmatch.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} -@c fnmatch @mtsenv @mtslocale @ascuheap @acsmem +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} +@c fnmatch @mtslocale @ascuheap @acsmem @c strnlen dup ok @c mbsrtowcs @c memset dup ok @c malloc dup @ascuheap @acsmem @c mbsinit dup ok @c free dup @ascuheap @acsmem -@c FCT = internal_fnwmatch @mtsenv @mtslocale @ascuheap @acsmem +@c FCT = internal_fnwmatch @mtslocale @ascuheap @acsmem @c FOLD @mtslocale @c towlower @mtslocale -@c EXT @mtsenv @mtslocale @ascuheap @acsmem +@c EXT @mtslocale @ascuheap @acsmem @c STRLEN = wcslen dup ok -@c getenv @mtsenv @c malloc dup @ascuheap @acsmem @c MEMPCPY = wmempcpy dup ok -@c FCT dup @mtsenv @mtslocale @ascuheap @acsmem +@c FCT dup @mtslocale @ascuheap @acsmem @c STRCAT = wcscat dup ok @c free dup @ascuheap @acsmem -@c END @mtsenv -@c getenv @mtsenv @c MEMCHR = wmemchr dup ok -@c getenv @mtsenv @c IS_CHAR_CLASS = is_char_class @mtslocale @c wctype @mtslocale @c BTOWC ok @@ -385,8 +381,8 @@ This is a GNU extension. @deftypefun int glob (const char *@var{pattern}, int @var{flags}, int (*@var{errfunc}) (const char *@var{filename}, int @var{error-code}), glob_t *@var{vector-ptr}) @standards{POSIX.2, glob.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtsenv{} @mtascusig{:ALRM} @mtascutimer{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c glob @mtasurace:utent @mtsenv @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c glob @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c strlen dup ok @c strchr dup ok @c malloc dup @ascuheap @acsmem @@ -396,7 +392,6 @@ This is a GNU extension. @c globfree dup @asucorrupt @ascuheap @acucorrupt @acsmem @c glob_pattern_p ok @c glob_pattern_type dup ok -@c getenv dup @mtsenv @c GET_LOGIN_NAME_MAX ok @c getlogin_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c GETPW_R_SIZE_MAX ok @@ -409,7 +404,7 @@ This is a GNU extension. @c S_ISDIR dup ok @c strdup dup @ascuheap @acsmem @c glob_pattern_type ok -@c glob_in_dir @mtsenv @mtslocale @asucorrupt @ascuheap @acucorrupt @acsfd @acsmem +@c glob_in_dir @mtslocale @asucorrupt @ascuheap @acucorrupt @acsfd @acsmem @c strlen dup ok @c glob_pattern_type dup ok @c malloc dup @ascuheap @acsmem @@ -425,7 +420,7 @@ This is a GNU extension. @c readdir64 ok [protected by exclusive use of the stream] @c REAL_DIR_ENTRY ok @c DIRENT_MIGHT_BE_DIR ok -@c fnmatch dup @mtsenv @mtslocale @ascuheap @acsmem +@c fnmatch dup @mtslocale @ascuheap @acsmem @c DIRENT_MIGHT_BE_SYMLINK ok @c link_exists_p ok @c link_exists2_p ok @@ -509,7 +504,7 @@ is encountered @code{glob} @emph{can} fail. @deftypefun int glob64 (const char *@var{pattern}, int @var{flags}, int (*@var{errfunc}) (const char *@var{filename}, int @var{error-code}), glob64_t *@var{vector-ptr}) @standards{GNU, glob.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtsenv{} @mtascusig{:ALRM} @mtascutimer{} @mtslocale{}}@asunsafe{@ascudlopen{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{} @mtslocale{}}@asunsafe{@ascudlopen{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c Same code as glob, but with glob64_t #defined as glob_t. The @code{glob64} function was added as part of the Large File Summit extensions but is not part of the original LFS proposal. The reason for @@ -1622,7 +1617,7 @@ the function @code{regerror} to turn it into an error message string. @deftypefun size_t regerror (int @var{errcode}, const regex_t *restrict @var{compiled}, char *restrict @var{buffer}, size_t @var{length}) @standards{POSIX.2, regex.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} @c regerror calls gettext, strcmp and mempcpy or memcpy. This function produces an error message string for the error code @var{errcode}, and stores the string in @var{length} bytes of memory @@ -1787,20 +1782,19 @@ the beginning of the vector. @deftypefun int wordexp (const char *@var{words}, wordexp_t *@var{word-vector-ptr}, int @var{flags}) @standards{POSIX.2, wordexp.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtasuconst{:@mtsenv{}} @mtsenv{} @mtascusig{:ALRM} @mtascutimer{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuintl{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c wordexp @mtasurace:utent @mtasuconst:@mtsenv @mtsenv @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asucorrupt @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuintl{} @ascuheap{} @asucorrupt{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c wordexp @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asucorrupt @asulock @acucorrupt @aculock @acsfd @acsmem @c w_newword ok @c wordfree dup @asucorrupt @ascuheap @acucorrupt @acsmem @c calloc dup @ascuheap @acsmem -@c getenv dup @mtsenv @c strcpy dup ok @c parse_backslash @ascuheap @acsmem @c w_addchar dup @ascuheap @acsmem -@c parse_dollars @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_dollars @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c w_addchar dup @ascuheap @acsmem -@c parse_arith @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_arith @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c w_newword dup ok -@c parse_dollars dup @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_dollars dup @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c parse_backtick dup @ascuplugin @ascuheap @aculock @acsfd @acsmem @c parse_qtd_backslash dup @ascuheap @acsmem @c eval_expr @mtslocale @@ -1842,7 +1836,7 @@ the beginning of the vector. @c free dup @ascuheap @acsmem @c kill dup ok @c free dup @ascuheap @acsmem -@c parse_param @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_param @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c reads from __libc_argc and __libc_argv without guards @c w_newword dup ok @c isalpha dup @mtslocale^^ @@ -1860,14 +1854,13 @@ the beginning of the vector. @c stpcpy dup ok @c w_addword dup @ascuheap @acsmem @c strdup dup @ascuheap @acsmem -@c getenv dup @mtsenv -@c parse_dollars dup @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c parse_tilde dup @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c fnmatch dup @mtsenv @mtslocale @ascuheap @acsmem +@c parse_dollars dup @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_tilde dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c fnmatch dup @mtslocale @ascuheap @acsmem @c mempcpy dup ok @c _ dup @ascuintl @c fxprintf dup @aculock -@c setenv dup @mtasuconst:@mtsenv @ascuheap @asulock @acucorrupt @aculock @acsmem +@c setenv dup @ascuheap @asulock @acucorrupt @aculock @acsmem @c strspn dup ok @c strcspn dup ok @c parse_backtick @ascuplugin @ascuheap @aculock @acsfd @acsmem @@ -1877,8 +1870,8 @@ the beginning of the vector. @c parse_qtd_backslash dup @ascuheap @acsmem @c parse_backslash dup @ascuheap @acsmem @c w_addchar dup @ascuheap @acsmem -@c parse_dquote @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c parse_dollars dup @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_dquote @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_dollars dup @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c parse_backtick dup @ascuplugin @ascuheap @aculock @acsfd @acsmem @c parse_qtd_backslash dup @ascuheap @acsmem @c w_addchar dup @ascuheap @acsmem @@ -1888,10 +1881,9 @@ the beginning of the vector. @c free dup @ascuheap @acsmem @c parse_squote dup @ascuheap @acsmem @c w_addchar dup @ascuheap @acsmem -@c parse_tilde @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_tilde @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c strchr dup ok @c w_addchar dup @ascuheap @acsmem -@c getenv dup @mtsenv @c w_addstr dup @ascuheap @acsmem @c strlen dup ok @c w_addmem dup @ascuheap @acsmem @@ -1901,17 +1893,17 @@ the beginning of the vector. @c getuid dup ok @c getpwuid_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c getpwnam_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c parse_glob @mtasurace:utent @mtasuconst:@mtsenv @mtsenv @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_glob @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c strchr dup ok -@c parse_dollars dup @mtasuconst:@mtsenv @mtslocale @mtsenv @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c parse_dollars dup @mtslocale @ascudlopen @ascuplugin @ascuintl @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c parse_qtd_backslash @ascuheap @acsmem @c w_addchar dup @ascuheap @acsmem @c parse_backslash dup @ascuheap @acsmem @c w_addchar dup @ascuheap @acsmem @c w_addword dup @ascuheap @acsmem @c w_newword dup ok -@c do_parse_glob @mtasurace:utent @mtsenv @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @aculock @acsfd @acsmem -@c glob dup @mtasurace:utent @mtsenv @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @aculock @acsfd @acsmem [auto glob_t avoids @asucorrupt @acucorrupt] +@c do_parse_glob @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @aculock @acsfd @acsmem +@c glob dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @aculock @acsfd @acsmem [auto glob_t avoids @asucorrupt @acucorrupt] @c w_addstr dup @ascuheap @acsmem @c w_addchar dup @ascuheap @acsmem @c globfree dup @ascuheap @acsmem [auto glob_t avoids @asucorrupt @acucorrupt] diff --git a/manual/process.texi b/manual/process.texi index 8254e5ee86..78e355899d 100644 --- a/manual/process.texi +++ b/manual/process.texi @@ -477,7 +477,7 @@ the @code{environ} variable. @deftypefun int execvp (const char *@var{filename}, char *const @var{argv}@t{[]}) @standards{POSIX.1, unistd.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} The @code{execvp} function is similar to @code{execv}, except that it searches the directories listed in the @code{PATH} environment variable (@pxref{Standard Environment}) to find the full file name of a @@ -490,7 +490,7 @@ to run the commands that users type. @deftypefun int execlp (const char *@var{filename}, const char *@var{arg0}, @dots{}) @standards{POSIX.1, unistd.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} +@safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} This function is like @code{execl}, except that it performs the same file name searching as the @code{execvp} function. @end deftypefun diff --git a/manual/socket.texi b/manual/socket.texi index 8708cbb07c..b46ea147e1 100644 --- a/manual/socket.texi +++ b/manual/socket.texi @@ -1287,19 +1287,18 @@ need to save it across calls. You can also use @code{getaddrinfo} and @deftypefun {struct hostent *} gethostbyname (const char *@var{name}) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:hostbyname} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} -@c gethostbyname @mtasurace:hostbyname @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@safety{@prelim{}@mtunsafe{@mtasurace{:hostbyname} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} +@c gethostbyname @mtasurace:hostbyname @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd @c libc_lock_lock dup @asulock @aculock @c malloc dup @ascuheap @acsmem -@c nss_hostname_digits_dots @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c res_maybe_init(!preinit) @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nss_hostname_digits_dots @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_maybe_init(!preinit) @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c res_iclose @acsuheap @acsmem @acsfd @c close_not_cancel_no_status dup @acsfd @c free dup @acsuheap @acsmem -@c res_vinit @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_vinit @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c res_randomid ok @c getpid dup ok -@c getenv dup @mtsenv @c strncpy dup ok @c fopen dup @ascuheap @asulock @acsmem @acsfd @aculock @c fsetlocking dup ok [no concurrent uses] @@ -1334,8 +1333,8 @@ need to save it across calls. You can also use @code{getaddrinfo} and @c gethostname dup ok @c strcpy dup ok @c rawmemchr dup ok -@c res_ninit @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c res_vinit dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_ninit @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_vinit dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c isdigit dup @mtslocale @c isxdigit dup @mtslocale @c strlen dup ok @@ -1347,7 +1346,7 @@ need to save it across calls. You can also use @code{getaddrinfo} and @c strcpy dup ok @c memcpy dup ok @c strchr dup ok -@c gethostbyname_r dup @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@c gethostbyname_r dup @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd @c realloc dup @ascuheap @acsmem @c free dup @ascuheap @acsmem @c libc_lock_unlock dup @aculock @@ -1358,12 +1357,12 @@ named @var{name}. If the lookup fails, it returns a null pointer. @deftypefun {struct hostent *} gethostbyname2 (const char *@var{name}, int @var{af}) @standards{IPv6 Basic API, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:hostbyname2} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} -@c gethostbyname2 @mtasurace:hostbyname2 @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@safety{@prelim{}@mtunsafe{@mtasurace{:hostbyname2} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} +@c gethostbyname2 @mtasurace:hostbyname2 @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd @c libc_lock_lock dup @asulock @aculock @c malloc dup @ascuheap @acsmem -@c nss_hostname_digits_dots dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c gethostbyname2_r dup @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@c nss_hostname_digits_dots dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c gethostbyname2_r dup @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd @c realloc dup @ascuheap @acsmem @c free dup @ascuheap @acsmem @c libc_lock_unlock dup @aculock @@ -1375,11 +1374,11 @@ allows the caller to specify the desired address family (e.g.@: @deftypefun {struct hostent *} gethostbyaddr (const void *@var{addr}, socklen_t @var{length}, int @var{format}) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:hostbyaddr} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} -@c gethostbyaddr @mtasurace:hostbyaddr @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@safety{@prelim{}@mtunsafe{@mtasurace{:hostbyaddr} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} +@c gethostbyaddr @mtasurace:hostbyaddr @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd @c libc_lock_lock dup @asulock @aculock @c malloc dup @ascuheap @acsmem -@c gethostbyaddr_r dup @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@c gethostbyaddr_r dup @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd @c realloc dup @ascuheap @acsmem @c free dup @ascuheap @acsmem @c libc_lock_unlock dup @aculock @@ -1431,12 +1430,11 @@ used in this context. @deftypefun int gethostbyname_r (const char *restrict @var{name}, struct hostent *restrict @var{result_buf}, char *restrict @var{buf}, size_t @var{buflen}, struct hostent **restrict @var{result}, int *restrict @var{h_errnop}) @standards{GNU, netdb.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} -@c gethostbyname_r @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd -@c nss_hostname_digits_dots dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c nscd_gethostbyname_r @mtsenv @ascuheap @acsfd @acsmem -@c nscd_gethst_r @mtsenv @ascuheap @acsfd @acsmem -@c getenv dup @mtsenv +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} +@c gethostbyname_r @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@c nss_hostname_digits_dots dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nscd_gethostbyname_r @ascuheap @acsfd @acsmem +@c nscd_gethst_r @ascuheap @acsfd @acsmem @c nscd_get_map_ref dup @ascuheap @acsfd @acsmem @c nscd_cache_search dup ok @c memcpy dup ok @@ -1446,11 +1444,10 @@ used in this context. @c close_not_cancel_no_status dup @acsfd @c nscd_drop_map_ref dup @ascuheap @acsmem @c nscd_unmap dup @ascuheap @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c res_hconf_init @mtsenv @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem [no @asuinit:reshconf @acuinit:reshconf, conditionally called] -@c res_hconf.c:do_init @mtsenv @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_hconf_init @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem [no @asuinit:reshconf @acuinit:reshconf, conditionally called] +@c res_hconf.c:do_init @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem @c memset dup ok -@c getenv dup @mtsenv @c fopen dup @ascuheap @asulock @acsmem @acsfd @aculock @c fsetlocking dup ok [no concurrent uses] @c fgets_unlocked dup ok [no concurrent uses] @@ -1555,13 +1552,13 @@ gethostname (char *host) @deftypefun int gethostbyname2_r (const char *@var{name}, int @var{af}, struct hostent *restrict @var{result_buf}, char *restrict @var{buf}, size_t @var{buflen}, struct hostent **restrict @var{result}, int *restrict @var{h_errnop}) @standards{GNU, netdb.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} -@c gethostbyname2_r @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd -@c nss_hostname_digits_dots dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c nscd_gethostbyname2_r @mtsenv @ascuheap @asulock @aculock @acsfd @acsmem -@c nscd_gethst_r dup @mtsenv @ascuheap @asulock @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c res_hconf_init dup @mtsenv @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem [no @asuinit:reshconf @acuinit:reshconf, conditionally called] +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} +@c gethostbyname2_r @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@c nss_hostname_digits_dots dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nscd_gethostbyname2_r @ascuheap @asulock @aculock @acsfd @acsmem +@c nscd_gethst_r dup @ascuheap @asulock @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_hconf_init dup @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem [no @asuinit:reshconf @acuinit:reshconf, conditionally called] @c nss_hosts_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *fct.l -> _nss_*_gethostbyname2_r @ascuplugin @c nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -1573,13 +1570,13 @@ allows the caller to specify the desired address family (e.g.@: @deftypefun int gethostbyaddr_r (const void *@var{addr}, socklen_t @var{length}, int @var{format}, struct hostent *restrict @var{result_buf}, char *restrict @var{buf}, size_t @var{buflen}, struct hostent **restrict @var{result}, int *restrict @var{h_errnop}) @standards{GNU, netdb.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} -@c gethostbyaddr_r @mtsenv @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} +@c gethostbyaddr_r @mtslocale @ascudlopen @ascuplugin @asucorrupt @ascuheap @asulock @aculock @acucorrupt @acsmem @acsfd @c memcmp dup ok -@c nscd_gethostbyaddr_r @mtsenv @ascuheap @asulock @aculock @acsfd @acsmem -@c nscd_gethst_r dup @mtsenv @ascuheap @asulock @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c res_hconf_init dup @mtsenv @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem [no @asuinit:reshconf @acuinit:reshconf, conditionally called] +@c nscd_gethostbyaddr_r @ascuheap @asulock @aculock @acsfd @acsmem +@c nscd_gethst_r dup @ascuheap @asulock @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_hconf_init dup @mtslocale @asucorrupt @ascuheap @aculock @acucorrupt @acsmem [no @asuinit:reshconf @acuinit:reshconf, conditionally called] @c nss_hosts_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *fct.l -> _nss_*_gethostbyaddr_r @ascuplugin @c nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -1609,11 +1606,11 @@ when using these functions because they are not reentrant. @deftypefun void sethostent (int @var{stayopen}) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:hostent} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c sethostent @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:hostent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c sethostent @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock dup @asulock @aculock -@c nss_setent(nss_hosts_lookup2) @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nss_setent(nss_hosts_lookup2) @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c set_h_errno dup ok @c setup(nss_hosts_lookup2) @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *lookup_fct = nss_hosts_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -1635,20 +1632,20 @@ reopening the database for each call. @deftypefun {struct hostent *} gethostent (void) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:hostent} @mtasurace{:hostentbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c gethostent @mtasurace:hostent @mtasurace:hostentbuf @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:hostent} @mtasurace{:hostentbuf} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c gethostent @mtasurace:hostent @mtasurace:hostentbuf @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock dup @asulock @aculock -@c nss_getent(gethostent_r) @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c nss_getent(gethostent_r) @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c malloc dup @ascuheap @acsmem -@c *func = gethostent_r dup @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c *func = gethostent_r dup @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c realloc dup @ascuheap @acsmem @c free dup @ascuheap @acsmem @c libc_lock_unlock dup @aculock @c -@c gethostent_r @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c gethostent_r @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock dup @asulock @aculock -@c nss_getent_r(nss_hosts_lookup2) @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nss_getent_r(nss_hosts_lookup2) @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c setup(nss_hosts_lookup2) dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *fct.f @mtasurace:hostent @ascuplugin @c nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -1662,11 +1659,11 @@ returns a null pointer if there are no more entries. @deftypefun void endhostent (void) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:hostent} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c endhostent @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:hostent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c endhostent @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock @asulock @aculock -@c nss_endent(nss_hosts_lookup2) @mtasurace:hostent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nss_endent(nss_hosts_lookup2) @mtasurace:hostent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c setup(nss_passwd_lookup2) dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *fct.f @mtasurace:hostent @ascuplugin @c nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -3526,18 +3523,18 @@ copy the information if you need to save it. @deftypefun {struct netent *} getnetbyname (const char *@var{name}) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:netbyname} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c getnetbyname =~ getpwuid @mtasurace:netbyname @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:netbyname} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c getnetbyname =~ getpwuid @mtasurace:netbyname @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock dup @asulock @aculock @c malloc dup @ascuheap @acsmem -@c getnetbyname_r dup @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c getnetbyname_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c realloc dup @ascuheap @acsmem @c free dup @ascuheap @acsmem @c libc_lock_unlock dup @aculock @c -@c getnetbyname_r =~ getpwuid_r @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c getnetbyname_r =~ getpwuid_r @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c no nscd support -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c nss_networks_lookup2 =~ nss_passwd_lookup2 @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *fct.l -> _nss_*_getnetbyname_r @ascuplugin @c nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -3576,11 +3573,11 @@ functions because they are not reentrant. @deftypefun void setnetent (int @var{stayopen}) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:netent} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c setnetent @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:netent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c setnetent @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock dup @asulock @aculock -@c nss_setent(nss_networks_lookup2) @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nss_setent(nss_networks_lookup2) @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c setup(nss_networks_lookup2) @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *lookup_fct = nss_networks_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c nss_lookup dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -3598,20 +3595,20 @@ reopening the database for each call. @deftypefun {struct netent *} getnetent (void) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:netent} @mtasurace{:netentbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c getnetent @mtasurace:netent @mtasurace:netentbuf @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:netent} @mtasurace{:netentbuf} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c getnetent @mtasurace:netent @mtasurace:netentbuf @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock dup @asulock @aculock -@c nss_getent(getnetent_r) @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c nss_getent(getnetent_r) @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c malloc dup @ascuheap @acsmem -@c *func = getnetent_r dup @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c *func = getnetent_r dup @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c realloc dup @ascuheap @acsmem @c free dup @ascuheap @acsmem @c libc_lock_unlock dup @aculock @c -@c getnetent_r @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c getnetent_r @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock dup @asulock @aculock -@c nss_getent_r(nss_networks_lookup2) @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nss_getent_r(nss_networks_lookup2) @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c setup(nss_networks_lookup2) dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *fct.f @mtasurace:servent @ascuplugin @c nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @@ -3624,11 +3621,11 @@ returns a null pointer if there are no more entries. @deftypefun void endnetent (void) @standards{BSD, netdb.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:netent} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} -@c endnetent @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@safety{@prelim{}@mtunsafe{@mtasurace{:netent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}} +@c endnetent @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c libc_lock_lock @asulock @aculock -@c nss_endent(nss_networks_lookup2) @mtasurace:netent @mtsenv @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem -@c res_maybe_init(!preinit) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c nss_endent(nss_networks_lookup2) @mtasurace:netent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem +@c res_maybe_init(!preinit) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c setup(nss_networks_lookup2) dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem @c *fct.f @mtasurace:netent @ascuplugin @c nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem diff --git a/manual/startup.texi b/manual/startup.texi index 747beed4d9..8fd8754208 100644 --- a/manual/startup.texi +++ b/manual/startup.texi @@ -92,9 +92,14 @@ int main (int @var{argc}, char *@var{argv}[], char *@var{envp}[]) The first two arguments are just the same. The third argument @var{envp} gives the program's environment; it is the same as the value -of @code{environ}. @xref{Environment Variables}. POSIX.1 does not -allow this three-argument form, so to be portable it is best to write -@code{main} to take two arguments, and use the value of @code{environ}. +of @code{environ}. The @var{envp} array is subject to the same +restrictions as the @code{environ} variable. @xref{Environment +Variables}. + +POSIX.1 does not allow this three-argument form, so to be portable it is +best to write @code{main} to take two arguments, and use the value of +@code{environ}. + @menu * Argument Syntax:: By convention, options start with a hyphen. @@ -326,20 +331,31 @@ functions can be safely used in multi-threaded programs. @deftypefun {char *} getenv (const char *@var{name}) @standards{ISO, stdlib.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@assafe{}@acsafe{}} -@c Unguarded access to __environ. +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}} This function returns a string that is the value of the environment variable @var{name}. You must not modify this string. In some non-Unix systems not using @theglibc{}, it might be overwritten by subsequent calls to @code{getenv} (but not by any other library function). If the environment variable @var{name} is not defined, the value is a null pointer. + +In @theglibc{} implementation, if the variable was last set using the +@code{setenv} function, the string returned by @code{getenv} will not +change or be deallocated even if the environment is subsequently +modified using @code{setenv}, @code{putenv}, or @code{unsetenv}. If the +variable was set using @code{putenv} or with a direct @code{environ} +array update, @code{getenv} returns a pointer into the +application-supplied string, and the behavior depends on what the +application does with that string. + +While the @code{getenv} function is thread-safe, threads may not observe +environment variable updates in the same order as they happened on other +threads. @end deftypefun @deftypefun {char *} secure_getenv (const char *@var{name}) @standards{GNU, stdlib.h} -@safety{@prelim{}@mtsafe{@mtsenv{}}@assafe{}@acsafe{}} -@c Calls getenv unless secure mode is enabled. +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}} This function is similar to @code{getenv}, but it returns a null pointer if the environment is untrusted. This happens when the program file has SUID or SGID bits set. General-purpose libraries @@ -352,13 +368,13 @@ This function is a GNU extension. @deftypefun int putenv (char *@var{string}) @standards{SVID, stdlib.h} -@safety{@prelim{}@mtunsafe{@mtasuconst{:@mtsenv{}}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{}}} -@c putenv @mtasuconst:@mtsenv @ascuheap @asulock @acucorrupt @aculock @acsmem +@safety{@mtsafe{}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{}}} +@c putenv @ascuheap @asulock @acucorrupt @aculock @acsmem @c strchr dup ok @c strndup dup @ascuheap @acsmem -@c add_to_environ dup @mtasuconst:@mtsenv @ascuheap @asulock @acucorrupt @aculock @acsmem +@c add_to_environ dup @ascuheap @asulock @acucorrupt @aculock @acsmem @c free dup @ascuheap @acsmem -@c unsetenv dup @mtasuconst:@mtsenv @asulock @aculock +@c unsetenv dup @asulock @aculock The @code{putenv} function adds or removes definitions from the environment. If the @var{string} is of the form @samp{@var{name}=@var{value}}, the definition is added to the environment. Otherwise, the @var{string} is @@ -376,6 +392,12 @@ reflect automatically in the environment. This also requires that variable is removed from the environment. The same applies of course to dynamically allocated variables which are freed later. +Modification of a string installed into the environment using +@code{putenv} can impact the thread-safety and async-signal-safety of +@code{getenv}. Simple modifications of an environment variable, for +example single-byte changes to the value part (such as changing a +@samp{0} to a @samp{1}), do not impact safety. + This function is part of the extended Unix interface. You should define @var{_XOPEN_SOURCE} before including any header. @end deftypefun @@ -383,9 +405,9 @@ This function is part of the extended Unix interface. You should define @deftypefun int setenv (const char *@var{name}, const char *@var{value}, int @var{replace}) @standards{BSD, stdlib.h} -@safety{@prelim{}@mtunsafe{@mtasuconst{:@mtsenv{}}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{}}} -@c setenv @mtasuconst:@mtsenv @ascuheap @asulock @acucorrupt @aculock @acsmem -@c add_to_environ @mtasuconst:@mtsenv @ascuheap @asulock @acucorrupt @aculock @acsmem +@safety{@prelim{}@mtsafe{}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{}}} +@c setenv @ascuheap @asulock @acucorrupt @aculock @acsmem +@c add_to_environ @ascuheap @asulock @acucorrupt @aculock @acsmem @c strlen dup ok @c libc_lock_lock @asulock @aculock @c strncmp dup ok @@ -423,8 +445,8 @@ the Unix standard. @deftypefun int unsetenv (const char *@var{name}) @standards{BSD, stdlib.h} -@safety{@prelim{}@mtunsafe{@mtasuconst{:@mtsenv{}}}@asunsafe{@asulock{}}@acunsafe{@aculock{}}} -@c unsetenv @mtasuconst:@mtsenv @asulock @aculock +@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}} +@c unsetenv @asulock @aculock @c strchr dup ok @c strlen dup ok @c libc_lock_lock @asulock @aculock @@ -440,6 +462,15 @@ The function returns @code{-1} if @var{name} is a null pointer, points to an empty string, or points to a string containing a @code{=} character. It returns @code{0} if the call succeeded. +In @theglibc{} implementation, this function does not actually +deallocate the storage for the environment variable. This ensures that +@code{getenv} is thread-safe and the returned strings can be used +without restriction (unless @code{putenv} was used to set these +variables). If storage for changed environment variables needs to be +reclaimed, applications must use @code{putenv} to set environment +variables and manage storage reclamation themselves. Storage for +variables set using @code{setenv} cannot be reclaimed. + This function was originally part of the BSD library but is now part of the Unix standard. The BSD version had no return value, though. @end deftypefun @@ -452,11 +483,7 @@ to enable writing standard compliant Fortran environments. @deftypefun int clearenv (void) @standards{GNU, stdlib.h} -@safety{@prelim{}@mtunsafe{@mtasuconst{:@mtsenv{}}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{}}} -@c clearenv @mtasuconst:@mtsenv @ascuheap @asulock @aculock @acsmem -@c libc_lock_lock @asulock @aculock -@c free dup @ascuheap @acsmem -@c libc_lock_unlock @aculock +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}} The @code{clearenv} function removes all entries from the environment. Using @code{putenv} and @code{setenv} new entries can be added again later. @@ -479,10 +506,61 @@ strings appear in the environment is not significant, but the same @var{name} must not appear more than once. The last element of the array is a null pointer. -This variable is declared in the header file @file{unistd.h}. +The @code{environ} variable is defined by POSIX, but the declaration in +@file{unistd.h} is a GNU extension. If you just want to get the value of an environment variable, use @code{getenv}. + +Direct writes to @code{environ} can impact the thread safety and +async-signal-safety of functionality that uses the process environment. +It is recommended to update @code{environ} using the functions +@code{setenv}, @code{putenv}, @code{unsetenv}, @code{clearenv} only, as +described above. + +The @code{environ} array can be passed to functions in the @code{execve} +and @code{posix_spawn} families. @xref{Executing a File}. + +As a GNU extension, if @code{environ} is used with these execution +functions, @theglibc{} ensures that a copy of the environment variables +is passed to the subprocess, without data races, even if the environment +is concurrently modified using the environment-updating functions +describe above. + +@strong{Note:} If the @code{environ} pointer is passed to +@code{execve}-style functions in multi-threaded programs, these +functions may need additional stack space to store a shallow copy of the +@code{environ} array. This does not apply to single-threaded programs +or the functions in @code{posix_spawn} family. + +In this context, @emph{concurrently modified} means that the invocations +of the environment-modifying functions are not in a happens-before +relationship with the @code{execve} or @code{posix_spawn} calls. The +copy of the @code{environ} array passed to the new process has the +following properties: + +@itemize @bullet +@item +If an environment variable is not modified or unset by the concurrent +calls, it retains its value. + +@item +If a variable is unset by concurrent calls, it is unspecified whether it +is present in the copy. + +@item +If a variable is set by concurrent calls, it is unspecified whether it +retains its original value (if any), or one of the set values. + +@item +Variables are not duplicated if the original array did not contain any +duplicates. + +@item +Even if the variable modifications obey a happens-before relationship +among themselves, the copy may not correspond to any intermediate state +consistent with that happens-before relationship. +@end itemize @end deftypevar Unix systems, and @gnusystems{}, pass the initial value of diff --git a/manual/sysinfo.texi b/manual/sysinfo.texi index 1ca77adc7e..2b1be40cbe 100644 --- a/manual/sysinfo.texi +++ b/manual/sysinfo.texi @@ -177,7 +177,7 @@ The specifics of this function are analogous to @code{sethostname}, above. @deftypefun {long int} gethostid (void) @standards{BSD, unistd.h} -@safety{@prelim{}@mtsafe{@mtshostid{} @mtsenv{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} +@safety{@prelim{}@mtsafe{@mtshostid{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @asucorrupt{} @ascuheap{} @asulock{}}@acunsafe{@aculock{} @acucorrupt{} @acsmem{} @acsfd{}}} @c On HURD, calls _hurd_get_host_config and strtol. On Linux, open @c HOSTIDFILE, reads an int32_t and closes; if that fails, it calls @c gethostname and gethostbyname_r to use the h_addr. diff --git a/manual/syslog.texi b/manual/syslog.texi index 02f84d6e6f..0e71fc0677 100644 --- a/manual/syslog.texi +++ b/manual/syslog.texi @@ -285,11 +285,11 @@ The symbols referred to in this section are declared in the file @c syslog() is implemented as a call to vsyslog(). @deftypefun void syslog (int @var{facility_priority}, const char *@var{format}, @dots{}) @standards{BSD, syslog.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} -@c syslog @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} +@c syslog @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd @c va_start dup ok -@c vsyslog_chk @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd -@c syslog(INTERNALLOG) dup @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c vsyslog_chk @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c syslog(INTERNALLOG) dup @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd @c open_memstream @ascuheap @acsmem @c stpcpy dup ok @c getpid dup ok @@ -297,8 +297,8 @@ The symbols referred to in this section are declared in the file @c fsetlocking [no @mtasurace:stream @asulock for exclusive stream] @c fprintf @mtslocale @ascuheap @acsmem [no @asucorrupt @aculock @acucorrupt on temp memstream] @c time dup ok -@c localtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c strftime_l(C) dup @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c localtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c strftime_l(C) dup @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd @c ftell dup ok [no @asucorrupt @aculock @acucorrupt on temp memstream] @c fputs_unlocked dup ok [no @mtasurace:stream @asucorrupt @acucorrupt on temp memstream] @c putc_unlocked dup ok [no @mtasurace:stream @asucorrupt @acucorrupt on temp memstream] @@ -444,9 +444,9 @@ syslog (LOG_MAKEPRI(LOG_LOCAL1, LOG_ERROR), @deftypefun void vsyslog (int @var{facility_priority}, const char *@var{format}, va_list @var{arglist}) @standards{BSD, syslog.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} -@c vsyslog @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd -@c vsyslog_chk dup @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} +@c vsyslog @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c vsyslog_chk dup @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd This is functionally identical to @code{syslog}, with the BSD style variable length argument. diff --git a/manual/time.texi b/manual/time.texi index 6b1080db06..03b400646d 100644 --- a/manual/time.texi +++ b/manual/time.texi @@ -1100,10 +1100,10 @@ has a geographical format. @xref{Time Zone State}. @deftypefun {struct tm *} localtime (const time_t *@var{time}) @standards{ISO, time.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:tmbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@safety{@prelim{}@mtunsafe{@mtasurace{:tmbuf} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} @c Calls tz_convert with a static buffer. -@c localtime @mtasurace:tmbuf @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c tz_convert dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c localtime @mtasurace:tmbuf @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tz_convert dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd The @code{localtime} function converts the simple time pointed to by @var{time} to broken-down time representation, expressed relative to the user's specified time zone. @@ -1130,24 +1130,22 @@ all threads. A variant function avoids this problem. @deftypefun {struct tm *} localtime_r (const time_t *@var{time}, struct tm *@var{resultp}) @standards{POSIX.1c, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c localtime_r @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c tz_convert(use_localtime) @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c localtime_r @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tz_convert(use_localtime) @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c libc_lock_lock dup @asulock @aculock -@c tzset_internal @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset_internal @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c always called with tzset_lock held @c sets static is_initialized before initialization; @c reads and sets old_tz; sets tz_rules. @c some of the issues only apply on the first call. @c subsequent calls only trigger these when called by localtime; @c otherwise, they're ok. -@c getenv dup @mtsenv @c strcmp dup ok @c strdup @ascuheap -@c tzfile_read @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzfile_read @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c memcmp dup ok @c strstr dup ok -@c getenv dup @mtsenv @c asprintf dup @mtslocale @ascuheap @acsmem @c stat64 dup ok @c fopen dup @ascuheap @asulock @acsmem @acsfd @aculock @@ -1176,9 +1174,8 @@ all threads. A variant function avoids this problem. @c double-underscore-prefixed name, this interface violation could @c be regarded as undefined behavior. @c strlen ok -@c tzset_parse_tz @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset_parse_tz @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c sscanf dup @mtslocale @ascuheap @acsmem -@c isalnum dup @mtsenv @c tzstring @ascuheap @acsmem @c reads and changes tzstring_list without synchronization, but @c only called with tzset_lock held (save for interface violations) @@ -1187,20 +1184,20 @@ all threads. A variant function avoids this problem. @c strcpy dup ok @c isdigit dup @mtslocale @c compute_offset ok -@c tzfile_default @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzfile_default @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c sets tzname, timezone, types, zone_names, rule_*off, etc; no guards @c strlen dup ok -@c tzfile_read dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzfile_read dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c mempcpy dup ok @c compute_tzname_max ok [if guarded by tzset_lock] @c iterates over zone_names; no guards @c free dup @ascuheap @acsmem @c strtoul dup @mtslocale @c update_vars dup ok -@c tzfile_compute(use_localtime) @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzfile_compute(use_localtime) @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c sets tzname; no guards. with !use_localtime, as in gmtime, it's ok @c tzstring dup @acsuheap @acsmem -@c tzset_parse_tz dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset_parse_tz dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c offtime dup ok @c tz_compute dup ok @c strcmp dup ok @@ -1227,9 +1224,9 @@ object the result was written into, i.e., it returns @var{resultp}. @deftypefun {struct tm *} gmtime (const time_t *@var{time}) @standards{ISO, time.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:tmbuf} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c gmtime @mtasurace:tmbuf @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c tz_convert dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtunsafe{@mtasurace{:tmbuf} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c gmtime @mtasurace:tmbuf @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tz_convert dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd This function is similar to @code{localtime}, except that the broken-down time is expressed as UTC rather than relative to a local time zone. The broken-down time's @code{tm_gmtoff} is 0, and its @@ -1243,15 +1240,15 @@ is placed in a static variable. A thread-safe replacement is also provided for @deftypefun {struct tm *} gmtime_r (const time_t *@var{time}, struct tm *@var{resultp}) @standards{POSIX.1c, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} @c You'd think tz_convert could avoid some safety issues with @c !use_localtime, but no such luck: tzset_internal will always bring @c about all possible AS and AC problems when it's first called. @c Calling any of localtime,gmtime_r once would run the initialization @c and avoid the heap, mem and fd issues in gmtime* in subsequent calls, @c but the unsafe locking would remain. -@c gmtime_r @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c tz_convert(gmtime_r) dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c gmtime_r @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tz_convert(gmtime_r) dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd This function is similar to @code{localtime_r}, except that it converts just like @code{gmtime} the given time as UTC. @@ -1262,8 +1259,8 @@ object the result was written into, i.e., it returns @var{resultp}. @deftypefun time_t mktime (struct tm *@var{brokentime}) @standards{ISO, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c mktime @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c mktime @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c passes a static localtime_offset to mktime_internal; it is read @c once, used as an initial guess, and updated at the end, but not @c used except as a guess for subsequent calls, so it should be safe. @@ -1273,11 +1270,11 @@ object the result was written into, i.e., it returns @var{resultp}. @c call to an external function, and a conditional change of the @c variable before the external call, so refraining from allocating a @c local variable at the first load would be a very bad optimization. -@c tzset dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c mktime_internal(localtime_r) @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c mktime_internal(localtime_r) @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c ydhms_diff ok -@c ranged_convert(localtime_r) @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c *convert = localtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c ranged_convert(localtime_r) @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c *convert = localtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c time_t_avg dup ok @c guess_time_tm dup ok @c ydhms_diff dup ok @@ -1311,7 +1308,7 @@ members. @xref{Time Zone State}. @deftypefun time_t timelocal (struct tm *@var{brokentime}) @standards{???, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} @c Alias to mktime. @code{timelocal} is functionally identical to @code{mktime}, but more @@ -1325,13 +1322,13 @@ available. @code{timelocal} is rather rare. @deftypefun time_t timegm (struct tm *@var{brokentime}) @standards{ISO, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c timegm @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c timegm @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c gmtime_offset triggers the same caveats as localtime_offset in mktime. @c although gmtime_r, as called by mktime, might save some issues, @c tzset calls tzset_internal with always, which forces @c reinitialization, so all issues may arise. -@c tzset dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c mktime_internal(gmtime_r) @asulock @aculock @c ..gmtime_r @asulock @aculock @c ... dup ok @@ -1364,10 +1361,10 @@ strings. These functions are declared in the header file @file{time.h}. @deftypefun size_t strftime (char *@var{s}, size_t @var{size}, const char *@var{template}, const struct tm *@var{brokentime}) @standards{ISO, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} -@c strftime @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd -@c strftime_l @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd -@c strftime_internal @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} +@c strftime @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c strftime_l @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c strftime_internal @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd @c add ok @c memset_zero dup ok @c memset_space dup ok @@ -1387,7 +1384,7 @@ strings. These functions are declared in the header file @file{time.h}. @c ISDIGIT ok @c STRLEN ok @c strlen dup ok -@c strftime_internal dup @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c strftime_internal dup @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd @c TOUPPER dup ok @c nl_get_era_entry @ascuheap @asulock @acsmem @aculock @c nl_init_era_entries @ascuheap @asulock @acsmem @aculock @@ -1414,12 +1411,12 @@ strings. These functions are declared in the header file @file{time.h}. @c memset dup ok @c memset_zero ok @c memset dup ok -@c mktime dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c mktime dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c iso_week_days ok @c isleap ok -@c tzset dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c localtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c gmtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c localtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c gmtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c tm_diff ok This function is similar to the @code{sprintf} function (@pxref{Formatted Input}), but the conversion specifications that can appear in the format @@ -1750,7 +1747,7 @@ For an example of @code{strftime}, see @ref{Time Functions Example}. @deftypefun size_t strftime_l (char *restrict @var{s}, size_t @var{size}, const char *restrict @var{template}, const struct tm *@var{brokentime}, locale_t @var{locale}) @standards{POSIX.1, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} The @code{strftime_l} function is equivalent to the @code{strftime} function except that it operates in @var{locale} rather than in the current locale. @@ -1758,10 +1755,10 @@ the current locale. @deftypefun size_t wcsftime (wchar_t *@var{s}, size_t @var{size}, const wchar_t *@var{template}, const struct tm *@var{brokentime}) @standards{ISO, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} -@c wcsftime @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd -@c wcsftime_l @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd -@c wcsftime_internal @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@asucorrupt{} @ascuheap{} @asulock{} @ascudlopen{}}@acunsafe{@acucorrupt{} @aculock{} @acsmem{} @acsfd{}}} +@c wcsftime @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c wcsftime_l @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c wcsftime_internal @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd @c add ok @c memset_zero dup ok @c memset_space dup ok @@ -1782,7 +1779,7 @@ the current locale. @c ISDIGIT ok @c STRLEN ok @c wcslen dup ok -@c wcsftime_internal dup @mtsenv @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd +@c wcsftime_internal dup @mtslocale @asucorrupt @ascuheap @asulock @ascudlopen @acucorrupt @aculock @acsmem @acsfd @c TOUPPER dup ok @c nl_get_era_entry dup @ascuheap @asulock @acsmem @aculock @c DO_NUMBER ok @@ -1798,12 +1795,12 @@ the current locale. @c wmemset dup ok @c memset_zero ok @c wmemset dup ok -@c mktime dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c mktime dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c iso_week_days ok @c isleap ok -@c tzset dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c localtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c gmtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c localtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c gmtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c tm_diff ok The @code{wcsftime} function is equivalent to the @code{strftime} function with the difference that it operates on wide character @@ -1886,9 +1883,9 @@ Programs should instead use @code{strftime} or even @code{sprintf}. @deftypefun {Deprecated function} {char *} ctime (const time_t *@var{time}) @standards{ISO, time.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:tmbuf} @mtasurace{:asctime} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c ctime @mtasurace:tmbuf @mtasurace:asctime @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c localtime dup @mtasurace:tmbuf @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtunsafe{@mtasurace{:tmbuf} @mtasurace{:asctime} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c ctime @mtasurace:tmbuf @mtasurace:asctime @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c localtime dup @mtasurace:tmbuf @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c asctime dup @mtasurace:asctime @mtslocale The @code{ctime} function is similar to @code{asctime}, except that you specify the calendar time argument as a @code{time_t} simple time value @@ -1911,9 +1908,9 @@ Programs should instead use @code{strftime} or even @code{sprintf}. @deftypefun {Deprecated function} {char *} ctime_r (const time_t *@var{time}, char *@var{buffer}) @standards{???, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c ctime_r @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c localtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c ctime_r @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c localtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c asctime_r dup @mtslocale This function is similar to @code{ctime}, but places the result in the string pointed to by @var{buffer}, and the time zone state is not @@ -1962,9 +1959,9 @@ which is defined and implemented in terms of calls to @code{strptime}. @deftypefun {char *} strptime (const char *@var{s}, const char *@var{fmt}, struct tm *@var{tp}) @standards{XPG4, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c strptime @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c strptime_internal @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c strptime @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c strptime_internal @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c memset dup ok @c ISSPACE ok @c isspace_l dup ok @@ -1973,11 +1970,11 @@ which is defined and implemented in terms of calls to @code{strptime}. @c strlen dup ok @c strncasecmp_l dup ok @c strcmp dup ok -@c recursive @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c strptime_internal dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c recursive @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c strptime_internal dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c get_number ok @c ISSPACE dup ok -@c localtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c localtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c nl_select_era_entry @ascuheap @asulock @acsmem @aculock @c nl_init_era_entries dup @ascuheap @asulock @acsmem @aculock @c get_alt_number dup @ascuheap @asulock @acsmem @aculock @@ -2406,9 +2403,9 @@ in a @code{time_t} variable. @deftypefun {struct tm *} getdate (const char *@var{string}) @standards{Unix98, time.h} -@safety{@prelim{}@mtunsafe{@mtasurace{:getdate} @mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c getdate @mtasurace:getdate @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c getdate_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtunsafe{@mtasurace{:getdate} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c getdate @mtasurace:getdate @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c getdate_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd The interface to @code{getdate} is the simplest possible for a function to parse a string and return the value. @var{string} is the input string and the result is returned in a statically-allocated variable. @@ -2519,9 +2516,8 @@ any arbitrary file and chances are high that with some bogus input @deftypefun int getdate_r (const char *@var{string}, struct tm *@var{tp}) @standards{GNU, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c getdate_r @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c getenv dup @mtsenv +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c getdate_r @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c stat64 dup ok @c access dup ok @c fopen dup @ascuheap @asulock @acsmem @acsfd @aculock @@ -2532,17 +2528,17 @@ any arbitrary file and chances are high that with some bogus input @c fclose dup @ascuheap @asulock @aculock @acsmem @acsfd @c memcpy dup ok @c getline dup @ascuheap @acsmem [no @asucorrupt @aculock @acucorrupt, exclusive] -@c strptime dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c strptime dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c feof_unlocked dup ok @c free dup @ascuheap @acsmem @c ferror_unlocked dup dup ok @c time dup ok -@c localtime_r dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd -@c first_wday @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c localtime_r dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c first_wday @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c memset dup ok -@c mktime dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c mktime dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c check_mday ok -@c mktime dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c mktime dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd The @code{getdate_r} function is the reentrant counterpart of @code{getdate}. It does not use the global variable @code{getdate_err} to signal an error, but instead returns an error code. The same error @@ -2815,10 +2811,10 @@ Programs should instead use the @code{tm_gmtoff} and @deftypefun void tzset (void) @standards{POSIX.1, time.h} -@safety{@prelim{}@mtsafe{@mtsenv{} @mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} -@c tzset @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}} +@c tzset @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c libc_lock_lock dup @asulock @aculock -@c tzset_internal dup @mtsenv @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd +@c tzset_internal dup @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd @c libc_lock_unlock dup @aculock The @code{tzset} function initializes the state variables from the value of the @env{TZ} environment variable.