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 <fstab/fstab.h>
31 #include <selinux/selinux.h>
32 #include <sys/signalfd.h>
33 #include "lmkd_service.h"
34 #include "reboot.h"
35 #include "service.h"
36 #include "service_list.h"
37 #include "service_parser.h"
38 #include "util.h"
39
40 using ::android::base::ReadFileToString;
41 using ::android::base::StringPrintf;
42 using ::android::base::StringReplace;
43 using ::android::base::unique_fd;
44 using ::android::base::WriteStringToFd;
45 using ::android::base::WriteStringToFile;
46
47 namespace android {
48 namespace init {
49
GetSecurityContext()50 static std::string GetSecurityContext() {
51 char* ctx;
52 if (getcon(&ctx) == -1) {
53 ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
54 }
55 std::string result{ctx};
56 freecon(ctx);
57 return result;
58 }
59
TEST(service,pod_initialized)60 TEST(service, pod_initialized) {
61 constexpr auto memory_size = sizeof(Service);
62 alignas(alignof(Service)) unsigned char old_memory[memory_size];
63
64 for (std::size_t i = 0; i < memory_size; ++i) {
65 old_memory[i] = 0xFF;
66 }
67
68 std::vector<std::string> dummy_args{"/bin/test"};
69 Service* service_in_old_memory =
70 new (old_memory) Service("test_old_memory", nullptr, /*filename=*/"", dummy_args);
71
72 EXPECT_EQ(0U, service_in_old_memory->flags());
73 EXPECT_EQ(0, service_in_old_memory->pid());
74 EXPECT_EQ(0, service_in_old_memory->crash_count());
75 EXPECT_EQ(0U, service_in_old_memory->uid());
76 EXPECT_EQ(0U, service_in_old_memory->gid());
77 EXPECT_EQ(0, service_in_old_memory->namespace_flags());
78 EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
79 EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
80 EXPECT_EQ(0, service_in_old_memory->priority());
81 EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory->oom_score_adjust());
82 EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
83
84 for (std::size_t i = 0; i < memory_size; ++i) {
85 old_memory[i] = 0xFF;
86 }
87
88 Service* service_in_old_memory2 = new (old_memory) Service(
89 "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "",
90 nullptr, /*filename=*/"", dummy_args);
91
92 EXPECT_EQ(0U, service_in_old_memory2->flags());
93 EXPECT_EQ(0, service_in_old_memory2->pid());
94 EXPECT_EQ(0, service_in_old_memory2->crash_count());
95 EXPECT_EQ(0U, service_in_old_memory2->uid());
96 EXPECT_EQ(0U, service_in_old_memory2->gid());
97 EXPECT_EQ(0, service_in_old_memory2->namespace_flags());
98 EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
99 EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
100 EXPECT_EQ(0, service_in_old_memory2->priority());
101 EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory2->oom_score_adjust());
102 EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
103 }
104
TEST(service,make_temporary_oneshot_service_invalid_syntax)105 TEST(service, make_temporary_oneshot_service_invalid_syntax) {
106 std::vector<std::string> args;
107 // Nothing.
108 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
109
110 // No arguments to 'exec'.
111 args.push_back("exec");
112 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
113
114 // No command in "exec --".
115 args.push_back("--");
116 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
117 }
118
TEST(service,make_temporary_oneshot_service_too_many_supplementary_gids)119 TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
120 std::vector<std::string> args;
121 args.push_back("exec");
122 args.push_back("seclabel");
123 args.push_back("root"); // uid.
124 args.push_back("root"); // gid.
125 for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
126 args.push_back("root"); // Supplementary gid.
127 }
128 args.push_back("--");
129 args.push_back("/system/bin/id");
130 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
131 }
132
Test_make_temporary_oneshot_service(bool dash_dash,bool seclabel,bool uid,bool gid,bool supplementary_gids)133 static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
134 bool supplementary_gids) {
135 std::vector<std::string> args;
136 args.push_back("exec");
137 if (seclabel) {
138 args.push_back("u:r:su:s0"); // seclabel
139 if (uid) {
140 args.push_back("log"); // uid
141 if (gid) {
142 args.push_back("shell"); // gid
143 if (supplementary_gids) {
144 args.push_back("system"); // supplementary gid 0
145 args.push_back("adb"); // supplementary gid 1
146 }
147 }
148 }
149 }
150 if (dash_dash) {
151 args.push_back("--");
152 }
153 args.push_back("/system/bin/toybox");
154 args.push_back("id");
155 auto service_ret = Service::MakeTemporaryOneshotService(args);
156 ASSERT_RESULT_OK(service_ret);
157 auto svc = std::move(*service_ret);
158
159 if (seclabel) {
160 ASSERT_EQ("u:r:su:s0", svc->seclabel());
161 } else {
162 ASSERT_EQ("", svc->seclabel());
163 }
164 if (uid) {
165 auto decoded_uid = DecodeUid("log");
166 ASSERT_RESULT_OK(decoded_uid);
167 ASSERT_EQ(*decoded_uid, svc->uid());
168 } else {
169 ASSERT_EQ(0U, svc->uid());
170 }
171 if (gid) {
172 auto decoded_uid = DecodeUid("shell");
173 ASSERT_RESULT_OK(decoded_uid);
174 ASSERT_EQ(*decoded_uid, svc->gid());
175 } else {
176 ASSERT_EQ(0U, svc->gid());
177 }
178 if (supplementary_gids) {
179 ASSERT_EQ(2U, svc->supp_gids().size());
180
181 auto decoded_uid = DecodeUid("system");
182 ASSERT_RESULT_OK(decoded_uid);
183 ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
184
185 decoded_uid = DecodeUid("adb");
186 ASSERT_RESULT_OK(decoded_uid);
187 ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
188 } else {
189 ASSERT_EQ(0U, svc->supp_gids().size());
190 }
191
192 ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
193 ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
194 ASSERT_EQ("id", svc->args()[1]);
195 }
196
TEST(service,make_temporary_oneshot_service_with_everything)197 TEST(service, make_temporary_oneshot_service_with_everything) {
198 Test_make_temporary_oneshot_service(true, true, true, true, true);
199 }
200
TEST(service,make_temporary_oneshot_service_with_seclabel_uid_gid)201 TEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {
202 Test_make_temporary_oneshot_service(true, true, true, true, false);
203 }
204
TEST(service,make_temporary_oneshot_service_with_seclabel_uid)205 TEST(service, make_temporary_oneshot_service_with_seclabel_uid) {
206 Test_make_temporary_oneshot_service(true, true, true, false, false);
207 }
208
TEST(service,make_temporary_oneshot_service_with_seclabel)209 TEST(service, make_temporary_oneshot_service_with_seclabel) {
210 Test_make_temporary_oneshot_service(true, true, false, false, false);
211 }
212
TEST(service,make_temporary_oneshot_service_with_just_command)213 TEST(service, make_temporary_oneshot_service_with_just_command) {
214 Test_make_temporary_oneshot_service(true, false, false, false, false);
215 }
216
TEST(service,make_temporary_oneshot_service_with_just_command_no_dash)217 TEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {
218 Test_make_temporary_oneshot_service(false, false, false, false, false);
219 }
220
221 // Returns the path in the v2 cgroup hierarchy for a given process in the format /uid_%d/pid_%d.
CgroupPath(pid_t pid)222 static std::string CgroupPath(pid_t pid) {
223 std::string cgroup_path = StringPrintf("/proc/%d/cgroup", pid);
224 std::ifstream is(cgroup_path, std::ios::in);
225 std::string line;
226 while (std::getline(is, line)) {
227 if (line.substr(0, 3) == "0::") {
228 return line.substr(3);
229 }
230 }
231 return {};
232 }
233
234 class ServiceStopTest : public testing::TestWithParam<bool> {};
235
236 // Before November 2023, processes that were migrated to another v2 cgroup were ignored by
237 // Service::Stop() if their uid_%d/pid_%d cgroup directory got removed. This test, if run with the
238 // parameter set to 'true', verifies that such services are stopped.
TEST_P(ServiceStopTest,stop)239 TEST_P(ServiceStopTest, stop) {
240 if (getuid() != 0) {
241 GTEST_SKIP() << "Must be run as root.";
242 return;
243 }
244
245 static constexpr std::string_view kServiceName = "ServiceA";
246 static constexpr std::string_view kScriptTemplate = R"init(
247 service $name /system/bin/yes
248 user shell
249 group shell
250 seclabel $selabel
251 )init";
252
253 std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", kServiceName, false),
254 "$selabel", GetSecurityContext(), false);
255 ServiceList& service_list = ServiceList::GetInstance();
256 Parser parser;
257 parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
258
259 TemporaryFile tf;
260 ASSERT_GE(tf.fd, 0);
261 ASSERT_TRUE(WriteStringToFd(script, tf.fd));
262 ASSERT_TRUE(parser.ParseConfig(tf.path));
263
264 Service* const service = ServiceList::GetInstance().FindService(kServiceName);
265 ASSERT_NE(service, nullptr);
266 ASSERT_RESULT_OK(service->Start());
267 ASSERT_TRUE(service->IsRunning());
268 if (GetParam()) {
269 const pid_t pid = service->pid();
270 const std::string cgroup_path = CgroupPath(pid);
271 EXPECT_NE(cgroup_path, "");
272 EXPECT_NE(cgroup_path, "/");
273 const std::string pid_str = std::to_string(pid);
274 EXPECT_TRUE(WriteStringToFile(pid_str, "/sys/fs/cgroup/cgroup.procs"));
275 EXPECT_EQ(CgroupPath(pid), "/");
276 EXPECT_EQ(rmdir(("/sys/fs/cgroup" + cgroup_path).c_str()), 0);
277 }
278 EXPECT_EQ(0, StopServicesAndLogViolations({service->name()}, 10s, /*terminate=*/true));
279 ServiceList::GetInstance().RemoveService(*service);
280 }
281
282 INSTANTIATE_TEST_SUITE_P(service, ServiceStopTest, testing::Values(false, true));
283
284 // Entering a network namespace requires remounting sysfs to update contents of
285 // /sys/class/net whose contents depend on the network namespace of the process
286 // that mounted it rather than the effective network namespace of the reading
287 // process.
288 //
289 // A side effect of the remounting is unmounting all filesystems mounted under
290 // /sys, like tracefs. Verify that init doesn't leave them unmounted by
291 // accident.
TEST(service,enter_namespace_net_preserves_mounts)292 TEST(service, enter_namespace_net_preserves_mounts) {
293 if (getuid() != 0) {
294 GTEST_SKIP() << "Must be run as root.";
295 return;
296 }
297
298 struct ScopedNetNs {
299 std::string name;
300 ScopedNetNs(std::string n) : name(n) {
301 EXPECT_EQ(system(("/system/bin/ip netns add " + name).c_str()), 0);
302 }
303 ~ScopedNetNs() { EXPECT_EQ(system(("/system/bin/ip netns delete " + name).c_str()), 0); }
304 };
305 const ScopedNetNs netns("test_ns");
306
307 static constexpr std::string_view kServiceName = "ServiceA";
308 static constexpr std::string_view kScriptTemplate = R"init(
309 service $name /system/bin/yes
310 user shell
311 group shell
312 seclabel $selabel
313 enter_namespace net /mnt/run/$ns_name
314 )init";
315
316 std::string script = StringReplace(kScriptTemplate, "$name", kServiceName, false);
317 script = StringReplace(script, "$selabel", GetSecurityContext(), false);
318 script = StringReplace(script, "$ns_name", netns.name, false);
319
320 ServiceList& service_list = ServiceList::GetInstance();
321 Parser parser;
322 parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
323
324 TemporaryFile tf;
325 ASSERT_GE(tf.fd, 0);
326 ASSERT_TRUE(WriteStringToFd(script, tf.fd));
327 ASSERT_TRUE(parser.ParseConfig(tf.path));
328
329 Service* const service = ServiceList::GetInstance().FindService(kServiceName);
330 ASSERT_NE(service, nullptr);
331 ASSERT_RESULT_OK(service->Start());
332 ASSERT_TRUE(service->IsRunning());
333
334 android::fs_mgr::Fstab root_mounts;
335 ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &root_mounts));
336
337 android::fs_mgr::Fstab ns_mounts;
338 ASSERT_TRUE(ReadFstabFromFile(StringReplace("/proc/$pid/mounts", "$pid",
339 std::to_string(service->pid()), /*all=*/false),
340 &ns_mounts));
341
342 for (const auto& expected_mount : root_mounts) {
343 auto it = std::find_if(ns_mounts.begin(), ns_mounts.end(), [&](const auto& ns_mount) {
344 return ns_mount.mount_point == expected_mount.mount_point;
345 });
346 EXPECT_TRUE(it != ns_mounts.end()) << StringPrintf(
347 "entering network namespace unmounted %s", expected_mount.mount_point.c_str());
348 }
349
350 ServiceList::GetInstance().RemoveService(*service);
351 }
352
353 } // namespace init
354 } // namespace android
355