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);
377 explicit Timestamp(uint64_t ts) : _(ts) {}
379 Timestamp operator-(const Timestamp &other) const {
380 RUNNER_ASSERT(_ > other._);
381 return Timestamp(_ - other._);
383 Timestamp operator+(const Timestamp &other) const {
384 return Timestamp(_ + other._);
386 bool operator<(const Timestamp &other) const {
389 Timestamp() = default;
390 static Timestamp future(uint64_t ns) {
392 const auto res = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
393 RUNNER_ASSERT_ERRNO(!res);
394 return Timestamp(ts.tv_sec * 1000000000ULL + ts.tv_nsec + ns);
396 static Timestamp now() {
399 template <size_t nLowDigitsToSkip = 3>
400 static void report(Timestamp *ts, size_t n) {
402 uint64_t sum = 0, mn = -1, mx = 0;
403 long double qsum = 0;
404 for (size_t i = 0; i < n; i++) {
405 const auto t = ts[i]._;
407 qsum += decltype(qsum)(t) * t;
408 mn = std::min(mn, t);
409 mx = std::max(mx, t);
411 uint64_t avg = sum / n;
412 auto qstddev = qsum/n - decltype(qsum)(avg)*avg;
413 const auto out = [](const char *desc, uint64_t t) {
415 char s[20 + 20/3 + 1];
418 raw[j++] = '0' + t % 10ULL;
422 if (j <= nLowDigitsToSkip)
428 std::cerr << desc << s;
433 out(" median ", ts[n/2]._);
434 out(" stddev ", std::floor(std::sqrt(qstddev)));
439 template <class T, size_t N>
440 constexpr size_t arraySize(T (&)[N]) { return N; }
443 RUNNER_TEST(security_manager_200_prepare_app_perf)
445 constexpr int8_t nThreads = 32;
446 constexpr int8_t nConcurrentAppsSamples[] = { 0 /* 1 app w/ nThreads */, 1, 2, 4, 8, 16, 32 };
447 constexpr uint64_t minTotalBenchTime = 60 * 1000ULL*1000*1000; // 60s
449 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
453 AppInstallHelper hlp;
457 std::vector<Timestamp> candidate, prepare, everything;
459 std::vector<App> apps;
460 std::vector<ScopedInstaller> appInstalls;
462 constexpr auto nAppsMax = nConcurrentAppsSamples[arraySize(nConcurrentAppsSamples) - 1] ?: 1;
463 apps.reserve(nAppsMax);
464 appInstalls.reserve(nAppsMax);
466 const auto uid = tmpUser.getUid();
467 for (int i = 0; i < nAppsMax; i++) {
468 apps.emplace_back(App{AppInstallHelper("app200_" + std::to_string(i), uid), 0});
469 auto &hlp = apps.back().hlp;
470 hlp.addPrivileges({PRIV_EXTERNALSTORAGE, PRIV_MEDIASTORAGE, PRIV_CAMERA, PRIV_INTERNET});
471 hlp.createSharedRODir();
472 appInstalls.emplace_back(ScopedInstaller(hlp));
475 for (const auto nConcurrentAppsDesc : nConcurrentAppsSamples) {
476 const auto nConcurrentApps = nConcurrentAppsDesc ?: 1;
477 const auto timeout = Timestamp::future(minTotalBenchTime / arraySize(nConcurrentAppsSamples));
479 SynchronizationPipe synchPipe;
480 auto exitEvFd = eventfd(0, 0);
481 RUNNER_ASSERT(exitEvFd >= 0);
483 for (int i = 0; i < nConcurrentApps; i++) {
485 const auto pid = fork();
486 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
490 synchPipe.claimChildEp();
491 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
493 const auto appId = app.hlp.getAppId();
495 synchPipe.post(); // declare readiness to start measuring
496 synchPipe.pollForWait(); // wait for parent to signal all kids simultaneously
498 const auto candBeg = Timestamp::now();
499 Api::prepareAppCandidate();
500 const auto candEnd = Timestamp::now();
502 if (!nConcurrentAppsDesc) {
503 for (int i = 0; i < nThreads; i++)
504 std::thread([]{ for (;;) usleep(1000); }).detach();
507 const auto prepBeg = Timestamp::now();
508 Api::prepareApp(appId);
509 const auto prepEnd = Timestamp::now();
511 const Timestamp ts[2] = { candEnd-candBeg, prepEnd-prepBeg };
512 synchPipe.post(ts, sizeof ts); // post measurement payload
514 // stay idle until all kids are done to simulate idle apps and reduce benchmark noise
517 fds->events = POLLIN;
518 auto ret = TEMP_FAILURE_RETRY(poll(fds, 1, -1));
519 RUNNER_ASSERT_ERRNO(ret > 0);
524 synchPipe.claimParentEp();
526 for (int i = 0; i < nConcurrentApps; i++) // wait for all kids to be ready to start measurement
528 synchPipe.post(); // signal all kids to start measuring
530 for (int i = 0; i < nConcurrentApps; i++) {
532 synchPipe.wait(ts, sizeof ts);
533 candidate.emplace_back(ts[0]);
534 prepare.emplace_back(ts[1]);
535 everything.emplace_back(ts[0] + ts[1]);
538 RUNNER_ASSERT(!eventfd_write(exitEvFd, 1)); // signal all kids to exit now
540 for (int i = 0; i < nConcurrentApps; i++) {
541 const auto &app = apps[i];
543 Api::cleanupApp(app.hlp.getAppId(), uid, app.pid);
545 } while (Timestamp::now() < timeout);
547 if (!nConcurrentAppsDesc)
548 std::cerr << "additionalThreads " << int(nThreads) << ' ';
549 std::cerr << "nConcurrentApps " << int(nConcurrentApps) << " samples " << candidate.size() << '\n';
550 std::cerr << " prepareAppCandidate [us]: ";
551 Timestamp::report(candidate.data(), candidate.size());
552 std::cerr << " prepareApp [us]: ";
553 Timestamp::report(prepare.data(), prepare.size());
554 std::cerr << " prepareAppCandidate + prepareApp [us]: ";
555 Timestamp::report(everything.data(), everything.size());