From 622677d4a1b3a9f20234571fafdceb7b98fe1425 Mon Sep 17 00:00:00 2001 From: Ville Juven Date: Mon, 2 May 2022 15:15:06 +0300 Subject: [PATCH] libc: Implement exit, atexit, on_exit and cxa_exit on the user side For CONFIG_BUILD_KERNEL using the sched/task/task_exithook implementation will just not work. It calls user code with kernel privileges which is a bit of a security issue. --- include/nuttx/atexit.h | 118 ++++++++++ include/nuttx/tls.h | 5 + libs/libc/machine/arm/aeabi_atexit.c | 9 +- libs/libc/stdlib/Kconfig | 7 + libs/libc/stdlib/Make.defs | 6 +- libs/libc/stdlib/lib_atexit.c | 221 +++++++++++++++++++ libs/libc/stdlib/lib_cxa_atexit.c | 56 +++++ libs/libc/stdlib/{lib__Exit.c => lib_exit.c} | 27 ++- libs/libc/stdlib/lib_onexit.c | 68 ++++++ libs/libc/unistd/lib_sysconf.c | 8 +- libs/libxx/libxx.hxx | 49 ---- sched/task/exit.c | 30 +-- 12 files changed, 515 insertions(+), 89 deletions(-) create mode 100644 include/nuttx/atexit.h create mode 100644 libs/libc/stdlib/lib_atexit.c create mode 100644 libs/libc/stdlib/lib_cxa_atexit.c rename libs/libc/stdlib/{lib__Exit.c => lib_exit.c} (67%) create mode 100644 libs/libc/stdlib/lib_onexit.c delete mode 100644 libs/libxx/libxx.hxx diff --git a/include/nuttx/atexit.h b/include/nuttx/atexit.h new file mode 100644 index 00000000000..ad511290b54 --- /dev/null +++ b/include/nuttx/atexit.h @@ -0,0 +1,118 @@ +/**************************************************************************** + * include/nuttx/atexit.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_ATEXIT_H +#define __INCLUDE_NUTTX_ATEXIT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Amount of exit functions */ + +#define ATEXIT_MAX (CONFIG_LIBC_MAX_EXITFUNS) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum atexit_type_e +{ + ATTYPE_NONE, + ATTYPE_ATEXIT, + ATTYPE_ONEXIT, + ATTYPE_CXA +}; + +struct atexit_s +{ + int type; + CODE void (*func)(void); + FAR void *arg; +}; + +struct atexit_list_s +{ + int nfuncs; + struct atexit_s funcs[ATEXIT_MAX]; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if CONFIG_LIBC_MAX_EXITFUNS > 0 + +/**************************************************************************** + * Name: atexit_register + * + * Description: + * atexit_register registers a function function to be called by exit(). + * + * Input Parameters: + * type - Type of exit function. Available types in atexit_type_e. + * func - Function to be called on exit. + * arg - Optional argument to be passed to function on exit. + * dso - Dso handle, called when shared library is unloaded. + * + * Returned value: + * OK on success; ERROR on failure + * + ****************************************************************************/ + +int atexit_register(int type, CODE void (*func)(void), FAR void *arg, + FAR void *dso); + +/**************************************************************************** + * Name: atexit_call_exitfuncs + * + * Description: + * Execute the registered exit functions. Call this in exit(). + * + * Input Parameters: + * status - Process exit status code. + * + * Returned value: + * None + * + ****************************************************************************/ + +void atexit_call_exitfuncs(int status); +#else +# define atexit_register(type, func, arg, dso) (0) +# define atexit_call_exitfuncs(status) +#endif /* CONFIG_LIBC_MAX_EXITFUNS */ + +#if defined(__cplusplus) +} +#endif +#endif /* __INCLUDE_NUTTX_ATEXIT_H */ diff --git a/include/nuttx/tls.h b/include/nuttx/tls.h index de4cbcfe14c..27e031e3e1d 100644 --- a/include/nuttx/tls.h +++ b/include/nuttx/tls.h @@ -27,6 +27,8 @@ #include +#include + #include #include @@ -132,6 +134,9 @@ struct task_info_s char ta_domain[NAME_MAX]; /* Current domain for gettext */ # endif #endif +#if CONFIG_LIBC_MAX_EXITFUNS > 0 + struct atexit_list_s ta_exit; /* Exit functions */ +#endif }; /* struct pthread_cleanup_s *************************************************/ diff --git a/libs/libc/machine/arm/aeabi_atexit.c b/libs/libc/machine/arm/aeabi_atexit.c index 4d28606d442..9b27d1f8771 100644 --- a/libs/libc/machine/arm/aeabi_atexit.c +++ b/libs/libc/machine/arm/aeabi_atexit.c @@ -24,11 +24,7 @@ #include -/**************************************************************************** - * Public Function Prototypes - ****************************************************************************/ - -int __cxa_atexit(void (*func)(void *), void *object, void *dso_handle); +#include /**************************************************************************** * Public Functions @@ -50,5 +46,6 @@ int weak_function __aeabi_atexit(void *object, void (*func)(void *), void *dso_handle) { - return __cxa_atexit(func, object, dso_handle); + return atexit_register(ATTYPE_CXA, (void (*)(void))func, object, + dso_handle); } diff --git a/libs/libc/stdlib/Kconfig b/libs/libc/stdlib/Kconfig index b9125f8848d..f963f30dc64 100644 --- a/libs/libc/stdlib/Kconfig +++ b/libs/libc/stdlib/Kconfig @@ -39,4 +39,11 @@ config LIBC_MAX_TMPFILE maximum size of that last filename. This size is the size of the full file path. +config LIBC_MAX_EXITFUNS + int "Maximum amount of exit functions" + default 0 + ---help--- + Configure the amount of exit functions for atexit/on_exit. The ANSI + default is 32, but most likely we don't need as many. + endmenu # stdlib Options diff --git a/libs/libc/stdlib/Make.defs b/libs/libc/stdlib/Make.defs index 4b5dabf2d9f..0c9b40b3533 100644 --- a/libs/libc/stdlib/Make.defs +++ b/libs/libc/stdlib/Make.defs @@ -21,13 +21,17 @@ # Add the stdlib C files to the build CSRCS += lib_abs.c lib_abort.c lib_atof.c lib_atoi.c lib_getprogname.c -CSRCS += lib_atol.c lib_atoll.c lib_div.c lib_ldiv.c lib_lldiv.c lib__Exit.c +CSRCS += lib_atol.c lib_atoll.c lib_div.c lib_ldiv.c lib_lldiv.c lib_exit.c CSRCS += lib_itoa.c lib_labs.c lib_llabs.c lib_realpath.c lib_bsearch.c CSRCS += lib_rand.c lib_qsort.c lib_srand.c lib_strtol.c CSRCS += lib_strtoll.c lib_strtoul.c lib_strtoull.c lib_strtod.c lib_strtof.c CSRCS += lib_strtold.c lib_checkbase.c lib_mktemp.c lib_mkstemp.c lib_mkdtemp.c CSRCS += lib_aligned_alloc.c lib_posix_memalign.c lib_valloc.c +ifneq ($(CONFIG_LIBC_MAX_EXITFUNS),0) +CSRCS += lib_atexit.c lib_cxa_atexit.c lib_onexit.c +endif + ifeq ($(CONFIG_LIBC_WCHAR),y) CSRCS += lib_mblen.c lib_mbtowc.c lib_wctomb.c CSRCS += lib_mbstowcs.c lib_wcstombs.c diff --git a/libs/libc/stdlib/lib_atexit.c b/libs/libc/stdlib/lib_atexit.c new file mode 100644 index 00000000000..7f3dcaefc4f --- /dev/null +++ b/libs/libc/stdlib/lib_atexit.c @@ -0,0 +1,221 @@ +/**************************************************************************** + * libs/libc/stdlib/lib_atexit.c + * + * 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 + +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: get_exitfuncs + * + * Description: + * Obtain the list of exit functions. + * + * Input Parameters: + * None + * + * Returned Value: + * Pointer to the list of exit functions. + * + ****************************************************************************/ + +static FAR struct atexit_list_s * get_exitfuncs(void) +{ + FAR struct task_info_s *info; + + info = task_get_info(); + return &info->ta_exit; +} + +/**************************************************************************** + * Name: exitfunc_lock + * + * Description: + * Obtain the exit function lock. + * + * Returned Value: + * OK on success, or negated errno on failure + * + ****************************************************************************/ + +static int exitfunc_lock(void) +{ + FAR struct task_info_s *info = task_get_info(); + int ret = _SEM_WAIT(&info->ta_sem); + + if (ret < 0) + { + ret = _SEM_ERRVAL(ret); + } + + return ret; +} + +/**************************************************************************** + * Name: exitfunc_unlock + * + * Description: + * Release exit function lock . + * + ****************************************************************************/ + +static void exitfunc_unlock(void) +{ + FAR struct task_info_s *info = task_get_info(); + + _SEM_POST(&info->ta_sem); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: atexit + * + * Description: + * Registers a function to be called at program exit. + * The atexit() function registers the given function to be called + * at normal process termination, whether via exit or via return from + * the program's main(). + * + * Limitations in the current implementation: + * + * 1. Only a single atexit function can be registered unless + * CONFIG_LIBC_MAX_EXITFUNS defines a larger number. + * 2. atexit functions are not inherited when a new task is + * created. + * + * Input Parameters: + * func - A pointer to the function to be called when the task exits. + * + * Returned Value: + * Zero on success. Non-zero on failure. + * + ****************************************************************************/ + +int atexit(CODE void (*func)(void)) +{ + return atexit_register(ATTYPE_ATEXIT, func, NULL, NULL); +} + +int atexit_register(int type, CODE void (*func)(void), FAR void *arg, + FAR void *dso) +{ + FAR struct atexit_list_s *aehead; + int idx; + int ret = ERROR; + + /* REVISIT: Missing logic */ + + UNUSED(dso); + + /* The following must be atomic */ + + aehead = get_exitfuncs(); + + if (func) + { + ret = exitfunc_lock(); + if (ret < 0) + { + return ret; + } + + if ((idx = aehead->nfuncs) < ATEXIT_MAX) + { + aehead->funcs[idx].type = type; + aehead->funcs[idx].func = func; + aehead->funcs[idx].arg = arg; + aehead->nfuncs++; + ret = OK; + } + else + { + ret = ERROR; + } + + exitfunc_unlock(); + } + + return ret; +} + +void atexit_call_exitfuncs(int status) +{ + FAR struct atexit_list_s *aehead; + CODE void (*func)(void); + FAR void *arg; + int idx; + int type; + + /* Call exit functions in reverse order */ + + aehead = get_exitfuncs(); + + for (idx = aehead->nfuncs - 1; idx >= 0; idx--) + { + /* Remove the function to prevent recursive call to it */ + + type = aehead->funcs[idx].type; + + func = aehead->funcs[idx].func; + arg = aehead->funcs[idx].arg; + + aehead->funcs[idx].func = NULL; + aehead->funcs[idx].arg = NULL; + + if (!func) + { + continue; + } + + /* Call the atexit/on_exit/cxa_atexit() function */ + + if (type == ATTYPE_ATEXIT) + { + (*func)(); + } + else if (type == ATTYPE_ONEXIT) + { + (*((CODE void (*)(int, FAR void *))func))(status, arg); + } + else if (type == ATTYPE_CXA) + { + (*((CODE void (*)(FAR void *))func))(arg); + } + } +} diff --git a/libs/libc/stdlib/lib_cxa_atexit.c b/libs/libc/stdlib/lib_cxa_atexit.c new file mode 100644 index 00000000000..3e68eedfb58 --- /dev/null +++ b/libs/libc/stdlib/lib_cxa_atexit.c @@ -0,0 +1,56 @@ +/**************************************************************************** + * libs/libc/stdlib/lib_cxa_atexit.c + * + * 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 + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: __cxa_atexit + * + * Description: + * __cxa_atexit() registers a destructor function to be called by exit(). + * On a call to exit(), the registered functions should be called with + * the single argument 'arg'. Destructor functions shall always be + * called in the reverse order to their registration (i.e. the most + * recently registered function shall be called first), + * + * If shared libraries were supported, the callbacks should be invoked + * when the shared library is unloaded as well. + * + * Reference: + * Linux base + * + ****************************************************************************/ + +int __cxa_atexit(CODE void (*func)(FAR void *), FAR void *arg, + FAR void *dso_handle) +{ + return atexit_register(ATTYPE_CXA, (CODE void (*)(void))func, arg, + dso_handle); +} diff --git a/libs/libc/stdlib/lib__Exit.c b/libs/libc/stdlib/lib_exit.c similarity index 67% rename from libs/libc/stdlib/lib__Exit.c rename to libs/libc/stdlib/lib_exit.c index 557dfa8b36a..73f669a790b 100644 --- a/libs/libc/stdlib/lib__Exit.c +++ b/libs/libc/stdlib/lib_exit.c @@ -1,5 +1,5 @@ /**************************************************************************** - * libs/libc/stdlib/lib__Exit.c + * libs/libc/stdlib/lib_exit.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -22,13 +22,38 @@ * Included Files ****************************************************************************/ +#include + +#include +#include + #include #include +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern FAR void *__dso_handle weak_data; +FAR void *__dso_handle = &__dso_handle; + /**************************************************************************** * Public Functions ****************************************************************************/ +void exit(int status) +{ + atexit_call_exitfuncs(status); + + /* REVISIT: Need to flush files and streams */ + + _exit(status); +} + void _Exit(int status) { _exit(status); diff --git a/libs/libc/stdlib/lib_onexit.c b/libs/libc/stdlib/lib_onexit.c new file mode 100644 index 00000000000..f3a34d9827b --- /dev/null +++ b/libs/libc/stdlib/lib_onexit.c @@ -0,0 +1,68 @@ +/**************************************************************************** + * libs/libc/stdlib/lib_onexit.c + * + * 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 + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: on_exit + * + * Description: + * Registers a function to be called at program exit. + * The on_exit() function registers the given function to be called + * at normal process termination, whether via exit or via return from + * the program's main(). The function is passed the status argument + * given to the last call to exit and the arg argument from on_exit(). + * + * NOTE 1: This function comes from SunOS 4, but is also present in + * libc4, libc5 and glibc. It no longer occurs in Solaris (SunOS 5). + * Avoid this function, and use the standard atexit() instead. + * + * Limitations in the current implementation: + * + * 1. Only a single on_exit function can be registered unless + * CONFIG_LIBC_MAX_EXITFUNS defines a larger number. + * 2. on_exit functions are not inherited when a new task is + * created. + * + * Input Parameters: + * func - A pointer to the function to be called when the task exits. + * arg - An argument that will be provided to the on_exit() function when + * the task exits. + * + * Returned Value: + * Zero on success. Non-zero on failure. + * + ****************************************************************************/ + +int on_exit(CODE void (*func)(int, FAR void *), FAR void *arg) +{ + return atexit_register(ATTYPE_ONEXIT, (CODE void (*)(void))func, arg, + NULL); +} diff --git a/libs/libc/unistd/lib_sysconf.c b/libs/libc/unistd/lib_sysconf.c index ee5fd2b35cd..6a141a2e526 100644 --- a/libs/libc/unistd/lib_sysconf.c +++ b/libs/libc/unistd/lib_sysconf.c @@ -24,6 +24,8 @@ #include +#include + #include #include #include @@ -214,11 +216,7 @@ long sysconf(int name) return OPEN_MAX; case _SC_ATEXIT_MAX: -#ifdef CONFIG_SCHED_EXIT_MAX - return CONFIG_SCHED_EXIT_MAX; -#else - return 0; -#endif + return ATEXIT_MAX; case _SC_NPROCESSORS_CONF: case _SC_NPROCESSORS_ONLN: diff --git a/libs/libxx/libxx.hxx b/libs/libxx/libxx.hxx deleted file mode 100644 index 9dfd110fdb5..00000000000 --- a/libs/libxx/libxx.hxx +++ /dev/null @@ -1,49 +0,0 @@ -//*************************************************************************** -// libs/libxx/libxx.hxx -// -// 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 -// -//*************************************************************************** - -#ifndef __LIBS_LIBXX_LIBXX_HXX -#define __LIBS_LIBXX_LIBXX_HXX 1 - -//*************************************************************************** -// Included Files -//*************************************************************************** - -#include - -#include - -//*************************************************************************** -// Public Types -//***************************************************************************/ - -typedef CODE void (*__cxa_exitfunc_t)(void *arg); - -//*************************************************************************** -// Public Data -//*************************************************************************** - -extern "C" FAR void *__dso_handle weak_data; - -//*************************************************************************** -// Public Function Prototypes -//*************************************************************************** - -extern "C" int __cxa_atexit(__cxa_exitfunc_t func, void *arg, void *dso_handle); - -#endif // __LIBS_LIBXX_LIBXX_HXX diff --git a/sched/task/exit.c b/sched/task/exit.c index 97d0b7a05b3..0834d87e957 100644 --- a/sched/task/exit.c +++ b/sched/task/exit.c @@ -52,25 +52,6 @@ ****************************************************************************/ void _exit(int status) -{ - up_exit(status); -} - -/**************************************************************************** - * Name: exit - * - * Description: - * The exit() function causes normal process termination and the value of - * status & 0377 to be returned to the parent. - * - * All functions registered with atexit() and on_exit() are called, in the - * reverse order of their registration. - * - * All open streams are flushed and closed. - * - ****************************************************************************/ - -void exit(int status) { FAR struct tcb_s *tcb = this_task(); @@ -94,17 +75,12 @@ void exit(int status) #endif /* Perform common task termination logic. This will get called again later - * through logic kicked off by _exit(). However, we need to call it before - * calling _exit() in order to handle atexit() and on_exit() callbacks and + * through logic kicked off by up_exit(). However, we need to call it here * so that we can flush buffered I/O (both of which may required - * suspending). + * suspending). This will be fixed later when I/O flush is moved to libc. */ nxtask_exithook(tcb, status, false); - /* Then "really" exit. Only the lower 8 bits of the exit status are - * used. - */ - - _exit(status); + up_exit(status); }