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