Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F115509669
D49979.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D49979.diff
View Options
diff --git a/include/ssp/stdlib.h b/include/ssp/stdlib.h
--- a/include/ssp/stdlib.h
+++ b/include/ssp/stdlib.h
@@ -38,6 +38,10 @@
__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
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -95,6 +95,9 @@
_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
--- a/lib/libc/stdlib/Symbol.map
+++ b/lib/libc/stdlib/Symbol.map
@@ -127,6 +127,10 @@
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
--- 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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
.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
--- a/lib/libc/stdlib/getenv.c
+++ b/lib/libc/stdlib/getenv.c
@@ -28,6 +28,7 @@
#include "namespace.h"
#include <sys/types.h>
+#include <ssp/ssp.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
@@ -443,6 +444,41 @@
}
+/*
+ * 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
--- a/lib/libc/tests/secure/fortify_stdlib_test.c
+++ b/lib/libc/tests/secure/fortify_stdlib_test.c
@@ -305,6 +305,148 @@
}
+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_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
--- a/lib/libc/tests/secure/generate-fortify-tests.lua
+++ b/lib/libc/tests/secure/generate-fortify-tests.lua
@@ -584,6 +584,15 @@
},
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
--- 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
--- /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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+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());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 25, 3:42 PM (16 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17788061
Default Alt Text
D49979.diff (10 KB)
Attached To
Mode
D49979: libc: Add getenv_r() function.
Attached
Detach File
Event Timeline
Log In to Comment