1 /*
2  * Copyright (c) 2023, Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 
10 #include <zephyr/ztest.h>
11 
12 #define M_HOME "/home/zephyr"
13 #define M_UID  "1000"
14 #define M_PWD  "/tmp"
15 
16 #define _m_alt_home "/this/path/is/much/longer/than" M_HOME
17 
18 #define DEFINE_ENVIRON(_handle, _key, _val) char _handle[] = _key "=" _val
19 #define RESET_ENVIRON(_handle, _key, _val)                                                         \
20 	snprintf(_handle, ARRAY_SIZE(_handle), "%s=%s", _key, _val)
21 
22 #if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_PICOLIBC)
23 /* newlib headers seem to be missing this */
24 int getenv_r(const char *name, char *val, size_t len);
25 #endif
26 
27 extern char **environ;
28 static char **old_environ;
29 
30 static DEFINE_ENVIRON(home, "HOME", M_HOME);
31 static DEFINE_ENVIRON(uid, "UID", M_UID);
32 static DEFINE_ENVIRON(pwd, "PWD", M_PWD);
33 
34 static char *environ_for_test[] = {home, uid, pwd, NULL};
35 
ZTEST(posix_single_process,test_getenv)36 ZTEST(posix_single_process, test_getenv)
37 {
38 	zassert_equal(getenv(NULL), NULL);
39 	zassert_equal(getenv(""), NULL);
40 	zassert_equal(getenv("invalid=key"), NULL);
41 	zassert_equal(getenv("HOME=" M_HOME), NULL);
42 	zassert_equal(getenv("PWDR"), NULL);
43 
44 	zassert_mem_equal(getenv("HOME"), M_HOME, strlen(M_HOME) + 1);
45 	zassert_mem_equal(getenv("UID"), M_UID, strlen(M_UID) + 1);
46 	zassert_mem_equal(getenv("PWD"), M_PWD, strlen(M_PWD) + 1);
47 }
48 
ZTEST(posix_single_process,test_getenv_r)49 ZTEST(posix_single_process, test_getenv_r)
50 {
51 	static char buf[16];
52 	static const int exp_errno[] = {
53 		EINVAL, EINVAL, EINVAL, EINVAL, ENOENT, ENOENT, ENOENT, EINVAL, EINVAL, EINVAL,
54 	};
55 	static const struct args_s {
56 		const char *name;
57 		char *buf;
58 		size_t size;
59 	} args[] = {
60 		/* invalid input */
61 		{NULL, NULL, 0},
62 		{NULL, NULL, 42},
63 		{NULL, buf, 0},
64 		{NULL, buf, sizeof(buf)},
65 		{"hello", NULL, 0},
66 		{"hello", NULL, 42},
67 		{"hello", buf, 0},
68 
69 		/* invalid names */
70 		{"", buf, sizeof(buf)},
71 		{"invalid=key", buf, sizeof(buf)},
72 		{"HOME=", buf, sizeof(buf)},
73 	};
74 
75 	BUILD_ASSERT(ARRAY_SIZE(exp_errno) == ARRAY_SIZE(args));
76 
77 	ARRAY_FOR_EACH(args, i) {
78 		errno = 0;
79 		zassert_equal(getenv_r(args[i].name, args[i].buf, args[i].size), -1,
80 			      "getenv_r(\"%s\", %p, %zu): expected to fail", args[i].name,
81 			      args[i].buf, args[i].size);
82 		zassert_equal(errno, exp_errno[i],
83 			      "getenv_r(\"%s\", %p, %zu): act_errno: %d exp_errno: %d",
84 			      args[i].name, args[i].buf, args[i].size, errno, exp_errno[i]);
85 	}
86 
87 	zassert_mem_equal(getenv("HOME"), M_HOME, strlen(M_HOME) + 1);
88 	zassert_mem_equal(getenv("UID"), M_UID, strlen(M_UID) + 1);
89 	zassert_mem_equal(getenv("PWD"), M_PWD, strlen(M_PWD) + 1);
90 }
91 
ZTEST(posix_single_process,test_setenv)92 ZTEST(posix_single_process, test_setenv)
93 {
94 	zassert_equal(setenv(NULL, NULL, 0), -1);
95 	zassert_equal(errno, EINVAL);
96 
97 	/*
98 	 * bug in picolibc / newlib
99 	 * https://github.com/picolibc/picolibc/issues/648
100 	 */
101 	zassert_equal(setenv("", "42", 0), -1);
102 	zassert_equal(errno, EINVAL);
103 
104 	zassert_equal(setenv("invalid=key", "42", 0), -1);
105 	zassert_equal(errno, EINVAL);
106 
107 	/* do not overwrite if environ[key] exists */
108 	zassert_ok(setenv("HOME", "/root", 0));
109 	zassert_mem_equal(getenv("HOME"), M_HOME, strlen(M_HOME) + 1);
110 
111 	/* should overwrite (without malloc) */
112 	zassert_ok(setenv("HOME", "/root", 1));
113 	zassert_mem_equal(getenv("HOME"), "/root", strlen("/root") + 1);
114 }
115 
ZTEST(posix_single_process,test_unsetenv)116 ZTEST(posix_single_process, test_unsetenv)
117 {
118 	/* not hardened / application should fault */
119 	zassert_equal(unsetenv(NULL), -1);
120 	zassert_equal(errno, EINVAL);
121 
122 	errno = 0;
123 	/* bug in picolibc / newlib */
124 	zassert_equal(unsetenv(""), -1);
125 	zassert_equal(errno, EINVAL);
126 
127 	zassert_equal(unsetenv("invalid=key"), -1);
128 	zassert_equal(errno, EINVAL);
129 
130 	/* restore original environ */
131 	environ = old_environ;
132 	/* should overwrite (requires realloc) */
133 	zassert_ok(setenv("HOME", _m_alt_home, 1));
134 	zassert_mem_equal(getenv("HOME"), _m_alt_home, strlen(_m_alt_home) + 1);
135 	zassert_ok(unsetenv("HOME"));
136 	zassert_is_null(getenv("HOME"));
137 }
138 
ZTEST(posix_single_process,test_watertight)139 ZTEST(posix_single_process, test_watertight)
140 {
141 	extern size_t posix_env_get_allocated_space(void);
142 
143 	char buf[4];
144 
145 	/* restore original environ, which should support realloc, free, etc */
146 	environ = old_environ;
147 
148 	for (int i = 0; i < 256; ++i) {
149 		snprintf(buf, sizeof(buf), "%u", i);
150 		zassert_ok(setenv("COUNTER", buf, 1));
151 		zassert_mem_equal(getenv("COUNTER"), buf, strlen(buf));
152 		zassert_ok(getenv_r("COUNTER", buf, sizeof(buf)));
153 		zassert_equal(atoi(buf), i);
154 		zassert_ok(unsetenv("COUNTER"));
155 	}
156 
157 	zassert_equal(posix_env_get_allocated_space(), 0);
158 }
159 
test_env_before(void)160 void test_env_before(void)
161 {
162 	old_environ = environ;
163 
164 	RESET_ENVIRON(home, "HOME", M_HOME);
165 	RESET_ENVIRON(uid, "UID", M_UID);
166 	RESET_ENVIRON(pwd, "PWD", M_PWD);
167 	environ_for_test[0] = home;
168 	environ_for_test[1] = uid;
169 	environ_for_test[2] = pwd;
170 
171 	zassert_equal((environ = environ_for_test), environ_for_test);
172 }
173 
test_env_after(void)174 void test_env_after(void)
175 {
176 	environ = old_environ;
177 }
178