From nobody Sun Apr 27 06:29:53 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ZlcBT6WGsz5vNJr; Sun, 27 Apr 2025 06:29:53 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ZlcBT5nf4z3qZL; Sun, 27 Apr 2025 06:29:53 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1745735393; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=JdLj9FV64NKBBEoN2U2H29Ev38Q/3Xkn3Hdk+SD7Glw=; b=htGtzZdNU3kDSQlihrAY6jtU8POg/OWj4dNsnDLf/KoJ0FJm4BHnA1yCWUG3szjihKfGHo 1R6G8dzjxKSwBFpM8G8ScGO6qijnt5ZX4zxkjxpNAbppRxLqB1eadYTBtPT0PEoms1qfR6 O1KEydnC+ZMQGbOS0OI+UMyd1anbcQywarAClaQaJOgo8wMgaPqNqkImvLQtuDdjFTb1rR +9gSTyesGO0Eeb2veS9avLOcBfOckcNTiXUAxiMi5mLTWFHEUwi0AijSYmeI3Fwe4ZRzdR Ekl4b2BXDvZv37pelXU6SqO6wp4pJF6iOqL07dcmVqrKd/RgfmNBADqlz3HPkg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1745735393; a=rsa-sha256; cv=none; b=gNiWCTQblSv2oAl5psNO6XDRmt+leAVPvoRGzpCmCbieGgsIIYr5FmL7fHFUwfzI5qdm3p A/jSbM7r7JJcH6zgfJKilvMHqV/vp+OIkBmUnR52wVaoGccgvu1bL9gOybU8OxgfuS3ll8 TqMi1Osr/VNpYQ8x8VM/E5wL6Fg3vm487PM0lnwkWIlyUMvMM5I4Tr/LQNQjWIuxfriy3U waYUKdwdJ9wcsY6B6edNM7qblIVWiDWjXnDLkxYiiGETZkC8qbWT31pt65NsW8xND/GWpj OoFCM4eq9tfmhf1KBPg7sxgdLv8EyyD6uKepF9dUXj2xuZTwJOEklMBUwCXuag== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1745735393; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=JdLj9FV64NKBBEoN2U2H29Ev38Q/3Xkn3Hdk+SD7Glw=; b=eRG7AKSrdnXk7+MVsqFlPAumpDR21L38C3vVWhNeqep/dhvX1cxP8BFejWO0PHouqfvTk2 Ux9dnnp7OzNCrygkV2l8FHchCTZ6DKrwG4CQknCXT6LjUXVRE6zqW3eLjrm6VNsWLTqJXt lE1x1gW+6JoHo4DFlfbKjBKA9SdCcn68M6+1YgeyAX4ie6mRylx0aVXNeEU/mgrSSUxtCT 4F5d/EhvE2Hqhq7Esa0a2yeHc5chrk6gB8W5LkZ7VaznpEIJnOnLRBn0DfqUqBch9XGZWD /vLC6WBpHZo4y3+ZzMKnWxO/Z7fxdqrkmJTwSBGHL3Gf69JLu4c5J2ao52swhA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4ZlcBT4ndbzvWl; Sun, 27 Apr 2025 06:29:53 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 53R6Trua053360; Sun, 27 Apr 2025 06:29:53 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 53R6TrZ6053357; Sun, 27 Apr 2025 06:29:53 GMT (envelope-from git) Date: Sun, 27 Apr 2025 06:29:53 GMT Message-Id: <202504270629.53R6TrZ6053357@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling =?utf-8?Q?Sm=C3=B8rgrav?= Subject: git: 873420ca1e6e - main - libc: Add getenv_r() function. List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 873420ca1e6e8a2459684f5b5d3e557a8ef75928 Auto-Submitted: auto-generated The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=873420ca1e6e8a2459684f5b5d3e557a8ef75928 commit 873420ca1e6e8a2459684f5b5d3e557a8ef75928 Author: Dag-Erling Smørgrav AuthorDate: 2025-04-27 06:29:10 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2025-04-27 06:29:32 +0000 libc: Add getenv_r() function. This is a calque of the NetBSD function of the same name. MFC after: never Relontes: yes Sponsored by: Klara, Inc. Reviewed by: kevans Differential Revision: https://reviews.freebsd.org/D49979 --- include/ssp/stdlib.h | 4 + include/stdlib.h | 3 + lib/libc/stdlib/Symbol.map | 4 + lib/libc/stdlib/getenv.3 | 38 +++++- lib/libc/stdlib/getenv.c | 36 ++++++ lib/libc/tests/secure/fortify_stdlib_test.c | 147 +++++++++++++++++++++++ lib/libc/tests/secure/generate-fortify-tests.lua | 9 ++ lib/libc/tests/stdlib/Makefile | 1 + lib/libc/tests/stdlib/getenv_r_test.c | 69 +++++++++++ 9 files changed, 308 insertions(+), 3 deletions(-) diff --git a/include/ssp/stdlib.h b/include/ssp/stdlib.h index f595ecbcbc0a..0a87be9f17d9 100644 --- a/include/ssp/stdlib.h +++ b/include/ssp/stdlib.h @@ -38,6 +38,10 @@ __BEGIN_DECLS __ssp_redirect(void, arc4random_buf, (void *__buf, size_t __len), (__buf, __len)); +__ssp_redirect(int, getenv_r, + (const char *name, char * _Nonnull __buf, size_t __len), + (name, __buf, __len)); + __ssp_redirect_raw_impl(char *, realpath, realpath, (const char *__restrict path, char *__restrict buf)) { diff --git a/include/stdlib.h b/include/stdlib.h index 162031ab393d..ba0cf4b5e88e 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -95,6 +95,9 @@ div_t div(int, int) __pure2; _Noreturn void exit(int); void free(void *); char *getenv(const char *); +#if __BSD_VISIBLE +int getenv_r(const char *, char * _Nonnull, size_t); +#endif long labs(long) __pure2; ldiv_t ldiv(long, long) __pure2; void *malloc(size_t) __malloc_like __result_use_check __alloc_size(1); diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map index d74df869cf54..2b79ca2ece8b 100644 --- a/lib/libc/stdlib/Symbol.map +++ b/lib/libc/stdlib/Symbol.map @@ -127,6 +127,10 @@ FBSD_1.7 { secure_getenv; }; +FBSD_1.8 { + getenv_r; +}; + FBSDprivate_1.0 { __system; _system; diff --git a/lib/libc/stdlib/getenv.3 b/lib/libc/stdlib/getenv.3 index d78dcfbef9eb..045a1831dfdc 100644 --- a/lib/libc/stdlib/getenv.3 +++ b/lib/libc/stdlib/getenv.3 @@ -29,12 +29,13 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 17, 2025 +.Dd April 22, 2025 .Dt GETENV 3 .Os .Sh NAME .Nm clearenv , .Nm getenv , +.Nm getenv_r , .Nm putenv , .Nm secure_getenv , .Nm setenv , @@ -48,6 +49,8 @@ .Fn clearenv "void" .Ft char * .Fn getenv "const char *name" +.Ft int +.Fn getenv_r "const char *name" "char *buf" "size_t len" .Ft char * .Fn secure_getenv "const char *name" .Ft int @@ -71,7 +74,8 @@ and .Pp The .Fn getenv -function obtains the current value of the environment variable, +function obtains the current value of the environment variable +designated by .Fa name . The application should not modify the string pointed to by the @@ -79,6 +83,16 @@ to by the function. .Pp The +.Fn getenv_r +function obtains the current value of the environment variable +designated by +.Fa name +and copies it into the buffer +.Fa buf +of length +.Fa len . +.Pp +The .Fn secure_getenv returns .Va NULL @@ -157,12 +171,13 @@ function returns if the process is in "secure execution," otherwise it will call .Fn getenv . .Pp -.Rv -std clearenv setenv putenv unsetenv +.Rv -std clearenv getenv_r setenv putenv unsetenv .Sh ERRORS .Bl -tag -width Er .It Bq Er EINVAL The function .Fn getenv , +.Fn getenv_r , .Fn setenv or .Fn unsetenv @@ -191,6 +206,11 @@ is the first character in This does not follow the .Tn POSIX specification. +.It Bq Er ENOENT +The function +.Fn getenv_r +failed because the requested variable was not found in the +environment. .It Bq Er ENOMEM The function .Fn setenv , @@ -198,6 +218,11 @@ The function or .Fn putenv failed because they were unable to allocate memory for the environment. +.It Bq Er ERANGE +The function +.Fn getenv_r +failed because the value of the requested variable was too long to fit +in the provided buffer. .El .Sh SEE ALSO .Xr csh 1 , @@ -251,6 +276,13 @@ and .Fn secure_getenv functions were added in .Fx 14 . +.Pp +The +.Fn getenv_r +function first appeared in +.Nx 4.0 +and was added in +.Fx 15 . .Sh BUGS Successive calls to .Fn setenv diff --git a/lib/libc/stdlib/getenv.c b/lib/libc/stdlib/getenv.c index f8f59526421a..c1d0b7a559d5 100644 --- a/lib/libc/stdlib/getenv.c +++ b/lib/libc/stdlib/getenv.c @@ -28,6 +28,7 @@ #include "namespace.h" #include +#include #include #include #include @@ -443,6 +444,41 @@ getenv(const char *name) } +/* + * Like getenv(), but copies the value into the provided buffer. + */ +int +__ssp_real(getenv_r)(const char *name, char *buf, size_t len) +{ + const char *val; + size_t nameLen; + int envNdx; + + if (name == NULL || (nameLen = __strleneq(name)) == 0) { + errno = EINVAL; + return (-1); + } + + if (environ == NULL || environ[0] == NULL) { + val = NULL; + } else if (envVars == NULL || environ != intEnviron) { + val = __findenv_environ(name, nameLen); + } else { + envNdx = envVarsTotal - 1; + val = __findenv(name, nameLen, &envNdx, true); + } + if (val == NULL) { + errno = ENOENT; + return (-1); + } + if (strlcpy(buf, val, len) >= len) { + errno = ERANGE; + return (-1); + } + return (0); +} + + /* * Runs getenv() unless the current process is tainted by uid or gid changes, in * which case it will return NULL. diff --git a/lib/libc/tests/secure/fortify_stdlib_test.c b/lib/libc/tests/secure/fortify_stdlib_test.c index ae021e8418f7..d0b1af78da86 100644 --- a/lib/libc/tests/secure/fortify_stdlib_test.c +++ b/lib/libc/tests/secure/fortify_stdlib_test.c @@ -305,6 +305,148 @@ monitor: } +ATF_TC(getenv_r_before_end); +ATF_TC_HEAD(getenv_r_before_end, tc) +{ +} +ATF_TC_BODY(getenv_r_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_end); +ATF_TC_HEAD(getenv_r_end, tc) +{ +} +ATF_TC_BODY(getenv_r_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_heap_before_end); +ATF_TC_HEAD(getenv_r_heap_before_end, tc) +{ +} +ATF_TC_BODY(getenv_r_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_heap_end); +ATF_TC_HEAD(getenv_r_heap_end, tc) +{ +} +ATF_TC_BODY(getenv_r_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_heap_after_end); +ATF_TC_HEAD(getenv_r_heap_after_end, tc) +{ +} +ATF_TC_BODY(getenv_r_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + + getenv_r("PATH", __stack.__buf, __len); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + ATF_TC(realpath_before_end); ATF_TC_HEAD(realpath_before_end, tc) { @@ -454,6 +596,11 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, arc4random_buf_heap_before_end); ATF_TP_ADD_TC(tp, arc4random_buf_heap_end); ATF_TP_ADD_TC(tp, arc4random_buf_heap_after_end); + ATF_TP_ADD_TC(tp, getenv_r_before_end); + ATF_TP_ADD_TC(tp, getenv_r_end); + ATF_TP_ADD_TC(tp, getenv_r_heap_before_end); + ATF_TP_ADD_TC(tp, getenv_r_heap_end); + ATF_TP_ADD_TC(tp, getenv_r_heap_after_end); ATF_TP_ADD_TC(tp, realpath_before_end); ATF_TP_ADD_TC(tp, realpath_end); ATF_TP_ADD_TC(tp, realpath_heap_before_end); diff --git a/lib/libc/tests/secure/generate-fortify-tests.lua b/lib/libc/tests/secure/generate-fortify-tests.lua index 36ff01af7a17..6c2a80b20609 100755 --- a/lib/libc/tests/secure/generate-fortify-tests.lua +++ b/lib/libc/tests/secure/generate-fortify-tests.lua @@ -584,6 +584,15 @@ local all_tests = { }, exclude = excludes_stack_overflow, }, + { + func = "getenv_r", + arguments = { + "\"PATH\"", + "__buf", + "__len", + }, + exclude = excludes_stack_overflow, + }, { func = "realpath", bufsize = "PATH_MAX", diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile index 08e356fc8706..50726a5d8af6 100644 --- a/lib/libc/tests/stdlib/Makefile +++ b/lib/libc/tests/stdlib/Makefile @@ -3,6 +3,7 @@ ATF_TESTS_C+= clearenv_test ATF_TESTS_C+= cxa_atexit_test ATF_TESTS_C+= dynthr_test +ATF_TESTS_C+= getenv_r_test ATF_TESTS_C+= heapsort_test ATF_TESTS_C+= libc_exit_test ATF_TESTS_C+= mergesort_test diff --git a/lib/libc/tests/stdlib/getenv_r_test.c b/lib/libc/tests/stdlib/getenv_r_test.c new file mode 100644 index 000000000000..7935ead6c495 --- /dev/null +++ b/lib/libc/tests/stdlib/getenv_r_test.c @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#include + +ATF_TC_WITHOUT_HEAD(getenv_r_ok); +ATF_TC_BODY(getenv_r_ok, tc) +{ + const char *ident = atf_tc_get_ident(tc); + char buf[256]; + + ATF_REQUIRE_EQ(0, setenv("ATF_TC_IDENT", ident, 1)); + ATF_REQUIRE_EQ(0, getenv_r("ATF_TC_IDENT", buf, sizeof(buf))); + ATF_REQUIRE_STREQ(ident, buf); +} + +ATF_TC_WITHOUT_HEAD(getenv_r_einval); +ATF_TC_BODY(getenv_r_einval, tc) +{ + char buf[256]; + + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r(NULL, buf, sizeof(buf))); + ATF_REQUIRE_EQ(EINVAL, errno); + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r("", buf, sizeof(buf))); + ATF_REQUIRE_EQ(EINVAL, errno); + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r("A=B", buf, sizeof(buf))); + ATF_REQUIRE_EQ(EINVAL, errno); +} + +ATF_TC_WITHOUT_HEAD(getenv_r_enoent); +ATF_TC_BODY(getenv_r_enoent, tc) +{ + char buf[256]; + + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r("no such variable", buf, sizeof(buf))); + ATF_REQUIRE_EQ(ENOENT, errno); +} + +ATF_TC_WITHOUT_HEAD(getenv_r_erange); +ATF_TC_BODY(getenv_r_erange, tc) +{ + const char *ident = atf_tc_get_ident(tc); + char buf[256]; + + ATF_REQUIRE_EQ(0, setenv("ATF_TC_IDENT", ident, 1)); + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r(NULL, buf, strlen(ident))); + ATF_REQUIRE_EQ(ERANGE, errno); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, getenv_r_ok); + ATF_TP_ADD_TC(tp, getenv_r_einval); + ATF_TP_ADD_TC(tp, getenv_r_enoent); + ATF_TP_ADD_TC(tp, getenv_r_erange); + return (atf_no_error()); +}