This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 9fa99caa5810d599e434702e83ffb3a9f073edb1 Author: Marco Casaroli <[email protected]> AuthorDate: Tue Oct 13 06:42:53 2026 +0200 libs/libc/environ: Keep the strings that libc allocated until exit. environ is an lvalue, so a program can assign it, as POSIX allows, and change its entries. Both happen: gnulib's putenv() replaces an entry in place, and GNU make installs its own arrays. libc then freed a string that the program put there, or an array on the program's stack. For example, setenv() after an in-place replacement made free() assert. libc now frees no environment string before the task exits. Each block that env_alloc() gives for the environment goes on a list of the task, and env_release() frees the list. glibc also keeps these strings, so a pointer that getenv() returned stays valid after setenv() or unsetenv(). env_alloc.c chooses the heap of the task and keeps the block header private. Before a change, env_reserve() makes environ an array that libc allocated. If the program assigned its own array, libc copies it and does not change it. The array of libc that it replaced stays until exit, because the program can assign it again. env_dup() allocates the array and all of the strings in two blocks. putenv() passes the name part of its string to env_setvar(), a setenv() that takes the length of the name, so it needs no copy. The counts of the environment are size_t. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- include/nuttx/tls.h | 10 +- libs/libc/environ/CMakeLists.txt | 3 +- libs/libc/environ/Make.defs | 1 + libs/libc/environ/env_alloc.c | 206 +++++++++++++++++++++++++++++++++++++ libs/libc/environ/env_clearenv.c | 17 ++- libs/libc/environ/env_dup.c | 74 +++++++------ libs/libc/environ/env_findvar.c | 29 ++---- libs/libc/environ/env_foreach.c | 2 - libs/libc/environ/env_getenv.c | 2 +- libs/libc/environ/env_putenv.c | 39 +++---- libs/libc/environ/env_release.c | 24 ++--- libs/libc/environ/env_removevar.c | 44 ++------ libs/libc/environ/env_secureexec.c | 21 +++- libs/libc/environ/env_setenv.c | 187 +++++++++++++++------------------ libs/libc/environ/env_unsetenv.c | 16 ++- libs/libc/environ/environ.h | 113 ++++++++++++++------ 16 files changed, 504 insertions(+), 284 deletions(-) diff --git a/include/nuttx/tls.h b/include/nuttx/tls.h index 2e802dd05cf..bd103a73dca 100644 --- a/include/nuttx/tls.h +++ b/include/nuttx/tls.h @@ -165,9 +165,13 @@ struct task_info_s #ifndef CONFIG_DISABLE_ENVIRON /* Environment variables **************************************************/ - FAR char **ta_envp; /* Allocated environment strings */ - ssize_t ta_envpc; /* Maximum entries of environment array */ - ssize_t ta_envc; /* Number of environment strings */ + FAR char **ta_envp; /* The array that environ names */ + FAR char **ta_envarr; /* The array that libc allocated */ + FAR void *ta_envmem; /* Blocks that libc allocated, freed at + * exit + */ + size_t ta_envpc; /* Maximum entries of environment array */ + size_t ta_envc; /* Number of environment strings */ #endif }; diff --git a/libs/libc/environ/CMakeLists.txt b/libs/libc/environ/CMakeLists.txt index a721bcfb091..316c854b9f8 100644 --- a/libs/libc/environ/CMakeLists.txt +++ b/libs/libc/environ/CMakeLists.txt @@ -33,6 +33,7 @@ if(NOT CONFIG_DISABLE_ENVIRON) env_setenv.c env_unsetenv.c env_foreach.c - env_secureexec.c) + env_secureexec.c + env_alloc.c) target_sources(c PRIVATE ${SRCS}) endif() diff --git a/libs/libc/environ/Make.defs b/libs/libc/environ/Make.defs index 3c74a82f5f7..37e9fcb7ec3 100644 --- a/libs/libc/environ/Make.defs +++ b/libs/libc/environ/Make.defs @@ -25,6 +25,7 @@ ifneq ($(CONFIG_DISABLE_ENVIRON),y) CSRCS += env_getenvironptr.c env_dup.c env_release.c env_findvar.c CSRCS += env_removevar.c env_clearenv.c env_getenv.c env_putenv.c CSRCS += env_setenv.c env_unsetenv.c env_foreach.c env_secureexec.c +CSRCS += env_alloc.c # Include environ build support diff --git a/libs/libc/environ/env_alloc.c b/libs/libc/environ/env_alloc.c new file mode 100644 index 00000000000..fb4a7b97213 --- /dev/null +++ b/libs/libc/environ/env_alloc.c @@ -0,0 +1,206 @@ +/**************************************************************************** + * libs/libc/environ/env_alloc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include <nuttx/tls.h> + +#ifdef __KERNEL__ +# include <nuttx/kmalloc.h> +#endif + +#include "environ/environ.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Each block that env_alloc() gives starts with this header. The list at + * ta_envmem and flink hold the address after the header, the address that + * env_alloc() returned. + */ + +struct env_block_s +{ + FAR void *flink; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: env_heap_alloc + * + * Description: + * The environment is in the heap that holds the task information: the + * kernel heap for a kernel thread, the user heap for a user task. Kernel + * code also changes the environment of kernel threads (the idle task at + * boot) and of a new user task. + * + ****************************************************************************/ + +static FAR struct env_block_s *env_heap_alloc(FAR struct task_info_s *info, + size_t size) +{ +#ifdef __KERNEL__ + return kmm_heapmember(info) ? kmm_malloc(size) : kumm_malloc(size); +#else + UNUSED(info); + return lib_umalloc(size); +#endif +} + +/**************************************************************************** + * Name: env_heap_free + ****************************************************************************/ + +static void env_heap_free(FAR struct task_info_s *info, + FAR struct env_block_s *block) +{ +#ifdef __KERNEL__ + if (kmm_heapmember(info)) + { + kmm_free(block); + } + else + { + kumm_free(block); + } +#else + UNUSED(info); + lib_ufree(block); +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: env_alloc + ****************************************************************************/ + +FAR void *env_alloc(FAR struct task_info_s *info, size_t size) +{ + FAR struct env_block_s *block; + + block = env_heap_alloc(info, sizeof(*block) + size); + if (block == NULL) + { + return NULL; + } + + block->flink = info->ta_envmem; + info->ta_envmem = block + 1; + return block + 1; +} + +/**************************************************************************** + * Name: env_free + ****************************************************************************/ + +void env_free(FAR struct task_info_s *info, FAR void *mem) +{ + FAR struct env_block_s *block = (FAR struct env_block_s *)mem - 1; + FAR void **prev; + + for (prev = &info->ta_envmem; + *prev != NULL; + prev = &((FAR struct env_block_s *)*prev - 1)->flink) + { + if (*prev == mem) + { + *prev = block->flink; + env_heap_free(info, block); + return; + } + } + + DEBUGPANIC(); +} + +/**************************************************************************** + * Name: env_reserve + ****************************************************************************/ + +int env_reserve(FAR struct task_info_s *info, size_t more) +{ + FAR char **envp = info->ta_envp; + FAR char **arr; + size_t envc = 0; + size_t envpc; + + /* The program can change the entries of environ in place, so count + * them again. + */ + + while (envp != NULL && envp[envc] != NULL) + { + envc++; + } + + info->ta_envc = envc; + + if (envp != NULL && envp == info->ta_envarr && + envc + more < info->ta_envpc) + { + return OK; + } + + envpc = envc + more + SCHED_ENVIRON_RESERVED + 1; + arr = env_alloc(info, sizeof(*arr) * envpc); + if (arr == NULL) + { + return -ENOMEM; + } + + if (envc > 0) + { + memcpy(arr, envp, sizeof(*arr) * envc); + } + + arr[envc] = NULL; + + /* A full array of libc is not used again. An array of libc that the + * program replaced stays until exit: the program can assign it to + * environ again. + */ + + if (envp != NULL && envp == info->ta_envarr) + { + env_free(info, envp); + } + + info->ta_envp = arr; + info->ta_envarr = arr; + info->ta_envpc = envpc; + return OK; +} diff --git a/libs/libc/environ/env_clearenv.c b/libs/libc/environ/env_clearenv.c index a87bb3f7f94..a37fabab739 100644 --- a/libs/libc/environ/env_clearenv.c +++ b/libs/libc/environ/env_clearenv.c @@ -27,6 +27,8 @@ #include <nuttx/environ.h> #include <nuttx/tls.h> +#include "environ/environ.h" + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -54,7 +56,20 @@ int clearenv(void) FAR struct task_info_s *info = task_get_info(); nxrmutex_lock(&info->ta_lock); - env_release(info); + + /* Free the array of libc if environ still names it. The strings stay + * until exit, as a pointer that getenv() returned can still be in use. + */ + + if (info->ta_envarr != NULL && info->ta_envp == info->ta_envarr) + { + env_free(info, info->ta_envarr); + } + + info->ta_envp = NULL; + info->ta_envarr = NULL; + info->ta_envpc = 0; + info->ta_envc = 0; nxrmutex_unlock(&info->ta_lock); return OK; } diff --git a/libs/libc/environ/env_dup.c b/libs/libc/environ/env_dup.c index be85a18f564..351f66134bc 100644 --- a/libs/libc/environ/env_dup.c +++ b/libs/libc/environ/env_dup.c @@ -59,9 +59,11 @@ int env_dup(FAR struct task_info_s *info, FAR char * const *envcp) { - FAR char **envp = NULL; + FAR char **envp; + FAR char *str; size_t envc = 0; - size_t size; + size_t size = 0; + size_t i; int ret = OK; DEBUGASSERT(info != NULL); @@ -70,22 +72,15 @@ int env_dup(FAR struct task_info_s *info, FAR char * const *envcp) if (envcp != NULL && info->ta_envp == NULL) { - /* Pre-emption must be disabled throughout the following because the - * environment may be shared. - */ - nxrmutex_lock(&info->ta_lock); - /* Count the strings */ + /* Count the strings and their size */ while (envcp[envc] != NULL) { - envc++; + size += strlen(envcp[envc++]) + 1; } - info->ta_envc = envc; - info->ta_envpc = (envc + SCHED_ENVIRON_RESERVED + 1); - /* A special case is that the parent has an "empty" environment * allocation, i.e., there is an allocation in place but it * contains no variable definitions and, hence, envc == 0. @@ -93,48 +88,49 @@ int env_dup(FAR struct task_info_s *info, FAR char * const *envcp) if (envc > 0) { - /* There is an environment, duplicate it */ - - envp = env_malloc(info, sizeof(*envp) * info->ta_envpc); - if (envp == NULL) + /* Duplicate the parent environment: one block for the array and + * one for all of the strings. + */ + + info->ta_envpc = envc + SCHED_ENVIRON_RESERVED + 1; + envp = env_alloc(info, sizeof(*envp) * info->ta_envpc); + str = env_alloc(info, size); + if (envp == NULL || str == NULL) { /* The parent's environment can not be inherited due to a * failure in the allocation of the child environment. */ + if (envp != NULL) + { + env_free(info, envp); + } + + if (str != NULL) + { + env_free(info, str); + } + + info->ta_envpc = 0; ret = -ENOMEM; } else { - envp[envc] = NULL; - - /* Duplicate the parent environment. */ - - while (envc-- > 0) + for (i = 0; i < envc; i++) { - size = strlen(envcp[envc]) + 1; - envp[envc] = env_malloc(info, size); - if (envp[envc] == NULL) - { - while (envp[++envc] != NULL) - { - env_free(info, envp[envc]); - } - - env_free(info, envp); - envp = NULL; - ret = -ENOMEM; - break; - } - - strlcpy(envp[envc], envcp[envc], size); + size = strlen(envcp[i]) + 1; + memcpy(str, envcp[i], size); + envp[i] = str; + str += size; } + + envp[envc] = NULL; + info->ta_envp = envp; + info->ta_envarr = envp; + info->ta_envc = envc; } } - /* Save the child environment allocation. */ - - info->ta_envp = envp; nxrmutex_unlock(&info->ta_lock); } diff --git a/libs/libc/environ/env_findvar.c b/libs/libc/environ/env_findvar.c index 38d6245efd5..4150204c2f3 100644 --- a/libs/libc/environ/env_findvar.c +++ b/libs/libc/environ/env_findvar.c @@ -26,6 +26,7 @@ #include <assert.h> #include <errno.h> +#include <string.h> #include <nuttx/tls.h> @@ -39,22 +40,10 @@ * Name: env_cmpname ****************************************************************************/ -static bool env_cmpname(const char *pszname, const char *peqname) +static bool env_cmpname(FAR const char *pname, size_t namelen, + FAR const char *peqname) { - /* Search until we find anything different in the two names */ - - for (; *pszname == *peqname; pszname++, peqname++) - { - } - - /* On success, pszname will end with '\0' and peqname with '=' */ - - if (*pszname == '\0' && *peqname == '=') - { - return true; - } - - return false; + return strncmp(pname, peqname, namelen) == 0 && peqname[namelen] == '='; } /**************************************************************************** @@ -69,8 +58,9 @@ static bool env_cmpname(const char *pszname, const char *peqname) * specified name. * * Input Parameters: - * group - The task group containing environment array to be searched. - * pname - The variable name to find + * info - The task containing environment array to be searched. + * pname - The variable name to find. It needs no terminating NUL. + * namelen - The length of the name. * * Returned Value: * A index to the name=value string in the environment @@ -81,7 +71,8 @@ static bool env_cmpname(const char *pszname, const char *peqname) * ****************************************************************************/ -ssize_t env_findvar(FAR struct task_info_s *info, FAR const char *pname) +ssize_t env_findvar(FAR struct task_info_s *info, FAR const char *pname, + size_t namelen) { ssize_t i; @@ -98,7 +89,7 @@ ssize_t env_findvar(FAR struct task_info_s *info, FAR const char *pname) for (i = 0; info->ta_envp[i] != NULL; i++) { - if (env_cmpname(pname, info->ta_envp[i])) + if (env_cmpname(pname, namelen, info->ta_envp[i])) { return i; } diff --git a/libs/libc/environ/env_foreach.c b/libs/libc/environ/env_foreach.c index b26eb812ced..b5af887e6ec 100644 --- a/libs/libc/environ/env_foreach.c +++ b/libs/libc/environ/env_foreach.c @@ -89,7 +89,5 @@ int env_foreach(FAR struct task_info_s *info, } } - DEBUGASSERT(ret != OK || info->ta_envc == i); - return ret; } diff --git a/libs/libc/environ/env_getenv.c b/libs/libc/environ/env_getenv.c index 486f18fc3d9..2e8b8bd4894 100644 --- a/libs/libc/environ/env_getenv.c +++ b/libs/libc/environ/env_getenv.c @@ -73,7 +73,7 @@ FAR char *getenv(FAR const char *name) } nxrmutex_lock(&info->ta_lock); - ret = env_findvar(info, name); + ret = env_findvar(info, name, strlen(name)); if (ret < 0) { nxrmutex_unlock(&info->ta_lock); diff --git a/libs/libc/environ/env_putenv.c b/libs/libc/environ/env_putenv.c index 5f0e29155af..579119bc7c4 100644 --- a/libs/libc/environ/env_putenv.c +++ b/libs/libc/environ/env_putenv.c @@ -27,7 +27,6 @@ #include <string.h> #include <errno.h> -#include <nuttx/lib/lib.h> #include <nuttx/tls.h> #include "environ.h" @@ -60,45 +59,33 @@ int putenv(FAR const char *string) { - FAR struct task_info_s *info = task_get_info(); - FAR char *pname; - FAR char *pequal; - size_t len; - int ret = OK; + FAR const char *pequal; + int ret; /* Verify that a string was passed */ if (!string) { - ret = EINVAL; - goto errout; + set_errno(EINVAL); + return ERROR; } /* Parse the name=value string */ - len = strlen(string) + 1; - pname = env_malloc(info, len); - if (!pname) + pequal = strchr(string, '='); + if (pequal == NULL) { - ret = ENOMEM; - goto errout; + return OK; } - memcpy(pname, string, len); + /* Then let env_setvar do all of the work */ - pequal = strchr(pname, '='); - if (pequal) + ret = env_setvar(string, pequal - string, pequal + 1, TRUE); + if (ret < 0) { - /* Then let setenv do all of the work */ - - *pequal = '\0'; - ret = setenv(pname, pequal + 1, TRUE); + set_errno(-ret); + return ERROR; } - env_free(info, pname); - return ret; - -errout: - set_errno(ret); - return ERROR; + return OK; } diff --git a/libs/libc/environ/env_release.c b/libs/libc/environ/env_release.c index 4c1abfcd49a..91fe4e8ce97 100644 --- a/libs/libc/environ/env_release.c +++ b/libs/libc/environ/env_release.c @@ -57,29 +57,21 @@ void env_release(FAR struct task_info_s *info) { - int i; - DEBUGASSERT(info != NULL); - if (info->ta_envp) - { - /* Free any allocate environment strings */ - - for (i = 0; info->ta_envp[i] != NULL; i++) - { - env_free(info, info->ta_envp[i]); - } + /* Free every block that libc allocated for the environment */ - /* Free the environment */ - - env_free(info, info->ta_envp); + while (info->ta_envmem != NULL) + { + env_free(info, info->ta_envmem); } /* In any event, make sure that all environment-related variables in the * task group structure are reset to initial values. */ - info->ta_envp = NULL; - info->ta_envpc = 0; - info->ta_envc = 0; + info->ta_envp = NULL; + info->ta_envarr = NULL; + info->ta_envpc = 0; + info->ta_envc = 0; } diff --git a/libs/libc/environ/env_removevar.c b/libs/libc/environ/env_removevar.c index 207e0468b01..e1b5c9f43f0 100644 --- a/libs/libc/environ/env_removevar.c +++ b/libs/libc/environ/env_removevar.c @@ -55,44 +55,16 @@ * ****************************************************************************/ -void env_removevar(FAR struct task_info_s *info, ssize_t index) +void env_removevar(FAR struct task_info_s *info, size_t index) { - DEBUGASSERT(info != NULL && index >= 0 && index < info->ta_envc); + DEBUGASSERT(info != NULL && index < info->ta_envc); + DEBUGASSERT(info->ta_envp == info->ta_envarr); - /* Free the allocate environment string */ - - env_free(info, info->ta_envp[index]); - - /* Exchange the last env and the index env */ + /* Move the last entry into its place. The string is not freed: the + * program can still use it. env_release() frees it at exit. + */ info->ta_envc--; - if (index == info->ta_envc) - { - info->ta_envp[index] = NULL; - } - else - { - info->ta_envp[index] = info->ta_envp[info->ta_envc]; - info->ta_envp[info->ta_envc] = NULL; - } - - /* Free the old environment (if there was one) */ - - if (info->ta_envc == 0) - { - env_free(info, info->ta_envp); - info->ta_envp = NULL; - info->ta_envpc = 0; - } - else if (info->ta_envc <= - (info->ta_envpc - SCHED_ENVIRON_RESERVED * 2)) - { - /* Reallocate the environment to reclaim a little memory */ - - info->ta_envpc = info->ta_envc + SCHED_ENVIRON_RESERVED + 1; - - info->ta_envp = env_realloc(info, info->ta_envp, - sizeof(*info->ta_envp) * info->ta_envpc); - DEBUGASSERT(info->ta_envp != NULL); - } + info->ta_envp[index] = info->ta_envp[info->ta_envc]; + info->ta_envp[info->ta_envc] = NULL; } diff --git a/libs/libc/environ/env_secureexec.c b/libs/libc/environ/env_secureexec.c index f3ae0fcc3ec..12454262c4b 100644 --- a/libs/libc/environ/env_secureexec.c +++ b/libs/libc/environ/env_secureexec.c @@ -101,19 +101,32 @@ static bool env_is_unsafe(FAR const char *pair) #ifdef CONFIG_SCHED_USER_IDENTITY void env_sanitize_secure(FAR struct task_info_s *info) { - ssize_t index; + size_t index; DEBUGASSERT(info != NULL); nxrmutex_lock(&info->ta_lock); - for (index = info->ta_envc - 1; index >= 0; ) + if (env_reserve(info, 0) < 0) { - FAR char *pair = info->ta_envp[index]; + /* Without an array of libc to change, drop the whole environment */ + + info->ta_envp = NULL; + info->ta_envc = 0; + } + + /* Walk down from the last entry. env_removevar() moves the last entry + * into the removed one, so the same index is checked again. + */ + + index = info->ta_envc; + while (index > 0) + { + FAR char *pair = info->ta_envp[index - 1]; if (pair != NULL && env_is_unsafe(pair)) { - env_removevar(info, index); + env_removevar(info, index - 1); } else { diff --git a/libs/libc/environ/env_setenv.c b/libs/libc/environ/env_setenv.c index 7bd5a1748db..edf4619587b 100644 --- a/libs/libc/environ/env_setenv.c +++ b/libs/libc/environ/env_setenv.c @@ -38,6 +38,82 @@ * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: env_setvar + ****************************************************************************/ + +int env_setvar(FAR const char *name, size_t namelen, + FAR const char *value, int overwrite) +{ + FAR struct task_info_s *info = task_get_info(); + FAR char *pvar; + ssize_t idx; + size_t varlen; + int ret; + + DEBUGASSERT(info != NULL); + + nxrmutex_lock(&info->ta_lock); + + /* environ must be an array of libc with room for one more entry */ + + ret = env_reserve(info, 1); + if (ret < 0) + { + goto out; + } + + /* Check if the variable already exists */ + + idx = env_findvar(info, name, namelen); + if (idx >= 0) + { + /* It does! Do we have permission to overwrite the existing value? */ + + if (!overwrite) + { + goto out; + } + + /* Yes.. just remove the name=value pair from the environment. It will + * be added again below. + */ + + env_removevar(info, idx); + } + + /* An empty value removes the variable */ + + if (*value == '\0') + { + goto out; + } + + /* Get the size of the new name=value string. + * The +2 is for the '=' and for null terminator + */ + + varlen = namelen + strlen(value) + 2; + + pvar = env_alloc(info, varlen); + if (pvar == NULL) + { + ret = -ENOMEM; + goto out; + } + + /* Now, put the new name=value string into the environment */ + + snprintf(pvar, varlen, "%.*s=%s", (int)namelen, name, value); + + info->ta_envp[info->ta_envc++] = pvar; + info->ta_envp[info->ta_envc] = NULL; + +out: + nxrmutex_unlock(&info->ta_lock); + return ret; +} + /**************************************************************************** * Name: setenv * @@ -63,20 +139,14 @@ int setenv(FAR const char *name, FAR const char *value, int overwrite) { - FAR struct task_info_s *info; - FAR char *pvar; - FAR char **envp; - ssize_t envc; - ssize_t envpc; - ssize_t ret = OK; - int varlen; + int ret; /* Verify input parameter */ if (!name) { - ret = EINVAL; - goto errout; + set_errno(EINVAL); + return ERROR; } /* if no value is provided, then this is the same as unsetenv (unless @@ -101,103 +171,12 @@ int setenv(FAR const char *name, FAR const char *value, int overwrite) } } - /* Get a reference to the thread-private environ in the TCB. */ - - info = task_get_info(); - - DEBUGASSERT(info != NULL); - - nxrmutex_lock(&info->ta_lock); - - /* Check if the variable already exists */ - - if (info->ta_envp && (ret = env_findvar(info, name)) >= 0) + ret = env_setvar(name, strlen(name), value, overwrite); + if (ret < 0) { - /* It does! Do we have permission to overwrite the existing value? */ - - if (!overwrite) - { - /* No.. then just return success */ - - nxrmutex_unlock(&info->ta_lock); - return OK; - } - - /* Yes.. just remove the name=value pair from the environment. It will - * be added again below. Note that we are responsible for reallocating - * the environment buffer; this will happen below. - */ - - env_removevar(info, ret); + set_errno(-ret); + return ERROR; } - /* Check current envirments count */ - - DEBUGASSERT(info->ta_envc < SSIZE_MAX); - - /* Get the size of the new name=value string. - * The +2 is for the '=' and for null terminator - */ - - varlen = strlen(name) + strlen(value) + 2; - - /* Then allocate or reallocate the environment buffer */ - - pvar = env_malloc(info, varlen); - if (pvar == NULL) - { - ret = ENOMEM; - goto errout_with_lock; - } - - envc = info->ta_envc; - - if (info->ta_envp == NULL) - { - envpc = SCHED_ENVIRON_RESERVED + 2; - - envp = env_malloc(info, sizeof(*envp) * envpc); - if (envp == NULL) - { - ret = ENOMEM; - goto errout_with_var; - } - - info->ta_envp = envp; - info->ta_envpc = envpc; - } - else if (envc >= info->ta_envpc - 1) - { - envpc = envc + SCHED_ENVIRON_RESERVED + 2; - - envp = env_realloc(info, info->ta_envp, sizeof(*envp) * envpc); - if (envp == NULL) - { - ret = ENOMEM; - goto errout_with_var; - } - - info->ta_envp = envp; - info->ta_envpc = envpc; - } - - /* Save the new buffer and count */ - - info->ta_envp[envc++] = pvar; - info->ta_envp[envc] = NULL; - info->ta_envc = envc; - - /* Now, put the new name=value string into the environment buffer */ - - snprintf(pvar, varlen, "%s=%s", name, value); - nxrmutex_unlock(&info->ta_lock); return OK; - -errout_with_var: - env_free(info, pvar); -errout_with_lock: - nxrmutex_unlock(&info->ta_lock); -errout: - set_errno(ret); - return ERROR; } diff --git a/libs/libc/environ/env_unsetenv.c b/libs/libc/environ/env_unsetenv.c index 9e2dbfc48ae..3711213e740 100644 --- a/libs/libc/environ/env_unsetenv.c +++ b/libs/libc/environ/env_unsetenv.c @@ -57,6 +57,7 @@ int unsetenv(FAR const char *name) { FAR struct task_info_s *info = task_get_info(); ssize_t idx; + int ret; /* Check the incoming parameter */ @@ -66,12 +67,21 @@ int unsetenv(FAR const char *name) return ERROR; } + nxrmutex_lock(&info->ta_lock); + /* Check if the variable exists */ - nxrmutex_lock(&info->ta_lock); - if (info && (idx = env_findvar(info, name)) >= 0) + if ((idx = env_findvar(info, name, strlen(name))) >= 0) { - /* It does! Remove the name=value pair from the environment. */ + /* It does! Remove the name=value pair from an array of libc. */ + + ret = env_reserve(info, 0); + if (ret < 0) + { + nxrmutex_unlock(&info->ta_lock); + set_errno(-ret); + return ERROR; + } env_removevar(info, idx); } diff --git a/libs/libc/environ/environ.h b/libs/libc/environ/environ.h index 2b5f2888b5b..2618731054e 100644 --- a/libs/libc/environ/environ.h +++ b/libs/libc/environ/environ.h @@ -33,10 +33,6 @@ #include <nuttx/lib/lib.h> -#ifdef __KERNEL__ -# include <nuttx/kmalloc.h> -#endif - /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -45,25 +41,6 @@ # define SCHED_ENVIRON_RESERVED (4) -/* The environment is in the heap that holds the task information: the - * kernel heap for a kernel thread, the user heap for a user task. Kernel - * code also changes the environment of kernel threads (the idle task at - * boot) and of a new user task. - */ - -# ifdef __KERNEL__ -# define env_malloc(info, n) \ - (kmm_heapmember(info) ? kmm_malloc(n) : kumm_malloc(n)) -# define env_realloc(info, p, n) \ - (kmm_heapmember(info) ? kmm_realloc(p, n) : kumm_realloc(p, n)) -# define env_free(info, p) \ - (kmm_heapmember(info) ? kmm_free(p) : kumm_free(p)) -# else -# define env_malloc(info, n) ((void)(info), lib_umalloc(n)) -# define env_realloc(info, p, n) ((void)(info), lib_urealloc(p, n)) -# define env_free(info, p) ((void)(info), lib_ufree(p)) -# endif - /**************************************************************************** * Public Data ****************************************************************************/ @@ -88,8 +65,9 @@ extern "C" * specified name. * * Input Parameters: - * info - The task containing environment array to be searched. - * pname - The variable name to find + * info - The task containing environment array to be searched. + * pname - The variable name to find. It needs no terminating NUL. + * namelen - The length of the name. * * Returned Value: * A index to the name=value string in the environment @@ -100,7 +78,85 @@ extern "C" * ****************************************************************************/ -ssize_t env_findvar(FAR struct task_info_s *info, FAR const char *pname); +ssize_t env_findvar(FAR struct task_info_s *info, FAR const char *pname, + size_t namelen); + +/**************************************************************************** + * Name: env_setvar + * + * Description: + * setenv() with the length of the name, so that putenv() can pass the + * name part of its string without a copy. An empty value removes the + * variable, as in setenv(). + * + * Input Parameters: + * name - The name of the variable. It needs no terminating NUL. + * namelen - The length of the name. + * value - The new value of the variable. + * overwrite - Replace any existing value if non-zero. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int env_setvar(FAR const char *name, size_t namelen, + FAR const char *value, int overwrite); + +/**************************************************************************** + * Name: env_alloc + * + * Description: + * Allocate a block for the environment of a task. The block goes on a + * list of the task, and env_release() frees it when the task exits. + * libc does not free the strings before then, so a pointer that getenv() + * returned stays valid, and the program can keep a string or an array + * that it took from environ. + * + * Input Parameters: + * info - The task that owns the environment. + * size - The size of the block. + * + * Returned Value: + * The block, or NULL if there is no memory. + * + ****************************************************************************/ + +FAR void *env_alloc(FAR struct task_info_s *info, size_t size); + +/**************************************************************************** + * Name: env_free + * + * Description: + * Free a block from env_alloc(). Before the task exits, only for a block + * that nothing else can reach. + * + ****************************************************************************/ + +void env_free(FAR struct task_info_s *info, FAR void *mem); + +/**************************************************************************** + * Name: env_reserve + * + * Description: + * Make environ an array that libc allocated, with room for more entries. + * The program can assign environ, as POSIX allows. Then the array is + * copied, and the array of the program is not changed. The strings are + * not copied. + * + * Input Parameters: + * info - The task whose environment is changed. + * more - The number of entries to add. + * + * Returned Value: + * Zero (OK) on success; -ENOMEM if there is no memory. + * + * Assumptions: + * The caller holds ta_lock. + * + ****************************************************************************/ + +int env_reserve(FAR struct task_info_s *info, size_t more); /**************************************************************************** * Name: env_removevar @@ -118,12 +174,11 @@ ssize_t env_findvar(FAR struct task_info_s *info, FAR const char *pname); * * Assumptions: * - Not called from an interrupt handler - * - Caller has pre-emption disabled - * - Caller will reallocate the environment structure to the correct size + * - The caller holds ta_lock and called env_reserve() * ****************************************************************************/ -void env_removevar(FAR struct task_info_s *info, ssize_t index); +void env_removevar(FAR struct task_info_s *info, size_t index); #undef EXTERN #ifdef __cplusplus
