1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "sandboxed_api/sandbox2/util.h"
16
17 #include <sched.h>
18 #include <sys/mman.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21
22 #include <cstdint>
23 #include <cstdlib>
24 #include <cstring>
25 #include <string>
26 #include <vector>
27
28 #include "gmock/gmock.h"
29 #include "gtest/gtest.h"
30 #include "absl/cleanup/cleanup.h"
31 #include "absl/log/check.h"
32 #include "absl/status/statusor.h"
33 #include "absl/strings/str_cat.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/types/span.h"
36 #include "sandboxed_api/testing.h"
37 #include "sandboxed_api/util/status_matchers.h"
38
39 namespace sandbox2::util {
40 namespace {
41
42 using ::sapi::GetTestSourcePath;
43 using ::sapi::IsOk;
44 using ::testing::ElementsAre;
45 using ::testing::ElementsAreArray;
46 using ::testing::Eq;
47 using ::testing::Gt;
48 using ::testing::IsEmpty;
49 using ::testing::IsTrue;
50 using ::testing::Ne;
51 using ::testing::Not;
52 using ::testing::StartsWith;
53 using ::testing::StrEq;
54
55 constexpr absl::string_view kTestString = "This is a test string";
56
TEST(UtilTest,TestCreateMemFd)57 TEST(UtilTest, TestCreateMemFd) {
58 int fd = 0;
59 ASSERT_THAT(CreateMemFd(&fd), IsTrue());
60 EXPECT_THAT(fd, Gt(1));
61 close(fd);
62 }
63
TEST(CharPtrArrayTest,FromStringVector)64 TEST(CharPtrArrayTest, FromStringVector) {
65 std::vector<std::string> strings = {"a", "b", "c"};
66 CharPtrArray array = CharPtrArray::FromStringVector(strings);
67 EXPECT_THAT(array.ToStringVector(), Eq(strings));
68 EXPECT_THAT(array.array(),
69 ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), nullptr));
70 EXPECT_THAT(array.data(), Eq(array.array().data()));
71 }
72
TEST(CharPtrArrayTest,FromCharPtrArray)73 TEST(CharPtrArrayTest, FromCharPtrArray) {
74 std::vector<std::string> strings = {"a", "b", "c"};
75 std::vector<char*> string_arr;
76 for (std::string& s : strings) {
77 string_arr.push_back(s.data());
78 }
79 string_arr.push_back(nullptr);
80 CharPtrArray array(string_arr.data());
81 EXPECT_THAT(array.ToStringVector(), Eq(strings));
82 EXPECT_THAT(array.array(),
83 ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), nullptr));
84 EXPECT_THAT(array.data(), Eq(array.array().data()));
85 }
86
TEST(GetProcStatusLineTest,Pid)87 TEST(GetProcStatusLineTest, Pid) {
88 std::string line = GetProcStatusLine(getpid(), "Pid");
89 EXPECT_THAT(line, Eq(absl::StrCat(getpid())));
90 }
91
TEST(GetProcStatusLineTest,NonExisting)92 TEST(GetProcStatusLineTest, NonExisting) {
93 std::string line =
94 GetProcStatusLine(getpid(), "__N_o_n_ExistingStatusSetting");
95 EXPECT_THAT(line, IsEmpty());
96 }
97
TEST(ForkWithFlagsTest,DoesForkNormally)98 TEST(ForkWithFlagsTest, DoesForkNormally) {
99 int pfds[2];
100 ASSERT_THAT(pipe(pfds), Eq(0));
101 pid_t child = ForkWithFlags(SIGCHLD);
102 ASSERT_THAT(child, Ne(-1));
103 if (child == 0) {
104 char c = 'a';
105 if (!write(pfds[1], &c, 1)) {
106 exit(EXIT_FAILURE);
107 }
108 exit(EXIT_SUCCESS);
109 }
110 close(pfds[1]);
111 char c = ' ';
112 EXPECT_THAT(read(pfds[0], &c, 1), Eq(1));
113 close(pfds[0]);
114 EXPECT_THAT(c, Eq('a'));
115 int status;
116 ASSERT_THAT(TEMP_FAILURE_RETRY(waitpid(child, &status, 0)), Eq(child));
117 EXPECT_TRUE(WIFEXITED(status));
118 EXPECT_THAT(WEXITSTATUS(status), Eq(0));
119 }
120
TEST(ForkWithFlagsTest,UnsupportedFlag)121 TEST(ForkWithFlagsTest, UnsupportedFlag) {
122 EXPECT_THAT(ForkWithFlags(CLONE_CHILD_CLEARTID), Eq(-1));
123 }
124
TEST(ReadCPathFromPidSplitPageTest,Normal)125 TEST(ReadCPathFromPidSplitPageTest, Normal) {
126 std::string test_str(kTestString);
127 absl::StatusOr<std::string> read =
128 ReadCPathFromPid(getpid(), reinterpret_cast<uintptr_t>(test_str.data()));
129 ASSERT_THAT(read, IsOk());
130 EXPECT_THAT(*read, Eq(kTestString));
131 }
132
TEST(ReadCPathFromPidSplitPageTest,Overlong)133 TEST(ReadCPathFromPidSplitPageTest, Overlong) {
134 std::string test_str(PATH_MAX + 1, 'a');
135 absl::StatusOr<std::string> read =
136 ReadCPathFromPid(getpid(), reinterpret_cast<uintptr_t>(test_str.data()));
137 EXPECT_THAT(read, Not(IsOk()));
138 }
139
TEST(ReadCPathFromPidSplitPageTest,SplitPage)140 TEST(ReadCPathFromPidSplitPageTest, SplitPage) {
141 const uintptr_t page_size = getpagesize();
142 char* res = reinterpret_cast<char*>(mmap(nullptr, 2 * page_size,
143 PROT_READ | PROT_WRITE,
144 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
145 ASSERT_THAT(res, Ne(MAP_FAILED));
146 absl::Cleanup cleanup = [res, page_size]() {
147 ASSERT_THAT(munmap(res, 2 * page_size), Eq(0));
148 };
149 char* str = &res[page_size - kTestString.size() / 2];
150 memcpy(str, kTestString.data(), kTestString.size());
151 absl::StatusOr<std::string> read =
152 ReadCPathFromPid(getpid(), reinterpret_cast<uintptr_t>(str));
153 ASSERT_THAT(read, IsOk());
154 EXPECT_THAT(*read, Eq(kTestString));
155 }
156
TEST(ReadCPathFromPidSplitPageTest,NearUnreadableMemory)157 TEST(ReadCPathFromPidSplitPageTest, NearUnreadableMemory) {
158 const uintptr_t page_size = getpagesize();
159 char* res = reinterpret_cast<char*>(mmap(nullptr, 2 * page_size,
160 PROT_READ | PROT_WRITE,
161 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
162 ASSERT_THAT(res, Ne(MAP_FAILED));
163 absl::Cleanup cleanup = [res, page_size]() {
164 ASSERT_THAT(munmap(res, 2 * page_size), Eq(0));
165 };
166 ASSERT_THAT(mprotect(&res[page_size], page_size, PROT_NONE), Eq(0));
167 char* str = &res[page_size - kTestString.size() - 1];
168 memcpy(str, kTestString.data(), kTestString.size());
169 absl::StatusOr<std::string> read =
170 ReadCPathFromPid(getpid(), reinterpret_cast<uintptr_t>(str));
171 ASSERT_THAT(read, IsOk());
172 EXPECT_THAT(*read, Eq(kTestString));
173 }
174
TEST(CommunitacteTest,Normal)175 TEST(CommunitacteTest, Normal) {
176 const std::string path =
177 GetTestSourcePath("sandbox2/testcases/util_communicate");
178 std::string output;
179 SAPI_ASSERT_OK_AND_ASSIGN(
180 int exit_code,
181 util::Communicate({path, "argv1", "argv2"}, {"env1", "env2"}, &output));
182 EXPECT_THAT(exit_code, Eq(0));
183 EXPECT_THAT(output, StartsWith("3\nargv1\nargv2\nenv1\nenv2\n"));
184 }
185
TEST(ReadBytesFromPidTest,Normal)186 TEST(ReadBytesFromPidTest, Normal) {
187 absl::StatusOr<std::vector<uint8_t>> read = ReadBytesFromPid(
188 getpid(), reinterpret_cast<uintptr_t>(kTestString.data()),
189 kTestString.size());
190 EXPECT_THAT(*read, ElementsAreArray(kTestString));
191 }
192
TEST(ReadBytesFromPidTest,NearUnmappedMemory)193 TEST(ReadBytesFromPidTest, NearUnmappedMemory) {
194 const uintptr_t page_size = getpagesize();
195 ASSERT_LE(kTestString.size(), page_size);
196 char* res = reinterpret_cast<char*>(mmap(nullptr, 2 * page_size,
197 PROT_READ | PROT_WRITE,
198 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
199 ASSERT_THAT(res, Ne(MAP_FAILED));
200 ASSERT_THAT(munmap(&res[page_size], page_size), Eq(0));
201 absl::Cleanup cleanup = [res, page_size]() {
202 ASSERT_THAT(munmap(res, page_size), Eq(0));
203 };
204 char* data = &res[page_size - kTestString.size()];
205 memcpy(data, kTestString.data(), kTestString.size());
206 absl::StatusOr<std::vector<uint8_t>> read = ReadBytesFromPid(
207 getpid(), reinterpret_cast<uintptr_t>(data), 2 * kTestString.size());
208 ASSERT_THAT(read, IsOk());
209 EXPECT_THAT(*read, ElementsAreArray(kTestString));
210 }
211
212 class ReadBytesFromPidIntoTest : public testing::TestWithParam<bool> {
213 protected:
Read(pid_t pid,uintptr_t ptr,absl::Span<char> data)214 absl::StatusOr<size_t> Read(pid_t pid, uintptr_t ptr, absl::Span<char> data) {
215 if (GetParam()) {
216 return internal::ReadBytesFromPidWithReadv(pid, ptr, data);
217 } else {
218 return internal::ReadBytesFromPidWithReadvInSplitChunks(pid, ptr, data);
219 }
220 }
221 };
222
TEST_P(ReadBytesFromPidIntoTest,Normal)223 TEST_P(ReadBytesFromPidIntoTest, Normal) {
224 char data[kTestString.size()] = {0};
225 absl::StatusOr<size_t> bytes_read =
226 Read(getpid(), reinterpret_cast<uintptr_t>(kTestString.data()),
227 absl::MakeSpan(data));
228 ASSERT_THAT(bytes_read, IsOk());
229 EXPECT_THAT(*bytes_read, Eq(kTestString.size()));
230 EXPECT_THAT(data, ElementsAreArray(kTestString));
231 }
232
TEST_P(ReadBytesFromPidIntoTest,SplitPage)233 TEST_P(ReadBytesFromPidIntoTest, SplitPage) {
234 const uintptr_t page_size = getpagesize();
235 ASSERT_LE(kTestString.size(), page_size);
236 char* res = reinterpret_cast<char*>(mmap(nullptr, 2 * page_size,
237 PROT_READ | PROT_WRITE,
238 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
239 ASSERT_THAT(res, Ne(MAP_FAILED));
240 absl::Cleanup cleanup = [res, page_size]() {
241 ASSERT_THAT(munmap(res, 2 * page_size), Eq(0));
242 };
243 char* data = &res[page_size - kTestString.size() / 2];
244 memcpy(data, kTestString.data(), kTestString.size());
245 char output[kTestString.size()];
246 absl::StatusOr<size_t> bytes_read =
247 Read(getpid(), reinterpret_cast<uintptr_t>(data), absl::MakeSpan(output));
248 ASSERT_THAT(bytes_read, IsOk());
249 EXPECT_THAT(*bytes_read, Eq(kTestString.size()));
250 EXPECT_THAT(output, ElementsAreArray(kTestString));
251 }
252
TEST_P(ReadBytesFromPidIntoTest,InvalidPid)253 TEST_P(ReadBytesFromPidIntoTest, InvalidPid) {
254 char data;
255 absl::StatusOr<size_t> bytes_read =
256 Read(-1, reinterpret_cast<uintptr_t>(&data), absl::MakeSpan(&data, 1));
257 ASSERT_THAT(bytes_read, Not(IsOk()));
258 }
259
TEST_P(ReadBytesFromPidIntoTest,ZeroLength)260 TEST_P(ReadBytesFromPidIntoTest, ZeroLength) {
261 char data;
262 absl::StatusOr<size_t> bytes_read = Read(
263 getpid(), reinterpret_cast<uintptr_t>(&data), absl::MakeSpan(&data, 0));
264 ASSERT_THAT(bytes_read, IsOk());
265 ASSERT_THAT(*bytes_read, Eq(0));
266 }
267
TEST_P(ReadBytesFromPidIntoTest,ZeroLengthWithInvalidPid)268 TEST_P(ReadBytesFromPidIntoTest, ZeroLengthWithInvalidPid) {
269 char data;
270 absl::StatusOr<size_t> bytes_read =
271 Read(-1, reinterpret_cast<uintptr_t>(&data), absl::MakeSpan(&data, 0));
272 ASSERT_THAT(bytes_read, IsOk());
273 ASSERT_THAT(*bytes_read, Eq(0));
274 }
275
TEST_P(ReadBytesFromPidIntoTest,UnmappedMemory)276 TEST_P(ReadBytesFromPidIntoTest, UnmappedMemory) {
277 const uintptr_t page_size = getpagesize();
278 char* res =
279 reinterpret_cast<char*>(mmap(nullptr, page_size, PROT_READ | PROT_WRITE,
280 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
281 ASSERT_THAT(res, Ne(MAP_FAILED));
282 ASSERT_THAT(munmap(res, page_size), Eq(0));
283 absl::StatusOr<size_t> bytes_read =
284 Read(getpid(), reinterpret_cast<uintptr_t>(res), absl::MakeSpan(res, 1));
285 ASSERT_THAT(bytes_read, Not(IsOk()));
286 }
287
TEST_P(ReadBytesFromPidIntoTest,NearUnmappedMemory)288 TEST_P(ReadBytesFromPidIntoTest, NearUnmappedMemory) {
289 const uintptr_t page_size = getpagesize();
290 char* res = reinterpret_cast<char*>(mmap(nullptr, 2 * page_size,
291 PROT_READ | PROT_WRITE,
292 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
293 ASSERT_THAT(res, Ne(MAP_FAILED));
294 // Unmap second page so there's a gap.
295 ASSERT_THAT(munmap(&res[page_size], page_size), Eq(0));
296 absl::Cleanup cleanup = [res, page_size]() {
297 ASSERT_THAT(munmap(res, page_size), Eq(0));
298 };
299 char* data = &res[page_size - kTestString.size() / 2];
300 memcpy(data, kTestString.data(), kTestString.size() / 2);
301 char output[kTestString.size()];
302 absl::StatusOr<size_t> bytes_read =
303 Read(getpid(), reinterpret_cast<uintptr_t>(data), absl::MakeSpan(output));
304 ASSERT_THAT(bytes_read, IsOk());
305 EXPECT_THAT(*bytes_read, Eq(kTestString.size() / 2));
306 EXPECT_THAT(absl::MakeSpan(data, kTestString.size() / 2),
307 Eq(absl::MakeSpan(kTestString.data(), kTestString.size() / 2)));
308 }
309
TEST_P(ReadBytesFromPidIntoTest,ExceedIovMax)310 TEST_P(ReadBytesFromPidIntoTest, ExceedIovMax) {
311 // Read one page past the max readable memory in one set of iovecs.
312 const uintptr_t page_size = getpagesize();
313 const size_t length = (IOV_MAX + 1) * page_size;
314 char* data = reinterpret_cast<char*>(mmap(nullptr, length + page_size,
315 PROT_READ | PROT_WRITE,
316 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
317 ASSERT_THAT(data, Ne(MAP_FAILED));
318 // Unmap second page so there's a gap.
319 ASSERT_THAT(munmap(&data[length], page_size), Eq(0));
320 absl::Cleanup cleanup = [data, length]() {
321 ASSERT_THAT(munmap(data, length), Eq(0));
322 };
323 memset(data, 0x0e, length);
324 std::vector<char> output(length);
325 absl::StatusOr<size_t> bytes_read =
326 Read(getpid(), reinterpret_cast<uintptr_t>(data),
327 absl::MakeSpan(output.data(), length));
328 ASSERT_THAT(bytes_read, IsOk());
329 EXPECT_THAT(*bytes_read, Eq(length));
330 EXPECT_THAT(output, ElementsAreArray(data, length));
331 }
332
333 INSTANTIATE_TEST_SUITE_P(ReadBytesFromPidInto, ReadBytesFromPidIntoTest,
334 testing::Values(true, false));
335
336 class WriteBytesToPidFromTest : public testing::TestWithParam<bool> {
337 protected:
Write(pid_t pid,uintptr_t ptr,absl::Span<const char> data)338 absl::StatusOr<size_t> Write(pid_t pid, uintptr_t ptr,
339 absl::Span<const char> data) {
340 if (GetParam()) {
341 return internal::WriteBytesToPidWithWritev(pid, ptr, data);
342 } else {
343 return internal::WriteBytesToPidWithProcMem(pid, ptr, data);
344 }
345 };
346 };
347
TEST_P(WriteBytesToPidFromTest,Normal)348 TEST_P(WriteBytesToPidFromTest, Normal) {
349 char data[kTestString.size()] = {0};
350 absl::StatusOr<size_t> bytes_written =
351 Write(getpid(), reinterpret_cast<uintptr_t>(data), kTestString);
352 ASSERT_THAT(bytes_written, IsOk());
353 EXPECT_THAT(*bytes_written, Eq(kTestString.size()));
354 EXPECT_THAT(data, ElementsAreArray(kTestString));
355 }
356
TEST_P(WriteBytesToPidFromTest,SplitPage)357 TEST_P(WriteBytesToPidFromTest, SplitPage) {
358 const uintptr_t page_size = getpagesize();
359 ASSERT_LE(kTestString.size(), page_size);
360 char* res = reinterpret_cast<char*>(mmap(nullptr, 2 * page_size,
361 PROT_READ | PROT_WRITE,
362 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
363 ASSERT_THAT(res, Ne(MAP_FAILED));
364 absl::Cleanup cleanup = [res, page_size]() {
365 ASSERT_THAT(munmap(res, 2 * page_size), Eq(0));
366 };
367 char* data = &res[page_size - kTestString.size() / 2];
368 absl::StatusOr<size_t> bytes_written =
369 Write(getpid(), reinterpret_cast<uintptr_t>(data), kTestString);
370 ASSERT_THAT(bytes_written, IsOk());
371 EXPECT_THAT(*bytes_written, Eq(kTestString.size()));
372 EXPECT_THAT(kTestString, ElementsAreArray(data, kTestString.size()));
373 }
374
TEST_P(WriteBytesToPidFromTest,InvalidPid)375 TEST_P(WriteBytesToPidFromTest, InvalidPid) {
376 char data = 0;
377 absl::StatusOr<size_t> bytes_written =
378 Write(-1, reinterpret_cast<uintptr_t>(&data), absl::MakeSpan(&data, 1));
379 ASSERT_THAT(bytes_written, Not(IsOk()));
380 }
381
TEST_P(WriteBytesToPidFromTest,ZeroLength)382 TEST_P(WriteBytesToPidFromTest, ZeroLength) {
383 char data = 0;
384 absl::StatusOr<size_t> bytes_written = Write(
385 getpid(), reinterpret_cast<uintptr_t>(&data), absl::MakeSpan(&data, 0));
386 ASSERT_THAT(bytes_written, IsOk());
387 ASSERT_THAT(*bytes_written, Eq(0));
388 }
389
TEST_P(WriteBytesToPidFromTest,ZeroLengthWithInvalidPid)390 TEST_P(WriteBytesToPidFromTest, ZeroLengthWithInvalidPid) {
391 char data = 0;
392 absl::StatusOr<size_t> bytes_written =
393 Write(-1, reinterpret_cast<uintptr_t>(&data), absl::MakeSpan(&data, 0));
394 ASSERT_THAT(bytes_written, IsOk());
395 ASSERT_THAT(*bytes_written, Eq(0));
396 }
397
TEST_P(WriteBytesToPidFromTest,NearUnmappedMemory)398 TEST_P(WriteBytesToPidFromTest, NearUnmappedMemory) {
399 const uintptr_t page_size = getpagesize();
400 char* res = reinterpret_cast<char*>(mmap(nullptr, 2 * page_size,
401 PROT_READ | PROT_WRITE,
402 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
403 ASSERT_THAT(res, Ne(MAP_FAILED));
404 ASSERT_THAT(munmap(&res[page_size], page_size), Eq(0));
405 absl::Cleanup cleanup = [res, page_size]() {
406 ASSERT_THAT(munmap(res, page_size), Eq(0));
407 };
408 char* data = &res[page_size - kTestString.size() / 2];
409 absl::StatusOr<size_t> bytes_written =
410 Write(getpid(), reinterpret_cast<uintptr_t>(data), kTestString);
411 ASSERT_THAT(bytes_written, IsOk());
412 EXPECT_THAT(*bytes_written, Eq(kTestString.size() / 2));
413 EXPECT_THAT(absl::MakeSpan(data, kTestString.size() / 2),
414 Eq(absl::MakeSpan(kTestString.data(), kTestString.size() / 2)));
415 }
416
TEST_P(WriteBytesToPidFromTest,ExceedIovMax)417 TEST_P(WriteBytesToPidFromTest, ExceedIovMax) {
418 const uintptr_t page_size = getpagesize();
419 const size_t length = (IOV_MAX + 1) * page_size;
420 char* data = reinterpret_cast<char*>(mmap(nullptr, length + page_size,
421 PROT_READ | PROT_WRITE,
422 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0));
423 ASSERT_THAT(data, Ne(MAP_FAILED));
424 // Unmap second page so there's a gap.
425 ASSERT_THAT(munmap(&data[length], page_size), Eq(0));
426 absl::Cleanup cleanup = [data, length]() {
427 ASSERT_THAT(munmap(data, length), Eq(0));
428 };
429 memset(data, 0, length);
430 const std::vector<char> src(length, 0x0e);
431 absl::StatusOr<size_t> bytes_written =
432 Write(getpid(), reinterpret_cast<uintptr_t>(data),
433 absl::MakeSpan(src.data(), length));
434 ASSERT_THAT(bytes_written, IsOk());
435 EXPECT_THAT(*bytes_written, Eq(length));
436 EXPECT_THAT(src, ElementsAreArray(data, length));
437 }
438
439 INSTANTIATE_TEST_SUITE_P(WriteBytesToPidFrom, WriteBytesToPidFromTest,
440 testing::Values(true, false));
441
442 } // namespace
443 } // namespace sandbox2::util
444