1 /*
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
21 #include <string>
22
23 #include "android-base/file.h"
24 #include "android-base/logging.h"
25 #include "android-base/strings.h"
26 #include "base/common_art_test.h"
27 #include "base/globals.h"
28 #include "base/macros.h"
29 #include "base/os.h"
30 #include "base/scoped_cap.h"
31 #include "exec_utils.h"
32 #include "gmock/gmock.h"
33 #include "gtest/gtest.h"
34 #include "system/thread_defs.h"
35 #include "testing.h"
36
37 #ifdef ART_TARGET_ANDROID
38 #include "android-modules-utils/sdk_level.h"
39 #endif
40
41 namespace art {
42 namespace tools {
43 namespace {
44
45 using ::android::base::Split;
46 using ::testing::Contains;
47 using ::testing::ElementsAre;
48 using ::testing::HasSubstr;
49 using ::testing::Not;
50
51 constexpr uid_t kRoot = 0;
52 constexpr uid_t kNobody = 9999;
53
54 // Grants the current process the given root capability.
SetCap(cap_flag_t flag,cap_value_t value)55 void SetCap(cap_flag_t flag, cap_value_t value) {
56 ScopedCap cap(cap_get_proc());
57 CHECK_NE(cap.Get(), nullptr);
58 cap_value_t caps[]{value};
59 CHECK_EQ(cap_set_flag(cap.Get(), flag, /*ncap=*/1, caps, CAP_SET), 0);
60 CHECK_EQ(cap_set_proc(cap.Get()), 0);
61 }
62
63 // Returns true if the given process has the given root capability.
GetCap(pid_t pid,cap_flag_t flag,cap_value_t value)64 bool GetCap(pid_t pid, cap_flag_t flag, cap_value_t value) {
65 ScopedCap cap(cap_get_pid(pid));
66 CHECK_NE(cap.Get(), nullptr);
67 cap_flag_value_t flag_value;
68 CHECK_EQ(cap_get_flag(cap.Get(), value, flag, &flag_value), 0);
69 return flag_value == CAP_SET;
70 }
71
72 class ArtExecTest : public testing::Test {
73 protected:
SetUp()74 void SetUp() override {
75 testing::Test::SetUp();
76 if (!kIsTargetAndroid) {
77 GTEST_SKIP() << "art_exec is for device only";
78 }
79 if (getuid() != kRoot) {
80 GTEST_SKIP() << "art_exec requires root";
81 }
82 art_exec_bin_ = GetArtBin("art_exec");
83 }
84
85 std::string art_exec_bin_;
86 };
87
TEST_F(ArtExecTest,Command)88 TEST_F(ArtExecTest, Command) {
89 std::string error_msg;
90 int ret = ExecAndReturnCode({art_exec_bin_, "--", GetBin("sh"), "-c", "exit 123"}, &error_msg);
91 ASSERT_EQ(ret, 123) << error_msg;
92 }
93
TEST_F(ArtExecTest,SetTaskProfiles)94 TEST_F(ArtExecTest, SetTaskProfiles) {
95 // The condition is always true because ArtExecTest is run on device only.
96 #ifdef ART_TARGET_ANDROID
97 if (!android::modules::sdklevel::IsAtLeastU()) {
98 GTEST_SKIP() << "This test depends on a libartpalette API that is only available on U+";
99 }
100 #endif
101
102 std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
103 ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
104 ASSERT_GE(scratch_file.GetFd(), 0);
105
106 std::vector<std::string> args{art_exec_bin_,
107 "--set-task-profile=ProcessCapacityHigh",
108 "--",
109 GetBin("sh"),
110 "-c",
111 "cat /proc/self/cgroup > " + filename};
112 auto [pid, scope_guard] = ScopedExec(args, /*wait=*/true);
113 std::string cgroup;
114 ASSERT_TRUE(android::base::ReadFileToString(filename, &cgroup));
115 EXPECT_THAT(cgroup, HasSubstr(":cpuset:/foreground\n"));
116 }
117
TEST_F(ArtExecTest,SetPriority)118 TEST_F(ArtExecTest, SetPriority) {
119 std::vector<std::string> args{art_exec_bin_, "--set-priority=background", "--", GetBin("true")};
120 auto [pid, scope_guard] = ScopedExec(args, /*wait=*/true);
121 EXPECT_EQ(getpriority(PRIO_PROCESS, pid), ANDROID_PRIORITY_BACKGROUND);
122 }
123
TEST_F(ArtExecTest,DropCapabilities)124 TEST_F(ArtExecTest, DropCapabilities) {
125 // Switch to a non-root user, but still keep the CAP_FOWNER capability available and inheritable.
126 // The order of the following calls matters.
127 CHECK_EQ(cap_setuid(kNobody), 0);
128 SetCap(CAP_INHERITABLE, CAP_FOWNER);
129 SetCap(CAP_EFFECTIVE, CAP_FOWNER);
130 ASSERT_EQ(cap_set_ambient(CAP_FOWNER, CAP_SET), 0);
131
132 // Make sure the test is set up correctly (i.e., the child process should normally have the
133 // inherited root capability: CAP_FOWNER).
134 {
135 std::vector<std::string> args{art_exec_bin_, "--", GetBin("true")};
136 auto [pid, scope_guard] = ScopedExec(args, /*wait=*/true);
137 ASSERT_TRUE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER));
138 }
139
140 {
141 std::vector<std::string> args{art_exec_bin_, "--drop-capabilities", "--", GetBin("true")};
142 auto [pid, scope_guard] = ScopedExec(args, /*wait=*/true);
143 EXPECT_FALSE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER));
144 }
145 }
146
TEST_F(ArtExecTest,CloseFds)147 TEST_F(ArtExecTest, CloseFds) {
148 std::unique_ptr<File> file1(OS::OpenFileForReading("/dev/zero"));
149 std::unique_ptr<File> file2(OS::OpenFileForReading("/dev/zero"));
150 std::unique_ptr<File> file3(OS::OpenFileForReading("/dev/zero"));
151 ASSERT_NE(file1, nullptr);
152 ASSERT_NE(file2, nullptr);
153 ASSERT_NE(file3, nullptr);
154
155 std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
156 ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
157 ASSERT_GE(scratch_file.GetFd(), 0);
158
159 std::vector<std::string> args{art_exec_bin_,
160 ART_FORMAT("--keep-fds={}:{}", file3->Fd(), file2->Fd()),
161 "--",
162 GetBin("sh"),
163 "-c",
164 ART_FORMAT("("
165 "readlink /proc/self/fd/{} || echo;"
166 "readlink /proc/self/fd/{} || echo;"
167 "readlink /proc/self/fd/{} || echo;"
168 ") > {}",
169 file1->Fd(),
170 file2->Fd(),
171 file3->Fd(),
172 filename)};
173
174 ScopedExec(args, /*wait=*/true);
175
176 std::string open_fds;
177 ASSERT_TRUE(android::base::ReadFileToString(filename, &open_fds));
178
179 // `file1` should be closed, while the other two should be open. There's a blank line at the end.
180 EXPECT_THAT(Split(open_fds, "\n"), ElementsAre(Not("/dev/zero"), "/dev/zero", "/dev/zero", ""));
181 }
182
TEST_F(ArtExecTest,Env)183 TEST_F(ArtExecTest, Env) {
184 std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
185 ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
186 ASSERT_GE(scratch_file.GetFd(), 0);
187
188 std::vector<std::string> args{
189 art_exec_bin_, "--env=FOO=BAR", "--", GetBin("sh"), "-c", "env > " + filename};
190
191 ScopedExec(args, /*wait=*/true);
192
193 std::string envs;
194 ASSERT_TRUE(android::base::ReadFileToString(filename, &envs));
195
196 EXPECT_THAT(Split(envs, "\n"), Contains("FOO=BAR"));
197 }
198
TEST_F(ArtExecTest,ProcessNameSuffix)199 TEST_F(ArtExecTest, ProcessNameSuffix) {
200 std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
201 ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
202 ASSERT_GE(scratch_file.GetFd(), 0);
203
204 std::vector<std::string> args{art_exec_bin_,
205 "--process-name-suffix=my suffix",
206 "--",
207 GetBin("toybox"),
208 "cp",
209 "/proc/self/cmdline",
210 filename};
211
212 ScopedExec(args, /*wait=*/true);
213
214 std::string cmdline;
215 ASSERT_TRUE(android::base::ReadFileToString(filename, &cmdline));
216
217 EXPECT_THAT(cmdline, HasSubstr("toybox (my suffix)\0"));
218 }
219
220 } // namespace
221 } // namespace tools
222 } // namespace art
223