/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016-2020 Samsung Electronics Co., Ltd. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
+#include <poll.h>
#include <sys/smack.h>
#include <sys/capability.h>
#include <sys/prctl.h>
+#include <sys/eventfd.h>
+#include <cmath>
#include <thread>
#include <string>
#include <memory>
#include <sm_commons.h>
#include <memory.h>
#include <tests_common.h>
+#include <privilege_names.h>
using namespace SecurityManagerTest;
+using namespace PrivilegeNames;
namespace {
bool finish = false;
const std::string APP_TEST_USER = "app_test_user";
-const std::string EXTERNAL_STORAGE_PRIVILEGE = "http://tizen.org/privilege/externalstorage";
-const std::string MEDIA_STORAGE_PRIVILEGE = "http://tizen.org/privilege/mediastorage";
-
const std::string ACCESS_DENIED_DIR_PATH = "/usr/share/security-manager/dummy";
const std::string EXTERNAL_STORAGE_DIR_PATH = "/opt/media";
const std::string MEDIA_STORAGE_RW_DIR_PATH = "/opt/usr/media";
std::thread thread;
};
-int setLauncherSecurityAttributes(TemporaryTestUser &user)
-{
- // Add launcher capabilities (cap_dac_override, cap_setgid, cap_sys_admin, cap_mac_admin),
- // launcher is user process, we must drop root privileges (cap_setgid, cap_setuid are needed).
- // By default, the permitted capability set is cleared when credentials change is made
- // (if a process drops a capability from its permitted set, it can never reacquire that capability),
- // setting the "keep capabilities" flag prevents it from being cleared.
- // Effective capability set is always cleared when credential change is made, we need to add them again.
-
- setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep cap_setuid+ep");
- int ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0);
- if (ret != 0)
- return ret;
-
- ret = drop_root_privileges(user.getUid(), user.getGid());
- if (ret != 0)
- return ret;
-
- setCaps("cap_dac_override+ep cap_setgid+ep cap_sys_admin+ep cap_mac_admin+ep");
- return ret;
-}
-
ino_t getFileInode(const std::string &path)
{
struct stat st;
if (pid == 0) {
{
RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
-
+ Api::prepareAppCandidate();
ThreadWrapper threads[THREADS];
for (size_t i = 0; i < THREADS; i++)
exit(0);
} else {
waitPid(pid);
- Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid());
+ Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
+ }
+}
+
+RUNNER_CHILD_TEST(security_manager_101_create_namespace_test_n)
+{
+ TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
+ tmpUser.create();
+
+ AppInstallHelper app("app100_n", tmpUser.getUid());
+ ScopedInstaller appInstall(app);
+ const std::string expectedLabel = app.generateAppLabel();
+
+ pid_t pid = fork();
+ RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
+ if (pid == 0) {
+ {
+ RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
+ ThreadWrapper threads[THREADS];
+
+ for (size_t i = 0; i < THREADS; i++)
+ threads[i].run(i, expectedLabel);
+
+ Api::prepareAppCandidate(SECURITY_MANAGER_ERROR_INPUT_PARAM);
+ }
+ RUNNER_ASSERT_MSG(!thread_errors.empty(), std::endl << thread_errors);
+ exit(0);
+ } else {
+ waitPid(pid);
}
}
if (pid == 0) {
synchPipe.claimParentEp();
RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
-
+ Api::prepareAppCandidate();
Api::prepareApp(app.getAppId().c_str());
synchPipe.post();
synchPipe.wait();
synchPipe.claimChildEp();
synchPipe.wait();
- std::string appBindPath = std::string("/var/run/user/") + std::to_string(tmpUser.getUid())
- + "/apps/" + app.generateAppLabel();
+ std::string appBindPath = std::string("/var/run/user/") + tmpUser.getUidString()
+ + "/apps/" + app.generateAppLabel() + "/" + std::to_string(pid);
std::string appProcPath = std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
std::string launcherProcPath = std::string("/proc/") + std::to_string(getpid()) + "/ns/mnt";
synchPipe.post();
waitPid(pid);
- Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid());
+ Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
}
}
if (pid == 0) {
synchPipe.claimParentEp();
RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
-
+ Api::prepareAppCandidate();
Api::prepareApp(app.getAppId().c_str());
synchPipe.post();
synchPipe.wait();
synchPipe.post();
waitPid(pid);
- Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid());
+ Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
}
}
tmpUser.create();
AppInstallHelper app("app103", tmpUser.getUid());
- app.addPrivilege(EXTERNAL_STORAGE_PRIVILEGE);
- app.addPrivilege(MEDIA_STORAGE_PRIVILEGE);
+ app.addPrivileges({PRIV_EXTERNALSTORAGE, PRIV_MEDIASTORAGE});
ScopedInstaller appInstall(app);
SynchronizationPipe synchPipe;
if (pid == 0) {
synchPipe.claimParentEp();
RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
-
+ Api::prepareAppCandidate();
Api::prepareApp(app.getAppId().c_str());
synchPipe.post();
synchPipe.wait();
RUNNER_ASSERT_ERRNO_MSG(result == false, "path is bound");
PolicyRequest policyRequest;
- PolicyEntry policyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), EXTERNAL_STORAGE_PRIVILEGE);
- policyEntry.setLevel("Deny");
+ PolicyEntry policyEntry(app.getAppId(), tmpUser.getUidString(), PRIV_EXTERNALSTORAGE);
+ policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
policyRequest.addEntry(policyEntry);
- policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), MEDIA_STORAGE_PRIVILEGE);
- policyEntry.setLevel("Deny");
+ policyEntry = PolicyEntry(app.getAppId(), tmpUser.getUidString(), PRIV_MEDIASTORAGE);
+ policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
policyRequest.addEntry(policyEntry);
Api::sendPolicy(policyRequest);
result = isPathBound(ACCESS_DENIED_DIR_PATH, MEDIA_STORAGE_RO_DIR_PATH, pid);
RUNNER_ASSERT_ERRNO_MSG(result == true, "path is not bound");
- policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), EXTERNAL_STORAGE_PRIVILEGE);
- policyEntry.setLevel("Allow");
+ policyEntry = PolicyEntry(app.getAppId(), tmpUser.getUidString(), PRIV_EXTERNALSTORAGE);
+ policyEntry.setLevel(PolicyEntry::LEVEL_ALLOW);
policyRequest.addEntry(policyEntry);
- policyEntry = PolicyEntry(app.getAppId(), std::to_string(tmpUser.getUid()), MEDIA_STORAGE_PRIVILEGE);
- policyEntry.setLevel("Allow");
+ policyEntry = PolicyEntry(app.getAppId(), tmpUser.getUidString(), PRIV_MEDIASTORAGE);
+ policyEntry.setLevel(PolicyEntry::LEVEL_ALLOW);
policyRequest.addEntry(policyEntry);
Api::sendPolicy(policyRequest);
synchPipe.post();
waitPid(pid);
- Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid());
+ Api::cleanupApp(app.getAppId().c_str(), tmpUser.getUid(), pid);
+ }
+}
+
+RUNNER_CHILD_TEST(security_manager_104_policy_change_kill_app_test)
+{
+ TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
+ tmpUser.create();
+
+ AppInstallHelper app("app104", tmpUser.getUid());
+ app.addPrivileges({PRIV_EXTERNALSTORAGE, PRIV_MEDIASTORAGE});
+ ScopedInstaller appInstall(app);
+
+ SynchronizationPipe synchPipe;
+ pid_t pid = fork();
+ RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
+ if (pid == 0) {
+ synchPipe.claimParentEp();
+ try {
+ RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
+ Api::prepareAppCandidate();
+ Api::prepareApp(app.getAppId());
+ } catch (...) {
+ synchPipe.post();
+ throw;
+ }
+ synchPipe.post();
+ exit(0);
+ } else {
+ synchPipe.claimChildEp();
+ synchPipe.wait();
+
+ PolicyRequest policyRequest;
+ PolicyEntry policyEntry(app.getAppId(), tmpUser.getUidString(), PRIV_EXTERNALSTORAGE);
+ policyEntry.setLevel(PolicyEntry::LEVEL_DENY);
+ policyRequest.addEntry(policyEntry);
+ Api::sendPolicy(policyRequest);
+
+ waitPid(pid);
+ Api::cleanupApp(app.getAppId(), tmpUser.getUid(), pid);
+ }
+}
+
+namespace {
+class Timestamp {
+ uint64_t _;
+ explicit Timestamp(uint64_t ts) : _(ts) {}
+public:
+ Timestamp operator-(const Timestamp &other) const {
+ RUNNER_ASSERT(_ > other._);
+ return Timestamp(_ - other._);
+ }
+ Timestamp operator+(const Timestamp &other) const {
+ return Timestamp(_ + other._);
+ }
+ bool operator<(const Timestamp &other) const {
+ return _ < other._;
+ }
+ Timestamp() = default;
+ static Timestamp future(uint64_t ns) {
+ timespec ts;
+ const auto res = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+ RUNNER_ASSERT_ERRNO(!res);
+ return Timestamp(ts.tv_sec * 1000000000ULL + ts.tv_nsec + ns);
+ }
+ static Timestamp now() {
+ return future(0);
+ }
+ template <size_t nLowDigitsToSkip = 3>
+ static void report(Timestamp *ts, size_t n) {
+ std::sort(ts, ts+n);
+ uint64_t sum = 0, mn = -1, mx = 0;
+ long double qsum = 0;
+ for (size_t i = 0; i < n; i++) {
+ const auto t = ts[i]._;
+ sum += t;
+ qsum += decltype(qsum)(t) * t;
+ mn = std::min(mn, t);
+ mx = std::max(mx, t);
+ }
+ uint64_t avg = sum / n;
+ auto qstddev = qsum/n - decltype(qsum)(avg)*avg;
+ const auto out = [](const char *desc, uint64_t t) {
+ char raw[20];
+ char s[20 + 20/3 + 1];
+ size_t j = 0, i = 0;
+ do
+ raw[j++] = '0' + t % 10ULL;
+ while (t /= 10ULL);
+ for (;;) {
+ s[i++] = raw[--j];
+ if (j <= nLowDigitsToSkip)
+ break;
+ if (!(j % 3))
+ s[i++] = ' ';
+ }
+ s[i] = '\0';
+ std::cerr << desc << s;
+ };
+ out("min ", mn);
+ out(" max ", mx);
+ out(" avg ", avg);
+ out(" median ", ts[n/2]._);
+ out(" stddev ", std::floor(std::sqrt(qstddev)));
+ std::cerr << '\n';
+ }
+};
+
+template <class T, size_t N>
+constexpr size_t arraySize(T (&)[N]) { return N; }
+} // namespace
+
+RUNNER_TEST(security_manager_200_prepare_app_perf)
+{
+ constexpr int8_t nThreads = 32;
+ constexpr int8_t nConcurrentAppsSamples[] = { 0 /* 1 app w/ nThreads */, 1, 2, 4, 8, 16, 32 };
+ constexpr uint64_t minTotalBenchTime = 60 * 1000ULL*1000*1000; // 60s
+
+ TemporaryTestUser tmpUser(APP_TEST_USER, GUM_USERTYPE_NORMAL, false);
+ tmpUser.create();
+
+ struct App {
+ AppInstallHelper hlp;
+ pid_t pid;
+ };
+
+ std::vector<Timestamp> candidate, prepare, everything;
+
+ std::vector<App> apps;
+ std::vector<ScopedInstaller> appInstalls;
+
+ constexpr auto nAppsMax = nConcurrentAppsSamples[arraySize(nConcurrentAppsSamples) - 1] ?: 1;
+ apps.reserve(nAppsMax);
+ appInstalls.reserve(nAppsMax);
+
+ const auto uid = tmpUser.getUid();
+ for (int i = 0; i < nAppsMax; i++) {
+ apps.emplace_back(App{AppInstallHelper("app200_" + std::to_string(i), uid), 0});
+ auto &hlp = apps.back().hlp;
+ hlp.addPrivileges({PRIV_EXTERNALSTORAGE, PRIV_MEDIASTORAGE, PRIV_CAMERA, PRIV_INTERNET});
+ hlp.createSharedRODir();
+ appInstalls.emplace_back(ScopedInstaller(hlp));
+ }
+
+ for (const auto nConcurrentAppsDesc : nConcurrentAppsSamples) {
+ const auto nConcurrentApps = nConcurrentAppsDesc ?: 1;
+ const auto timeout = Timestamp::future(minTotalBenchTime / arraySize(nConcurrentAppsSamples));
+ do {
+ SynchronizationPipe synchPipe;
+ auto exitEvFd = eventfd(0, 0);
+ RUNNER_ASSERT(exitEvFd >= 0);
+
+ for (int i = 0; i < nConcurrentApps; i++) {
+ auto &app = apps[i];
+ const auto pid = fork();
+ RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "Fork failed");
+ if (pid)
+ app.pid = pid;
+ else {
+ synchPipe.claimChildEp();
+ RUNNER_ASSERT_ERRNO_MSG(setLauncherSecurityAttributes(tmpUser) == 0, "launcher failed");
+
+ const auto appId = app.hlp.getAppId();
+
+ synchPipe.post(); // declare readiness to start measuring
+ synchPipe.pollForWait(); // wait for parent to signal all kids simultaneously
+
+ const auto candBeg = Timestamp::now();
+ Api::prepareAppCandidate();
+ const auto candEnd = Timestamp::now();
+
+ if (!nConcurrentAppsDesc) {
+ for (int i = 0; i < nThreads; i++)
+ std::thread([]{ for (;;) usleep(1000); }).detach();
+ }
+
+ const auto prepBeg = Timestamp::now();
+ Api::prepareApp(appId);
+ const auto prepEnd = Timestamp::now();
+
+ const Timestamp ts[2] = { candEnd-candBeg, prepEnd-prepBeg };
+ synchPipe.post(ts, sizeof ts); // post measurement payload
+
+ // stay idle until all kids are done to simulate idle apps and reduce benchmark noise
+ pollfd fds[1];
+ fds->fd = exitEvFd;
+ fds->events = POLLIN;
+ auto ret = TEMP_FAILURE_RETRY(poll(fds, 1, -1));
+ RUNNER_ASSERT_ERRNO(ret > 0);
+
+ exit(0);
+ }
+ }
+ synchPipe.claimParentEp();
+
+ for (int i = 0; i < nConcurrentApps; i++) // wait for all kids to be ready to start measurement
+ synchPipe.wait();
+ synchPipe.post(); // signal all kids to start measuring
+
+ for (int i = 0; i < nConcurrentApps; i++) {
+ Timestamp ts[2];
+ synchPipe.wait(ts, sizeof ts);
+ candidate.emplace_back(ts[0]);
+ prepare.emplace_back(ts[1]);
+ everything.emplace_back(ts[0] + ts[1]);
+ }
+
+ RUNNER_ASSERT(!eventfd_write(exitEvFd, 1)); // signal all kids to exit now
+
+ for (int i = 0; i < nConcurrentApps; i++) {
+ const auto &app = apps[i];
+ waitPid(app.pid);
+ Api::cleanupApp(app.hlp.getAppId(), uid, app.pid);
+ }
+ } while (Timestamp::now() < timeout);
+
+ if (!nConcurrentAppsDesc)
+ std::cerr << "additionalThreads " << int(nThreads) << ' ';
+ std::cerr << "nConcurrentApps " << int(nConcurrentApps) << " samples " << candidate.size() << '\n';
+ std::cerr << " prepareAppCandidate [us]: ";
+ Timestamp::report(candidate.data(), candidate.size());
+ std::cerr << " prepareApp [us]: ";
+ Timestamp::report(prepare.data(), prepare.size());
+ std::cerr << " prepareAppCandidate + prepareApp [us]: ";
+ Timestamp::report(everything.data(), everything.size());
+ candidate.clear();
+ prepare.clear();
+ everything.clear();
}
}