2 * Copyright (c) 2016-2020 Samsung Electronics Co., Ltd. All rights reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <sys/smack.h>
19 #include <sys/capability.h>
20 #include <sys/prctl.h>
21 #include <sys/eventfd.h>
30 #include <dpl/test/test_runner_child.h>
31 #include <dpl/test/test_runner.h>
33 #include <app_install_helper.h>
34 #include <scoped_installer.h>
36 #include <sm_commons.h>
38 #include <tests_common.h>
39 #include <privilege_names.h>
41 using namespace SecurityManagerTest;
42 using namespace PrivilegeNames;
46 const size_t THREADS = 10;
48 const std::string APP_TEST_USER = "app_test_user";
50 const std::string ACCESS_DENIED_DIR_PATH = "/usr/share/security-manager/dummy";
51 const std::string EXTERNAL_STORAGE_DIR_PATH = "/opt/media";
52 const std::string MEDIA_STORAGE_RW_DIR_PATH = "/opt/usr/media";
53 const std::string MEDIA_STORAGE_RO_DIR_PATH = "/opt/usr/home/app_test_user/media";
55 typedef std::unique_ptr<_cap_struct, decltype(&cap_free)> CapPtr;
57 std::string thread_errors;
58 std::mutex error_mutex;
60 #define THREAD_ASSERT_MSG(test, message) \
65 std::ostringstream assertMsg; \
66 assertMsg << #test << " " << __FILE__ << " " << __LINE__ << " " << \
67 message << std::endl; \
68 std::lock_guard<std::mutex> guard(error_mutex); \
69 thread_errors.append(assertMsg.str()); \
73 void threadFn(int i, const std::string &expectedLabel)
78 THREAD_ASSERT_MSG(sigfillset(&sigset) == 0, "sigfillset failed");
79 THREAD_ASSERT_MSG(sigprocmask(SIG_BLOCK, &sigset, NULL) == 0, "sigprocmask failed");
86 THREAD_ASSERT_MSG(smack_new_label_from_self(&label) > 0, "smack_new_label_from_self failed");
87 CStringPtr labelPtr(label);
89 THREAD_ASSERT_MSG(expectedLabel.compare(label) == 0,
90 "Thread " << i << " has a wrong label: " << label);
92 CapPtr expectedCaps(cap_init(), cap_free);
93 THREAD_ASSERT_MSG(expectedCaps, "cap_init() failed");
94 THREAD_ASSERT_MSG(cap_clear(expectedCaps.get()) == 0, "cap_clear() failed");
96 CapPtr realCaps(cap_get_proc(), cap_free);
97 THREAD_ASSERT_MSG(realCaps, "cap_get_proc() failed");
98 THREAD_ASSERT_MSG(cap_compare(realCaps.get(), expectedCaps.get()) == 0,
99 "Thread " << i << " has wrong caps");
114 void run(int i, const std::string &expectedLabel)
116 THREAD_ASSERT_MSG(!thread.joinable(), "Thread already started");
117 thread = std::thread(threadFn, i, expectedLabel);
123 ino_t getFileInode(const std::string &path)
126 if (stat(path.c_str(), &st) != 0)
132 std::string getTextFileContents(const std::string &path)
134 std::ifstream in(path.c_str());
136 return std::string();
137 std::stringstream ss;
142 bool isPathBound(const std::string &what, const std::string &where, pid_t pid = 1)
144 std::string mountinfoPath = std::string("/proc/") + std::to_string(pid) + "/mountinfo";
145 std::string mountinfo = getTextFileContents(mountinfoPath);
146 std::string line = what + " " + where;
148 return std::string::npos != mountinfo.find(line);
151 } // anonymous namespace
153 RUNNER_TEST_GROUP_INIT(SECURITY_MANAGER_PREPARE_APP)
155 RUNNER_CHILD_TEST(security_manager_100_synchronize_credentials_test)
157 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
160 AppInstallHelper app("app100", tmpUser.getUid());
161 ScopedInstaller appInstall(app);
162 const std::string expectedLabel = app.generateAppLabel();
165 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
168 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
169 Api::prepareAppCandidate();
170 ThreadWrapper threads[THREADS];
172 for (size_t i = 0; i < THREADS; i++)
173 threads[i].run(i, expectedLabel);
175 Api::prepareApp(app.getAppId().c_str());
177 RUNNER_ASSERT_MSG(thread_errors.empty(), std::endl << thread_errors);
181 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
185 RUNNER_CHILD_TEST(security_manager_101_create_namespace_test_n)
187 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
190 AppInstallHelper app("app100_n", tmpUser.getUid());
191 ScopedInstaller appInstall(app);
192 const std::string expectedLabel = app.generateAppLabel();
195 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
198 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
199 ThreadWrapper threads[THREADS];
201 for (size_t i = 0; i < THREADS; i++)
202 threads[i].run(i, expectedLabel);
204 Api::prepareAppCandidate(SECURITY_MANAGER_ERROR_INPUT_PARAM);
206 RUNNER_ASSERT_MSG(!thread_errors.empty(), std::endl << thread_errors);
213 RUNNER_CHILD_TEST(security_manager_101_create_namespace_test)
215 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
218 AppInstallHelper app("app101", tmpUser.getUid());
219 ScopedInstaller appInstall(app);
221 SynchronizationPipe synchPipe;
223 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
225 synchPipe.claimParentEp();
226 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
227 Api::prepareAppCandidate();
228 Api::prepareApp(app.getAppId().c_str());
234 synchPipe.claimChildEp();
237 std::string appBindPath = std::string("/var/run/user/") + std::to_string(tmpUser.getUid())
238 + "/apps/" + app.generateAppLabel() + "/" + std::to_string(pid);
239 std::string appProcPath = std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
240 std::string launcherProcPath = std::string("/proc/") + std::to_string(getpid()) + "/ns/mnt";
242 ino_t appBindInode = getFileInode(appBindPath);
243 ino_t appProcInode = getFileInode(appProcPath);
244 ino_t launcherProcInode = getFileInode(launcherProcPath);
246 RUNNER_ASSERT_ERRNO_MSG(appBindInode != 0, "get inode failed");
247 RUNNER_ASSERT_ERRNO_MSG(appProcInode != 0, "get inode failed");
248 RUNNER_ASSERT_ERRNO_MSG(launcherProcInode != 0, "get inode failed");
250 RUNNER_ASSERT_ERRNO_MSG(launcherProcInode != appProcInode, "create mount namespace failed");
251 RUNNER_ASSERT_ERRNO_MSG(appBindInode == appProcInode, "bind namespace failed");
255 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
259 RUNNER_CHILD_TEST(security_manager_102_check_propagation_test)
261 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
264 AppInstallHelper app("app102", tmpUser.getUid());
265 ScopedInstaller appInstall(app);
267 SynchronizationPipe synchPipe;
269 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
271 synchPipe.claimParentEp();
272 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
273 Api::prepareAppCandidate();
274 Api::prepareApp(app.getAppId().c_str());
280 synchPipe.claimChildEp();
283 bool result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
284 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
285 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
286 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
287 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
288 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
290 result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH);
291 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
292 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH);
293 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
294 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH);
295 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
299 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
303 RUNNER_CHILD_TEST(security_manager_103_policy_change_test)
305 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
308 AppInstallHelper app("app103", tmpUser.getUid());
309 app.addPrivileges({PRIV_EXTERNALSTORAGE, PRIV_MEDIASTORAGE});
310 ScopedInstaller appInstall(app);
312 SynchronizationPipe synchPipe;
314 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
316 synchPipe.claimParentEp();
317 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
318 Api::prepareAppCandidate();
319 Api::prepareApp(app.getAppId().c_str());
325 synchPipe.claimChildEp();
328 bool result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
329 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
330 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
331 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
332 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
333 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
335 PolicyRequest policyRequest;
336 PolicyEntry policyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), PRIV_EXTERNALSTORAGE);
337 policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
338 policyRequest.addEntry(policyEntry);
340 policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), PRIV_MEDIASTORAGE);
341 policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
342 policyRequest.addEntry(policyEntry);
343 Api::sendPolicy(policyRequest);
345 result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
346 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
347 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
348 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
349 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
350 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
352 policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), PRIV_EXTERNALSTORAGE);
353 policyEntry.setLevel(PolicyEntry::LEVEL_ALLOW);
354 policyRequest.addEntry(policyEntry);
356 policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), PRIV_MEDIASTORAGE);
357 policyEntry.setLevel(PolicyEntry::LEVEL_ALLOW);
358 policyRequest.addEntry(policyEntry);
359 Api::sendPolicy(policyRequest);
361 result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
362 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
363 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
364 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
365 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
366 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
370 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
374 RUNNER_CHILD_TEST(security_manager_104_policy_change_kill_app_test)
376 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
379 AppInstallHelper app("app104", tmpUser.getUid());
380 app.addPrivileges({PRIV_EXTERNALSTORAGE, PRIV_MEDIASTORAGE});
381 ScopedInstaller appInstall(app);
383 SynchronizationPipe synchPipe;
385 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
387 synchPipe.claimParentEp();
389 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
390 Api::prepareAppCandidate();
391 Api::prepareApp(app.getAppId());
399 synchPipe.claimChildEp();
402 PolicyRequest policyRequest;
403 PolicyEntry policyEntry(app.getAppId(), tmpUser.getUidString(), PRIV_EXTERNALSTORAGE);
404 policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
405 policyRequest.addEntry(policyEntry);
406 Api::sendPolicy(policyRequest);
409 Api::cleanupApp(app.getAppId(), tmpUser.getUid(), pid);
416 explicit Timestamp(uint64_t ts) : _(ts) {}
418 Timestamp operator-(const Timestamp &other) const {
419 RUNNER_ASSERT(_ > other._);
420 return Timestamp(_ - other._);
422 Timestamp operator+(const Timestamp &other) const {
423 return Timestamp(_ + other._);
425 bool operator<(const Timestamp &other) const {
428 Timestamp() = default;
429 static Timestamp future(uint64_t ns) {
431 const auto res = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
432 RUNNER_ASSERT_ERRNO(!res);
433 return Timestamp(ts.tv_sec * 1000000000ULL + ts.tv_nsec + ns);
435 static Timestamp now() {
438 template <size_t nLowDigitsToSkip = 3>
439 static void report(Timestamp *ts, size_t n) {
441 uint64_t sum = 0, mn = -1, mx = 0;
442 long double qsum = 0;
443 for (size_t i = 0; i < n; i++) {
444 const auto t = ts[i]._;
446 qsum += decltype(qsum)(t) * t;
447 mn = std::min(mn, t);
448 mx = std::max(mx, t);
450 uint64_t avg = sum / n;
451 auto qstddev = qsum/n - decltype(qsum)(avg)*avg;
452 const auto out = [](const char *desc, uint64_t t) {
454 char s[20 + 20/3 + 1];
457 raw[j++] = '0' + t % 10ULL;
461 if (j <= nLowDigitsToSkip)
467 std::cerr << desc << s;
472 out(" median ", ts[n/2]._);
473 out(" stddev ", std::floor(std::sqrt(qstddev)));
478 template <class T, size_t N>
479 constexpr size_t arraySize(T (&)[N]) { return N; }
482 RUNNER_TEST(security_manager_200_prepare_app_perf)
484 constexpr int8_t nThreads = 32;
485 constexpr int8_t nConcurrentAppsSamples[] = { 0 /* 1 app w/ nThreads */, 1, 2, 4, 8, 16, 32 };
486 constexpr uint64_t minTotalBenchTime = 60 * 1000ULL*1000*1000; // 60s
488 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
492 AppInstallHelper hlp;
496 std::vector<Timestamp> candidate, prepare, everything;
498 std::vector<App> apps;
499 std::vector<ScopedInstaller> appInstalls;
501 constexpr auto nAppsMax = nConcurrentAppsSamples[arraySize(nConcurrentAppsSamples) - 1] ?: 1;
502 apps.reserve(nAppsMax);
503 appInstalls.reserve(nAppsMax);
505 const auto uid = tmpUser.getUid();
506 for (int i = 0; i < nAppsMax; i++) {
507 apps.emplace_back(App{AppInstallHelper("app200_" + std::to_string(i), uid), 0});
508 auto &hlp = apps.back().hlp;
509 hlp.addPrivileges({PRIV_EXTERNALSTORAGE, PRIV_MEDIASTORAGE, PRIV_CAMERA, PRIV_INTERNET});
510 hlp.createSharedRODir();
511 appInstalls.emplace_back(ScopedInstaller(hlp));
514 for (const auto nConcurrentAppsDesc : nConcurrentAppsSamples) {
515 const auto nConcurrentApps = nConcurrentAppsDesc ?: 1;
516 const auto timeout = Timestamp::future(minTotalBenchTime / arraySize(nConcurrentAppsSamples));
518 SynchronizationPipe synchPipe;
519 auto exitEvFd = eventfd(0, 0);
520 RUNNER_ASSERT(exitEvFd >= 0);
522 for (int i = 0; i < nConcurrentApps; i++) {
524 const auto pid = fork();
525 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
529 synchPipe.claimChildEp();
530 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
532 const auto appId = app.hlp.getAppId();
534 synchPipe.post(); // declare readiness to start measuring
535 synchPipe.pollForWait(); // wait for parent to signal all kids simultaneously
537 const auto candBeg = Timestamp::now();
538 Api::prepareAppCandidate();
539 const auto candEnd = Timestamp::now();
541 if (!nConcurrentAppsDesc) {
542 for (int i = 0; i < nThreads; i++)
543 std::thread([]{ for (;;) usleep(1000); }).detach();
546 const auto prepBeg = Timestamp::now();
547 Api::prepareApp(appId);
548 const auto prepEnd = Timestamp::now();
550 const Timestamp ts[2] = { candEnd-candBeg, prepEnd-prepBeg };
551 synchPipe.post(ts, sizeof ts); // post measurement payload
553 // stay idle until all kids are done to simulate idle apps and reduce benchmark noise
556 fds->events = POLLIN;
557 auto ret = TEMP_FAILURE_RETRY(poll(fds, 1, -1));
558 RUNNER_ASSERT_ERRNO(ret > 0);
563 synchPipe.claimParentEp();
565 for (int i = 0; i < nConcurrentApps; i++) // wait for all kids to be ready to start measurement
567 synchPipe.post(); // signal all kids to start measuring
569 for (int i = 0; i < nConcurrentApps; i++) {
571 synchPipe.wait(ts, sizeof ts);
572 candidate.emplace_back(ts[0]);
573 prepare.emplace_back(ts[1]);
574 everything.emplace_back(ts[0] + ts[1]);
577 RUNNER_ASSERT(!eventfd_write(exitEvFd, 1)); // signal all kids to exit now
579 for (int i = 0; i < nConcurrentApps; i++) {
580 const auto &app = apps[i];
582 Api::cleanupApp(app.hlp.getAppId(), uid, app.pid);
584 } while (Timestamp::now() < timeout);
586 if (!nConcurrentAppsDesc)
587 std::cerr << "additionalThreads " << int(nThreads) << ' ';
588 std::cerr << "nConcurrentApps " << int(nConcurrentApps) << " samples " << candidate.size() << '\n';
589 std::cerr << " prepareAppCandidate [us]: ";
590 Timestamp::report(candidate.data(), candidate.size());
591 std::cerr << " prepareApp [us]: ";
592 Timestamp::report(prepare.data(), prepare.size());
593 std::cerr << " prepareAppCandidate + prepareApp [us]: ";
594 Timestamp::report(everything.data(), everything.size());