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>
40 using namespace SecurityManagerTest;
44 const size_t THREADS = 10;
46 const std::string APP_TEST_USER = "app_test_user";
48 const std::string EXTERNAL_STORAGE_PRIVILEGE = "http://tizen.org/privilege/externalstorage";
49 const std::string MEDIA_STORAGE_PRIVILEGE = "http://tizen.org/privilege/mediastorage";
51 const std::string ACCESS_DENIED_DIR_PATH = "/usr/share/security-manager/dummy";
52 const std::string EXTERNAL_STORAGE_DIR_PATH = "/opt/media";
53 const std::string MEDIA_STORAGE_RW_DIR_PATH = "/opt/usr/media";
54 const std::string MEDIA_STORAGE_RO_DIR_PATH = "/opt/usr/home/app_test_user/media";
56 typedef std::unique_ptr<_cap_struct, decltype(&cap_free)> CapPtr;
58 std::string thread_errors;
59 std::mutex error_mutex;
61 #define THREAD_ASSERT_MSG(test, message) \
66 std::ostringstream assertMsg; \
67 assertMsg << #test << " " << __FILE__ << " " << __LINE__ << " " << \
68 message << std::endl; \
69 std::lock_guard<std::mutex> guard(error_mutex); \
70 thread_errors.append(assertMsg.str()); \
74 void threadFn(int i, const std::string &expectedLabel)
79 THREAD_ASSERT_MSG(sigfillset(&sigset) == 0, "sigfillset failed");
80 THREAD_ASSERT_MSG(sigprocmask(SIG_BLOCK, &sigset, NULL) == 0, "sigprocmask failed");
87 THREAD_ASSERT_MSG(smack_new_label_from_self(&label) > 0, "smack_new_label_from_self failed");
88 CStringPtr labelPtr(label);
90 THREAD_ASSERT_MSG(expectedLabel.compare(label) == 0,
91 "Thread " << i << " has a wrong label: " << label);
93 CapPtr expectedCaps(cap_init(), cap_free);
94 THREAD_ASSERT_MSG(expectedCaps, "cap_init() failed");
95 THREAD_ASSERT_MSG(cap_clear(expectedCaps.get()) == 0, "cap_clear() failed");
97 CapPtr realCaps(cap_get_proc(), cap_free);
98 THREAD_ASSERT_MSG(realCaps, "cap_get_proc() failed");
99 THREAD_ASSERT_MSG(cap_compare(realCaps.get(), expectedCaps.get()) == 0,
100 "Thread " << i << " has wrong caps");
115 void run(int i, const std::string &expectedLabel)
117 THREAD_ASSERT_MSG(!thread.joinable(), "Thread already started");
118 thread = std::thread(threadFn, i, expectedLabel);
124 ino_t getFileInode(const std::string &path)
127 if (stat(path.c_str(), &st) != 0)
133 std::string getTextFileContents(const std::string &path)
135 std::ifstream in(path.c_str());
137 return std::string();
138 std::stringstream ss;
143 bool isPathBound(const std::string &what, const std::string &where, pid_t pid = 1)
145 std::string mountinfoPath = std::string("/proc/") + std::to_string(pid) + "/mountinfo";
146 std::string mountinfo = getTextFileContents(mountinfoPath);
147 std::string line = what + " " + where;
149 return std::string::npos != mountinfo.find(line);
152 } // anonymous namespace
154 RUNNER_TEST_GROUP_INIT(SECURITY_MANAGER_PREPARE_APP)
156 RUNNER_CHILD_TEST(security_manager_100_synchronize_credentials_test)
158 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
161 AppInstallHelper app("app100", tmpUser.getUid());
162 ScopedInstaller appInstall(app);
163 const std::string expectedLabel = app.generateAppLabel();
166 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
169 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
170 Api::prepareAppCandidate();
171 ThreadWrapper threads[THREADS];
173 for (size_t i = 0; i < THREADS; i++)
174 threads[i].run(i, expectedLabel);
176 Api::prepareApp(app.getAppId().c_str());
178 RUNNER_ASSERT_MSG(thread_errors.empty(), std::endl << thread_errors);
182 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
186 RUNNER_CHILD_TEST(security_manager_101_create_namespace_test_n)
188 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
191 AppInstallHelper app("app100_n", tmpUser.getUid());
192 ScopedInstaller appInstall(app);
193 const std::string expectedLabel = app.generateAppLabel();
196 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
199 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
200 ThreadWrapper threads[THREADS];
202 for (size_t i = 0; i < THREADS; i++)
203 threads[i].run(i, expectedLabel);
205 Api::prepareAppCandidate(SECURITY_MANAGER_ERROR_INPUT_PARAM);
207 RUNNER_ASSERT_MSG(!thread_errors.empty(), std::endl << thread_errors);
214 RUNNER_CHILD_TEST(security_manager_101_create_namespace_test)
216 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
219 AppInstallHelper app("app101", tmpUser.getUid());
220 ScopedInstaller appInstall(app);
222 SynchronizationPipe synchPipe;
224 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
226 synchPipe.claimParentEp();
227 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
228 Api::prepareAppCandidate();
229 Api::prepareApp(app.getAppId().c_str());
235 synchPipe.claimChildEp();
238 std::string appBindPath = std::string("/var/run/user/") + std::to_string(tmpUser.getUid())
239 + "/apps/" + app.generateAppLabel() + "/" + std::to_string(pid);
240 std::string appProcPath = std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
241 std::string launcherProcPath = std::string("/proc/") + std::to_string(getpid()) + "/ns/mnt";
243 ino_t appBindInode = getFileInode(appBindPath);
244 ino_t appProcInode = getFileInode(appProcPath);
245 ino_t launcherProcInode = getFileInode(launcherProcPath);
247 RUNNER_ASSERT_ERRNO_MSG(appBindInode != 0, "get inode failed");
248 RUNNER_ASSERT_ERRNO_MSG(appProcInode != 0, "get inode failed");
249 RUNNER_ASSERT_ERRNO_MSG(launcherProcInode != 0, "get inode failed");
251 RUNNER_ASSERT_ERRNO_MSG(launcherProcInode != appProcInode, "create mount namespace failed");
252 RUNNER_ASSERT_ERRNO_MSG(appBindInode == appProcInode, "bind namespace failed");
256 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
260 RUNNER_CHILD_TEST(security_manager_102_check_propagation_test)
262 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
265 AppInstallHelper app("app102", tmpUser.getUid());
266 ScopedInstaller appInstall(app);
268 SynchronizationPipe synchPipe;
270 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
272 synchPipe.claimParentEp();
273 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
274 Api::prepareAppCandidate();
275 Api::prepareApp(app.getAppId().c_str());
281 synchPipe.claimChildEp();
284 bool result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
285 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
286 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
287 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
288 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
289 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
291 result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH);
292 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
293 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH);
294 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
295 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH);
296 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
300 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
304 RUNNER_CHILD_TEST(security_manager_103_policy_change_test)
306 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
309 AppInstallHelper app("app103", tmpUser.getUid());
310 app.addPrivilege(EXTERNAL_STORAGE_PRIVILEGE);
311 app.addPrivilege(MEDIA_STORAGE_PRIVILEGE);
312 ScopedInstaller appInstall(app);
314 SynchronizationPipe synchPipe;
316 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
318 synchPipe.claimParentEp();
319 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
320 Api::prepareAppCandidate();
321 Api::prepareApp(app.getAppId().c_str());
327 synchPipe.claimChildEp();
330 bool result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
331 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
332 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
333 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
334 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
335 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
337 PolicyRequest policyRequest;
338 PolicyEntry policyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), EXTERNAL_STORAGE_PRIVILEGE);
339 policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
340 policyRequest.addEntry(policyEntry);
342 policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), MEDIA_STORAGE_PRIVILEGE);
343 policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
344 policyRequest.addEntry(policyEntry);
345 Api::sendPolicy(policyRequest);
347 result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
348 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
349 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
350 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
351 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
352 RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
354 policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), EXTERNAL_STORAGE_PRIVILEGE);
355 policyEntry.setLevel(PolicyEntry::LEVEL_ALLOW);
356 policyRequest.addEntry(policyEntry);
358 policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), MEDIA_STORAGE_PRIVILEGE);
359 policyEntry.setLevel(PolicyEntry::LEVEL_ALLOW);
360 policyRequest.addEntry(policyEntry);
361 Api::sendPolicy(policyRequest);
363 result = isPathBound(ACCESS_DENIED_DIR_PATH, EXTERNAL_STORAGE_DIR_PATH, pid);
364 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
365 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RW_DIR_PATH, pid);
366 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
367 result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
368 RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
372 Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
379 explicit Timestamp(uint64_t ts) : _(ts) {}
381 Timestamp operator-(const Timestamp &other) const {
382 RUNNER_ASSERT(_ > other._);
383 return Timestamp(_ - other._);
385 Timestamp operator+(const Timestamp &other) const {
386 return Timestamp(_ + other._);
388 bool operator<(const Timestamp &other) const {
391 Timestamp() = default;
392 static Timestamp future(uint64_t ns) {
394 const auto res = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
395 RUNNER_ASSERT_ERRNO(!res);
396 return Timestamp(ts.tv_sec * 1000000000ULL + ts.tv_nsec + ns);
398 static Timestamp now() {
401 template <size_t nLowDigitsToSkip = 3>
402 static void report(Timestamp *ts, size_t n) {
404 uint64_t sum = 0, mn = -1, mx = 0;
405 long double qsum = 0;
406 for (size_t i = 0; i < n; i++) {
407 const auto t = ts[i]._;
409 qsum += decltype(qsum)(t) * t;
410 mn = std::min(mn, t);
411 mx = std::max(mx, t);
413 uint64_t avg = sum / n;
414 auto qstddev = qsum/n - decltype(qsum)(avg)*avg;
415 const auto out = [](const char *desc, uint64_t t) {
417 char s[20 + 20/3 + 1];
420 raw[j++] = '0' + t % 10ULL;
424 if (j <= nLowDigitsToSkip)
430 std::cerr << desc << s;
435 out(" median ", ts[n/2]._);
436 out(" stddev ", std::floor(std::sqrt(qstddev)));
441 template <class T, size_t N>
442 constexpr size_t arraySize(T (&)[N]) { return N; }
445 RUNNER_TEST(security_manager_200_prepare_app_perf)
447 constexpr int8_t nThreads = 32;
448 constexpr int8_t nConcurrentAppsSamples[] = { 0 /* 1 app w/ nThreads */, 1, 2, 4, 8, 16, 32 };
449 constexpr uint64_t minTotalBenchTime = 60 * 1000ULL*1000*1000; // 60s
451 TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
455 AppInstallHelper hlp;
459 std::vector<Timestamp> candidate, prepare, everything;
461 std::vector<App> apps;
462 std::vector<ScopedInstaller> appInstalls;
464 constexpr auto nAppsMax = nConcurrentAppsSamples[arraySize(nConcurrentAppsSamples) - 1] ?: 1;
465 apps.reserve(nAppsMax);
466 appInstalls.reserve(nAppsMax);
468 const auto uid = tmpUser.getUid();
469 for (int i = 0; i < nAppsMax; i++) {
470 apps.emplace_back(App{AppInstallHelper("app200_" + std::to_string(i), uid), 0});
471 auto &hlp = apps.back().hlp;
472 for (const auto &p : { EXTERNAL_STORAGE_PRIVILEGE, MEDIA_STORAGE_PRIVILEGE,
473 std::string("http://tizen.org/privilege/camera"),
474 std::string("http://tizen.org/privilege/internet") })
476 hlp.createSharedRODir();
477 appInstalls.emplace_back(ScopedInstaller(hlp));
480 for (const auto nConcurrentAppsDesc : nConcurrentAppsSamples) {
481 const auto nConcurrentApps = nConcurrentAppsDesc ?: 1;
482 const auto timeout = Timestamp::future(minTotalBenchTime / arraySize(nConcurrentAppsSamples));
484 SynchronizationPipe synchPipe;
485 auto exitEvFd = eventfd(0, 0);
486 RUNNER_ASSERT(exitEvFd >= 0);
488 for (int i = 0; i < nConcurrentApps; i++) {
490 const auto pid = fork();
491 RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
495 synchPipe.claimChildEp();
496 RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
498 const auto appId = app.hlp.getAppId();
500 synchPipe.post(); // declare readiness to start measuring
501 synchPipe.pollForWait(); // wait for parent to signal all kids simultaneously
503 const auto candBeg = Timestamp::now();
504 Api::prepareAppCandidate();
505 const auto candEnd = Timestamp::now();
507 if (!nConcurrentAppsDesc) {
508 for (int i = 0; i < nThreads; i++)
509 std::thread([]{ for (;;) usleep(1000); }).detach();
512 const auto prepBeg = Timestamp::now();
513 Api::prepareApp(appId);
514 const auto prepEnd = Timestamp::now();
516 const Timestamp ts[2] = { candEnd-candBeg, prepEnd-prepBeg };
517 synchPipe.post(ts, sizeof ts); // post measurement payload
519 // stay idle until all kids are done to simulate idle apps and reduce benchmark noise
522 fds->events = POLLIN;
523 auto ret = TEMP_FAILURE_RETRY(poll(fds, 1, -1));
524 RUNNER_ASSERT_ERRNO(ret > 0);
529 synchPipe.claimParentEp();
531 for (int i = 0; i < nConcurrentApps; i++) // wait for all kids to be ready to start measurement
533 synchPipe.post(); // signal all kids to start measuring
535 for (int i = 0; i < nConcurrentApps; i++) {
537 synchPipe.wait(ts, sizeof ts);
538 candidate.emplace_back(ts[0]);
539 prepare.emplace_back(ts[1]);
540 everything.emplace_back(ts[0] + ts[1]);
543 RUNNER_ASSERT(!eventfd_write(exitEvFd, 1)); // signal all kids to exit now
545 for (int i = 0; i < nConcurrentApps; i++) {
546 const auto &app = apps[i];
548 Api::cleanupApp(app.hlp.getAppId(), uid, app.pid);
550 } while (Timestamp::now() < timeout);
552 if (!nConcurrentAppsDesc)
553 std::cerr << "additionalThreads " << int(nThreads) << ' ';
554 std::cerr << "nConcurrentApps " << int(nConcurrentApps) << " samples " << candidate.size() << '\n';
555 std::cerr << " prepareAppCandidate [us]: ";
556 Timestamp::report(candidate.data(), candidate.size());
557 std::cerr << " prepareApp [us]: ";
558 Timestamp::report(prepare.data(), prepare.size());
559 std::cerr << " prepareAppCandidate + prepareApp [us]: ";
560 Timestamp::report(everything.data(), everything.size());