1 /*
2 * Copyright (C) 2017 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 "service.h"
18
19 #include <algorithm>
20 #include <fstream>
21 #include <memory>
22 #include <type_traits>
23 #include <vector>
24
25 #include <gtest/gtest.h>
26
27 #include <android-base/file.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <selinux/selinux.h>
31 #include <sys/signalfd.h>
32 #include "lmkd_service.h"
33 #include "reboot.h"
34 #include "service.h"
35 #include "service_list.h"
36 #include "service_parser.h"
37 #include "util.h"
38
39 using ::android::base::ReadFileToString;
40 using ::android::base::StringPrintf;
41 using ::android::base::StringReplace;
42 using ::android::base::unique_fd;
43 using ::android::base::WriteStringToFd;
44 using ::android::base::WriteStringToFile;
45
46 namespace android {
47 namespace init {
48
GetSecurityContext()49 static std::string GetSecurityContext() {
50 char* ctx;
51 if (getcon(&ctx) == -1) {
52 ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
53 }
54 std::string result{ctx};
55 freecon(ctx);
56 return result;
57 }
58
TEST(service,pod_initialized)59 TEST(service, pod_initialized) {
60 constexpr auto memory_size = sizeof(Service);
61 alignas(alignof(Service)) unsigned char old_memory[memory_size];
62
63 for (std::size_t i = 0; i < memory_size; ++i) {
64 old_memory[i] = 0xFF;
65 }
66
67 std::vector<std::string> dummy_args{"/bin/test"};
68 Service* service_in_old_memory =
69 new (old_memory) Service("test_old_memory", nullptr, /*filename=*/"", dummy_args);
70
71 EXPECT_EQ(0U, service_in_old_memory->flags());
72 EXPECT_EQ(0, service_in_old_memory->pid());
73 EXPECT_EQ(0, service_in_old_memory->crash_count());
74 EXPECT_EQ(0U, service_in_old_memory->uid());
75 EXPECT_EQ(0U, service_in_old_memory->gid());
76 EXPECT_EQ(0, service_in_old_memory->namespace_flags());
77 EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
78 EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
79 EXPECT_EQ(0, service_in_old_memory->priority());
80 EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory->oom_score_adjust());
81 EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
82
83 for (std::size_t i = 0; i < memory_size; ++i) {
84 old_memory[i] = 0xFF;
85 }
86
87 Service* service_in_old_memory2 = new (old_memory) Service(
88 "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "",
89 nullptr, /*filename=*/"", dummy_args);
90
91 EXPECT_EQ(0U, service_in_old_memory2->flags());
92 EXPECT_EQ(0, service_in_old_memory2->pid());
93 EXPECT_EQ(0, service_in_old_memory2->crash_count());
94 EXPECT_EQ(0U, service_in_old_memory2->uid());
95 EXPECT_EQ(0U, service_in_old_memory2->gid());
96 EXPECT_EQ(0, service_in_old_memory2->namespace_flags());
97 EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
98 EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
99 EXPECT_EQ(0, service_in_old_memory2->priority());
100 EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory2->oom_score_adjust());
101 EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
102 }
103
TEST(service,make_temporary_oneshot_service_invalid_syntax)104 TEST(service, make_temporary_oneshot_service_invalid_syntax) {
105 std::vector<std::string> args;
106 // Nothing.
107 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
108
109 // No arguments to 'exec'.
110 args.push_back("exec");
111 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
112
113 // No command in "exec --".
114 args.push_back("--");
115 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
116 }
117
TEST(service,make_temporary_oneshot_service_too_many_supplementary_gids)118 TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
119 std::vector<std::string> args;
120 args.push_back("exec");
121 args.push_back("seclabel");
122 args.push_back("root"); // uid.
123 args.push_back("root"); // gid.
124 for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
125 args.push_back("root"); // Supplementary gid.
126 }
127 args.push_back("--");
128 args.push_back("/system/bin/id");
129 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
130 }
131
Test_make_temporary_oneshot_service(bool dash_dash,bool seclabel,bool uid,bool gid,bool supplementary_gids)132 static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
133 bool supplementary_gids) {
134 std::vector<std::string> args;
135 args.push_back("exec");
136 if (seclabel) {
137 args.push_back("u:r:su:s0"); // seclabel
138 if (uid) {
139 args.push_back("log"); // uid
140 if (gid) {
141 args.push_back("shell"); // gid
142 if (supplementary_gids) {
143 args.push_back("system"); // supplementary gid 0
144 args.push_back("adb"); // supplementary gid 1
145 }
146 }
147 }
148 }
149 if (dash_dash) {
150 args.push_back("--");
151 }
152 args.push_back("/system/bin/toybox");
153 args.push_back("id");
154 auto service_ret = Service::MakeTemporaryOneshotService(args);
155 ASSERT_RESULT_OK(service_ret);
156 auto svc = std::move(*service_ret);
157
158 if (seclabel) {
159 ASSERT_EQ("u:r:su:s0", svc->seclabel());
160 } else {
161 ASSERT_EQ("", svc->seclabel());
162 }
163 if (uid) {
164 auto decoded_uid = DecodeUid("log");
165 ASSERT_RESULT_OK(decoded_uid);
166 ASSERT_EQ(*decoded_uid, svc->uid());
167 } else {
168 ASSERT_EQ(0U, svc->uid());
169 }
170 if (gid) {
171 auto decoded_uid = DecodeUid("shell");
172 ASSERT_RESULT_OK(decoded_uid);
173 ASSERT_EQ(*decoded_uid, svc->gid());
174 } else {
175 ASSERT_EQ(0U, svc->gid());
176 }
177 if (supplementary_gids) {
178 ASSERT_EQ(2U, svc->supp_gids().size());
179
180 auto decoded_uid = DecodeUid("system");
181 ASSERT_RESULT_OK(decoded_uid);
182 ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
183
184 decoded_uid = DecodeUid("adb");
185 ASSERT_RESULT_OK(decoded_uid);
186 ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
187 } else {
188 ASSERT_EQ(0U, svc->supp_gids().size());
189 }
190
191 ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
192 ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
193 ASSERT_EQ("id", svc->args()[1]);
194 }
195
TEST(service,make_temporary_oneshot_service_with_everything)196 TEST(service, make_temporary_oneshot_service_with_everything) {
197 Test_make_temporary_oneshot_service(true, true, true, true, true);
198 }
199
TEST(service,make_temporary_oneshot_service_with_seclabel_uid_gid)200 TEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {
201 Test_make_temporary_oneshot_service(true, true, true, true, false);
202 }
203
TEST(service,make_temporary_oneshot_service_with_seclabel_uid)204 TEST(service, make_temporary_oneshot_service_with_seclabel_uid) {
205 Test_make_temporary_oneshot_service(true, true, true, false, false);
206 }
207
TEST(service,make_temporary_oneshot_service_with_seclabel)208 TEST(service, make_temporary_oneshot_service_with_seclabel) {
209 Test_make_temporary_oneshot_service(true, true, false, false, false);
210 }
211
TEST(service,make_temporary_oneshot_service_with_just_command)212 TEST(service, make_temporary_oneshot_service_with_just_command) {
213 Test_make_temporary_oneshot_service(true, false, false, false, false);
214 }
215
TEST(service,make_temporary_oneshot_service_with_just_command_no_dash)216 TEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {
217 Test_make_temporary_oneshot_service(false, false, false, false, false);
218 }
219
220 // Returns the path in the v2 cgroup hierarchy for a given process in the format /uid_%d/pid_%d.
CgroupPath(pid_t pid)221 static std::string CgroupPath(pid_t pid) {
222 std::string cgroup_path = StringPrintf("/proc/%d/cgroup", pid);
223 std::ifstream is(cgroup_path, std::ios::in);
224 std::string line;
225 while (std::getline(is, line)) {
226 if (line.substr(0, 3) == "0::") {
227 return line.substr(3);
228 }
229 }
230 return {};
231 }
232
233 class ServiceStopTest : public testing::TestWithParam<bool> {};
234
235 // Before November 2023, processes that were migrated to another v2 cgroup were ignored by
236 // Service::Stop() if their uid_%d/pid_%d cgroup directory got removed. This test, if run with the
237 // parameter set to 'true', verifies that such services are stopped.
TEST_P(ServiceStopTest,stop)238 TEST_P(ServiceStopTest, stop) {
239 if (getuid() != 0) {
240 GTEST_SKIP() << "Must be run as root.";
241 return;
242 }
243
244 static constexpr std::string_view kServiceName = "ServiceA";
245 static constexpr std::string_view kScriptTemplate = R"init(
246 service $name /system/bin/yes
247 user shell
248 group shell
249 seclabel $selabel
250 )init";
251
252 std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", kServiceName, false),
253 "$selabel", GetSecurityContext(), false);
254 ServiceList& service_list = ServiceList::GetInstance();
255 Parser parser;
256 parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
257
258 TemporaryFile tf;
259 ASSERT_GE(tf.fd, 0);
260 ASSERT_TRUE(WriteStringToFd(script, tf.fd));
261 ASSERT_TRUE(parser.ParseConfig(tf.path));
262
263 Service* const service = ServiceList::GetInstance().FindService(kServiceName);
264 ASSERT_NE(service, nullptr);
265 ASSERT_RESULT_OK(service->Start());
266 ASSERT_TRUE(service->IsRunning());
267 if (GetParam()) {
268 const pid_t pid = service->pid();
269 const std::string cgroup_path = CgroupPath(pid);
270 EXPECT_NE(cgroup_path, "");
271 EXPECT_NE(cgroup_path, "/");
272 const std::string pid_str = std::to_string(pid);
273 EXPECT_TRUE(WriteStringToFile(pid_str, "/sys/fs/cgroup/cgroup.procs"));
274 EXPECT_EQ(CgroupPath(pid), "/");
275 EXPECT_EQ(rmdir(("/sys/fs/cgroup" + cgroup_path).c_str()), 0);
276 }
277 EXPECT_EQ(0, StopServicesAndLogViolations({service->name()}, 10s, /*terminate=*/true));
278 ServiceList::GetInstance().RemoveService(*service);
279 }
280
281 INSTANTIATE_TEST_SUITE_P(service, ServiceStopTest, testing::Values(false, true));
282
283 } // namespace init
284 } // namespace android
285