mirror of
https://github.com/opnsense/src.git
synced 2026-06-25 08:30:12 -04:00
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
This commit is contained in:
parent
3bef9b313b
commit
873420ca1e
9 changed files with 308 additions and 3 deletions
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ FBSD_1.7 {
|
|||
secure_getenv;
|
||||
};
|
||||
|
||||
FBSD_1.8 {
|
||||
getenv_r;
|
||||
};
|
||||
|
||||
FBSDprivate_1.0 {
|
||||
__system;
|
||||
_system;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 @@ 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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
69
lib/libc/tests/stdlib/getenv_r_test.c
Normal file
69
lib/libc/tests/stdlib/getenv_r_test.c
Normal file
|
|
@ -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());
|
||||
}
|
||||
Loading…
Reference in a new issue