blob: a3590b5367b98d9fd04779a2c8e87a2b155bdad2 [file] [log] [blame]
Tom Cherry7da54852017-05-01 14:16:41 -07001/*
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>
Bart Van Assche86a2ae22023-11-14 16:13:50 -080020#include <fstream>
Tom Cherry7da54852017-05-01 14:16:41 -070021#include <memory>
22#include <type_traits>
23#include <vector>
24
25#include <gtest/gtest.h>
26
Bart Van Assche86a2ae22023-11-14 16:13:50 -080027#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>
Suren Baghdasaryanc29c2ba2019-10-22 17:18:42 -070032#include "lmkd_service.h"
Bart Van Assche86a2ae22023-11-14 16:13:50 -080033#include "reboot.h"
34#include "service.h"
35#include "service_list.h"
36#include "service_parser.h"
Tom Cherry67dee622017-07-27 12:54:48 -070037#include "util.h"
38
Bart Van Assche86a2ae22023-11-14 16:13:50 -080039using ::android::base::ReadFileToString;
40using ::android::base::StringPrintf;
41using ::android::base::StringReplace;
42using ::android::base::unique_fd;
43using ::android::base::WriteStringToFd;
44using ::android::base::WriteStringToFile;
45
Tom Cherry81f5d3e2017-06-22 12:53:17 -070046namespace android {
47namespace init {
48
Bart Van Assche86a2ae22023-11-14 16:13:50 -080049static 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
Tom Cherry7da54852017-05-01 14:16:41 -070059TEST(service, pod_initialized) {
60 constexpr auto memory_size = sizeof(Service);
Tom Cherry247ffbf2019-07-08 15:09:36 -070061 alignas(alignof(Service)) unsigned char old_memory[memory_size];
Tom Cherry7da54852017-05-01 14:16:41 -070062
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"};
Tom Cherrycb0f9bb2017-09-12 15:58:47 -070068 Service* service_in_old_memory =
Deyao Rendf40ed12022-07-14 22:51:10 +000069 new (old_memory) Service("test_old_memory", nullptr, /*filename=*/"", dummy_args);
Tom Cherry7da54852017-05-01 14:16:41 -070070
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());
Tom Cherry247ffbf2019-07-08 15:09:36 -070076 EXPECT_EQ(0, service_in_old_memory->namespace_flags());
Tom Cherry7da54852017-05-01 14:16:41 -070077 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());
Suren Baghdasaryanc29c2ba2019-10-22 17:18:42 -070080 EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory->oom_score_adjust());
Tom Cherry33838b12017-05-04 11:32:36 -070081 EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
Tom Cherry7da54852017-05-01 14:16:41 -070082
83 for (std::size_t i = 0; i < memory_size; ++i) {
84 old_memory[i] = 0xFF;
85 }
86
Tom Cherrycb0f9bb2017-09-12 15:58:47 -070087 Service* service_in_old_memory2 = new (old_memory) Service(
Deyao Rendf40ed12022-07-14 22:51:10 +000088 "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "",
89 nullptr, /*filename=*/"", dummy_args);
Tom Cherry7da54852017-05-01 14:16:41 -070090
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());
Tom Cherry247ffbf2019-07-08 15:09:36 -070096 EXPECT_EQ(0, service_in_old_memory2->namespace_flags());
Tom Cherry7da54852017-05-01 14:16:41 -070097 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());
Suren Baghdasaryanc29c2ba2019-10-22 17:18:42 -0700100 EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory2->oom_score_adjust());
Tom Cherry33838b12017-05-04 11:32:36 -0700101 EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
Tom Cherry7da54852017-05-01 14:16:41 -0700102}
Tom Cherry81f5d3e2017-06-22 12:53:17 -0700103
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700104TEST(service, make_temporary_oneshot_service_invalid_syntax) {
Tom Cherry67dee622017-07-27 12:54:48 -0700105 std::vector<std::string> args;
106 // Nothing.
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900107 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
Tom Cherry67dee622017-07-27 12:54:48 -0700108
109 // No arguments to 'exec'.
110 args.push_back("exec");
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900111 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
Tom Cherry67dee622017-07-27 12:54:48 -0700112
113 // No command in "exec --".
114 args.push_back("--");
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900115 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
Tom Cherry67dee622017-07-27 12:54:48 -0700116}
117
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700118TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
Tom Cherry67dee622017-07-27 12:54:48 -0700119 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");
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900129 ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());
Tom Cherry67dee622017-07-27 12:54:48 -0700130}
131
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700132static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
133 bool supplementary_gids) {
Tom Cherry67dee622017-07-27 12:54:48 -0700134 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");
Tom Cherry4772f1d2019-07-30 09:34:41 -0700154 auto service_ret = Service::MakeTemporaryOneshotService(args);
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900155 ASSERT_RESULT_OK(service_ret);
Tom Cherry4772f1d2019-07-30 09:34:41 -0700156 auto svc = std::move(*service_ret);
Tom Cherry67dee622017-07-27 12:54:48 -0700157
158 if (seclabel) {
159 ASSERT_EQ("u:r:su:s0", svc->seclabel());
160 } else {
161 ASSERT_EQ("", svc->seclabel());
162 }
163 if (uid) {
Tom Cherry11a3aee2017-08-03 12:54:07 -0700164 auto decoded_uid = DecodeUid("log");
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900165 ASSERT_RESULT_OK(decoded_uid);
Tom Cherry11a3aee2017-08-03 12:54:07 -0700166 ASSERT_EQ(*decoded_uid, svc->uid());
Tom Cherry67dee622017-07-27 12:54:48 -0700167 } else {
168 ASSERT_EQ(0U, svc->uid());
169 }
170 if (gid) {
Tom Cherry11a3aee2017-08-03 12:54:07 -0700171 auto decoded_uid = DecodeUid("shell");
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900172 ASSERT_RESULT_OK(decoded_uid);
Tom Cherry11a3aee2017-08-03 12:54:07 -0700173 ASSERT_EQ(*decoded_uid, svc->gid());
Tom Cherry67dee622017-07-27 12:54:48 -0700174 } else {
175 ASSERT_EQ(0U, svc->gid());
176 }
177 if (supplementary_gids) {
178 ASSERT_EQ(2U, svc->supp_gids().size());
Tom Cherry11a3aee2017-08-03 12:54:07 -0700179
180 auto decoded_uid = DecodeUid("system");
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900181 ASSERT_RESULT_OK(decoded_uid);
Tom Cherry11a3aee2017-08-03 12:54:07 -0700182 ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
183
184 decoded_uid = DecodeUid("adb");
Bernie Innocenticecebbb2020-02-06 03:49:33 +0900185 ASSERT_RESULT_OK(decoded_uid);
Tom Cherry11a3aee2017-08-03 12:54:07 -0700186 ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
Tom Cherry67dee622017-07-27 12:54:48 -0700187 } 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
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700196TEST(service, make_temporary_oneshot_service_with_everything) {
197 Test_make_temporary_oneshot_service(true, true, true, true, true);
Tom Cherry67dee622017-07-27 12:54:48 -0700198}
199
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700200TEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {
201 Test_make_temporary_oneshot_service(true, true, true, true, false);
Tom Cherry67dee622017-07-27 12:54:48 -0700202}
203
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700204TEST(service, make_temporary_oneshot_service_with_seclabel_uid) {
205 Test_make_temporary_oneshot_service(true, true, true, false, false);
Tom Cherry67dee622017-07-27 12:54:48 -0700206}
207
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700208TEST(service, make_temporary_oneshot_service_with_seclabel) {
209 Test_make_temporary_oneshot_service(true, true, false, false, false);
Tom Cherry67dee622017-07-27 12:54:48 -0700210}
211
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700212TEST(service, make_temporary_oneshot_service_with_just_command) {
213 Test_make_temporary_oneshot_service(true, false, false, false, false);
Tom Cherry67dee622017-07-27 12:54:48 -0700214}
215
Tom Cherry3b81f2d2017-07-28 14:48:41 -0700216TEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {
217 Test_make_temporary_oneshot_service(false, false, false, false, false);
Tom Cherry67dee622017-07-27 12:54:48 -0700218}
219
Bart Van Assche86a2ae22023-11-14 16:13:50 -0800220// Returns the path in the v2 cgroup hierarchy for a given process in the format /uid_%d/pid_%d.
221static 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
233class 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.
238TEST_P(ServiceStopTest, stop) {
Bart Van Asschefb3e64a2023-11-30 09:58:31 -0800239 if (getuid() != 0) {
240 GTEST_SKIP() << "Must be run as root.";
241 return;
242 }
243
Bart Van Assche86a2ae22023-11-14 16:13:50 -0800244 static constexpr std::string_view kServiceName = "ServiceA";
245 static constexpr std::string_view kScriptTemplate = R"init(
246service $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",
257 std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
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
282INSTANTIATE_TEST_SUITE_P(service, ServiceStopTest, testing::Values(false, true));
283
Tom Cherry81f5d3e2017-06-22 12:53:17 -0700284} // namespace init
285} // namespace android