• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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