xref: /aosp_15_r20/system/core/init/service_test.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
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