blob: 0e77633aa2d49420da9507022dcf3a4e211da6a3 [file] [log] [blame]
Jiakai Zhang2b905c32022-07-20 15:49:34 +01001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <sys/capability.h>
18#include <sys/resource.h>
19#include <sys/types.h>
20#include <sys/wait.h>
21#include <unistd.h>
22
23#include <csignal>
24#include <filesystem>
25#include <functional>
26#include <string>
27#include <utility>
28
29#include "android-base/file.h"
30#include "android-base/logging.h"
31#include "android-base/scopeguard.h"
Jiakai Zhang7b6aef72022-12-12 19:30:44 +000032#include "android-base/strings.h"
Jiakai Zhang2b905c32022-07-20 15:49:34 +010033#include "base/common_art_test.h"
34#include "base/file_utils.h"
35#include "base/globals.h"
36#include "base/macros.h"
37#include "base/os.h"
38#include "base/scoped_cap.h"
39#include "exec_utils.h"
40#include "fmt/format.h"
41#include "gmock/gmock.h"
42#include "gtest/gtest.h"
43#include "system/thread_defs.h"
44
45namespace art {
46namespace {
47
48using ::android::base::make_scope_guard;
49using ::android::base::ScopeGuard;
Jiakai Zhang7b6aef72022-12-12 19:30:44 +000050using ::android::base::Split;
Jiakai Zhang7b6aef72022-12-12 19:30:44 +000051using ::testing::Contains;
Jiakai Zhang7bc7ffe2023-02-02 00:17:04 +080052using ::testing::ElementsAre;
Jiakai Zhang2b905c32022-07-20 15:49:34 +010053using ::testing::HasSubstr;
Jiakai Zhang7b6aef72022-12-12 19:30:44 +000054using ::testing::Not;
Jiakai Zhang2b905c32022-07-20 15:49:34 +010055
56// clang-tidy incorrectly complaints about the using declaration while the user-defined literal is
57// actually being used.
58using ::fmt::literals::operator""_format; // NOLINT
59
60constexpr uid_t kRoot = 0;
61constexpr uid_t kNobody = 9999;
62
Jiakai Zhangd49058e2023-01-10 10:55:31 +000063// This test executes a few Linux system commands such as "ls", which are linked against system
64// libraries. In many ART gtests we set LD_LIBRARY_PATH to make the test binaries link to libraries
65// from the ART module first, and if that setting is propagated to the system commands they may also
66// try to link to those libraries instead of the system ones they are built against. This is
67// particularly noticeable when 32-bit tests run on a 64-bit system. Hence we need to set
68// LD_LIBRARY_PATH to an empty string here.
69// TODO(b/247108425): Remove this when ART gtests no longer use LD_LIBRARY_PATH.
70constexpr const char* kEmptyLdLibraryPath = "--env=LD_LIBRARY_PATH=";
71
Jiakai Zhang2b905c32022-07-20 15:49:34 +010072std::string GetArtBin(const std::string& name) { return "{}/bin/{}"_format(GetArtRoot(), name); }
73
74std::string GetBin(const std::string& name) { return "{}/bin/{}"_format(GetAndroidRoot(), name); }
75
76// Executes the command, waits for it to finish, and keeps it in a waitable state until the current
77// scope exits.
78std::pair<pid_t, ScopeGuard<std::function<void()>>> ScopedExecAndWait(
79 std::vector<std::string>& args) {
80 std::vector<char*> execv_args;
81 execv_args.reserve(args.size() + 1);
82 for (std::string& arg : args) {
83 execv_args.push_back(arg.data());
84 }
85 execv_args.push_back(nullptr);
86
87 pid_t pid = fork();
88 if (pid == 0) {
89 execv(execv_args[0], execv_args.data());
90 UNREACHABLE();
91 } else if (pid > 0) {
92 siginfo_t info;
93 CHECK_EQ(TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED | WNOWAIT)), 0);
94 CHECK_EQ(info.si_code, CLD_EXITED);
95 CHECK_EQ(info.si_status, 0);
96 std::function<void()> cleanup([=] {
97 siginfo_t info;
98 CHECK_EQ(TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)), 0);
99 });
100 return std::make_pair(pid, make_scope_guard(std::move(cleanup)));
101 } else {
102 LOG(FATAL) << "Failed to call fork";
103 UNREACHABLE();
104 }
105}
106
107// Grants the current process the given root capability.
108void SetCap(cap_flag_t flag, cap_value_t value) {
109 ScopedCap cap(cap_get_proc());
110 CHECK_NE(cap.Get(), nullptr);
111 cap_value_t caps[]{value};
112 CHECK_EQ(cap_set_flag(cap.Get(), flag, /*ncap=*/1, caps, CAP_SET), 0);
113 CHECK_EQ(cap_set_proc(cap.Get()), 0);
114}
115
116// Returns true if the given process has the given root capability.
117bool GetCap(pid_t pid, cap_flag_t flag, cap_value_t value) {
118 ScopedCap cap(cap_get_pid(pid));
119 CHECK_NE(cap.Get(), nullptr);
120 cap_flag_value_t flag_value;
121 CHECK_EQ(cap_get_flag(cap.Get(), value, flag, &flag_value), 0);
122 return flag_value == CAP_SET;
123}
124
125class ArtExecTest : public testing::Test {
126 protected:
127 void SetUp() override {
128 testing::Test::SetUp();
129 if (!kIsTargetAndroid) {
130 GTEST_SKIP() << "art_exec is for device only";
131 }
132 if (getuid() != kRoot) {
133 GTEST_SKIP() << "art_exec requires root";
134 }
135 art_exec_bin_ = GetArtBin("art_exec");
136 }
137
138 std::string art_exec_bin_;
139};
140
141TEST_F(ArtExecTest, Command) {
142 std::string error_msg;
143 int ret = ExecAndReturnCode({art_exec_bin_, "--", GetBin("sh"), "-c", "exit 123"}, &error_msg);
144 ASSERT_EQ(ret, 123) << error_msg;
145}
146
147TEST_F(ArtExecTest, SetTaskProfiles) {
148 std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
149 ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
150 ASSERT_GE(scratch_file.GetFd(), 0);
151
152 std::vector<std::string> args{art_exec_bin_,
Jiakai Zhangc4330772022-08-01 19:41:24 +0100153 "--set-task-profile=ProcessCapacityHigh",
Jiakai Zhangd49058e2023-01-10 10:55:31 +0000154 kEmptyLdLibraryPath,
Jiakai Zhang2b905c32022-07-20 15:49:34 +0100155 "--",
156 GetBin("sh"),
157 "-c",
158 "cat /proc/self/cgroup > " + filename};
159 auto [pid, scope_guard] = ScopedExecAndWait(args);
160 std::string cgroup;
161 ASSERT_TRUE(android::base::ReadFileToString(filename, &cgroup));
Jiakai Zhangc4330772022-08-01 19:41:24 +0100162 EXPECT_THAT(cgroup, HasSubstr(":cpuset:/foreground\n"));
Jiakai Zhang2b905c32022-07-20 15:49:34 +0100163}
164
165TEST_F(ArtExecTest, SetPriority) {
Jiakai Zhangd49058e2023-01-10 10:55:31 +0000166 std::vector<std::string> args{
167 art_exec_bin_, "--set-priority=background", kEmptyLdLibraryPath, "--", GetBin("true")};
Jiakai Zhang2b905c32022-07-20 15:49:34 +0100168 auto [pid, scope_guard] = ScopedExecAndWait(args);
169 EXPECT_EQ(getpriority(PRIO_PROCESS, pid), ANDROID_PRIORITY_BACKGROUND);
170}
171
172TEST_F(ArtExecTest, DropCapabilities) {
173 // Switch to a non-root user, but still keep the CAP_FOWNER capability available and inheritable.
174 // The order of the following calls matters.
175 CHECK_EQ(cap_setuid(kNobody), 0);
176 SetCap(CAP_INHERITABLE, CAP_FOWNER);
177 SetCap(CAP_EFFECTIVE, CAP_FOWNER);
178 ASSERT_EQ(cap_set_ambient(CAP_FOWNER, CAP_SET), 0);
179
180 // Make sure the test is set up correctly (i.e., the child process should normally have the
181 // inherited root capability: CAP_FOWNER).
182 {
Jiakai Zhangd49058e2023-01-10 10:55:31 +0000183 std::vector<std::string> args{art_exec_bin_, kEmptyLdLibraryPath, "--", GetBin("true")};
Jiakai Zhang2b905c32022-07-20 15:49:34 +0100184 auto [pid, scope_guard] = ScopedExecAndWait(args);
185 ASSERT_TRUE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER));
186 }
187
188 {
Jiakai Zhangd49058e2023-01-10 10:55:31 +0000189 std::vector<std::string> args{
190 art_exec_bin_, "--drop-capabilities", kEmptyLdLibraryPath, "--", GetBin("true")};
Jiakai Zhang2b905c32022-07-20 15:49:34 +0100191 auto [pid, scope_guard] = ScopedExecAndWait(args);
192 EXPECT_FALSE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER));
193 }
194}
195
Jiakai Zhang7b6aef72022-12-12 19:30:44 +0000196TEST_F(ArtExecTest, CloseFds) {
197 std::unique_ptr<File> file1(OS::OpenFileForReading("/dev/zero"));
198 std::unique_ptr<File> file2(OS::OpenFileForReading("/dev/zero"));
199 std::unique_ptr<File> file3(OS::OpenFileForReading("/dev/zero"));
200 ASSERT_NE(file1, nullptr);
201 ASSERT_NE(file2, nullptr);
202 ASSERT_NE(file3, nullptr);
203
204 std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
205 ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
206 ASSERT_GE(scratch_file.GetFd(), 0);
207
208 std::vector<std::string> args{art_exec_bin_,
209 "--keep-fds={}:{}"_format(file3->Fd(), file2->Fd()),
Jiakai Zhangd49058e2023-01-10 10:55:31 +0000210 kEmptyLdLibraryPath,
Jiakai Zhang7b6aef72022-12-12 19:30:44 +0000211 "--",
212 GetBin("sh"),
213 "-c",
Jiakai Zhang7bc7ffe2023-02-02 00:17:04 +0800214 "("
215 "readlink /proc/self/fd/{} || echo;"
216 "readlink /proc/self/fd/{} || echo;"
217 "readlink /proc/self/fd/{} || echo;"
218 ") > {}"_format(file1->Fd(), file2->Fd(), file3->Fd(), filename)};
Jiakai Zhang7b6aef72022-12-12 19:30:44 +0000219
Jiakai Zhangd49058e2023-01-10 10:55:31 +0000220 ScopedExecAndWait(args);
Jiakai Zhang7b6aef72022-12-12 19:30:44 +0000221
222 std::string open_fds;
223 ASSERT_TRUE(android::base::ReadFileToString(filename, &open_fds));
224
Jiakai Zhang7bc7ffe2023-02-02 00:17:04 +0800225 // `file1` should be closed, while the other two should be open. There's a blank line at the end.
226 EXPECT_THAT(Split(open_fds, "\n"), ElementsAre(Not("/dev/zero"), "/dev/zero", "/dev/zero", ""));
Jiakai Zhang7b6aef72022-12-12 19:30:44 +0000227}
228
Jiakai Zhangd49058e2023-01-10 10:55:31 +0000229TEST_F(ArtExecTest, Env) {
230 std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
231 ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
232 ASSERT_GE(scratch_file.GetFd(), 0);
233
234 std::vector<std::string> args{art_exec_bin_,
235 "--env=FOO=BAR",
236 kEmptyLdLibraryPath,
237 "--",
238 GetBin("sh"),
239 "-c",
240 "env > " + filename};
241
242 ScopedExecAndWait(args);
243
244 std::string envs;
245 ASSERT_TRUE(android::base::ReadFileToString(filename, &envs));
246
247 EXPECT_THAT(Split(envs, "\n"), Contains("FOO=BAR"));
248}
249
Jiakai Zhang2b905c32022-07-20 15:49:34 +0100250} // namespace
251} // namespace art