Add errno abort helper 05/322705/12
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Mon, 14 Apr 2025 17:12:37 +0000 (19:12 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 23 Apr 2025 17:47:17 +0000 (19:47 +0200)
When allocations and logging are disallowed there's no way to get the
errno value. Add a helper that puts the errno value in the stack using
template functions.

Also move the end of no-allocation area after the last barrier bump.

Change-Id: I864c2104e2fa617feb8764e64e8e73673dd1e2d5

src/client/client-security-manager.cpp

index 859765a5a7f942d545c889981aed33cb3efbfe4f..e11632502eac8dd62d0162c58c695c13eb596d2d 100644 (file)
@@ -47,6 +47,7 @@
 #include "utils.h"
 
 #include <atomic>
+#include <cassert>
 #include <cstdio>
 #include <cstdlib>
 #include <cxxabi.h>
 #include <stdlib.h>
 #include <unistd.h>
 
+namespace {
+
+class ErrnoAbort {
+    template<int Errno>
+    [[noreturn]] static void errnoHandler() noexcept
+    {
+        abort();
+    }
+
+    using AbortMap = std::unordered_map<int, void(*)()>;
+
+    template<int... Errno>
+    void addHandler()
+    {
+        ([&] {
+            [[maybe_unused]] bool inserted = map.try_emplace(Errno, &errnoHandler<Errno>).second;
+            assert(inserted);
+        }(), ...);
+    }
+
+    AbortMap map;
+
+public:
+    ErrnoAbort()
+    {
+        addHandler<EACCES,
+                   EAGAIN,
+                   EBADF,
+                   EDQUOT,
+                   EFAULT,
+                   EFBIG,
+                   EINTR,
+                   EINVAL,
+                   EISDIR,
+                   EIO,
+                   ELOOP,
+                   EMFILE,
+                   ENAMETOOLONG,
+                   ENFILE,
+                   ENOENT,
+                   ENOMEM,
+                   ENOSPC,
+                   ENOTDIR,
+                   EOVERFLOW,
+                   EPERM,
+                   ERANGE,
+                   ESRCH>();
+    }
+
+    [[noreturn]] void call() const noexcept
+    {
+        int err = errno;
+        auto it = map.find(err);
+        if (it == map.end())
+            abort();
+
+        it->second();
+        abort();
+    }
+};
+
+const ErrnoAbort errnoAbort;
+
+} // namespace
+
 static const char *EMPTY = "";
 static const std::string SMACK_SYSTEM = "System";
 static std::string SMACK_SYSTEM_PRIVILEGED = "System::Privileged";
@@ -597,17 +663,25 @@ inline static int sigaction(int signum, const struct sigaction *act, struct siga
 
 } // namespace Syscall
 
-inline static int label_for_self_internal(int tid)
+inline static int label_for_self_internal(int tid, void(*error_handler)() = nullptr)
 {
     char threadSelf[sizeof "/proc//attr/current" + 1 + std::numeric_limits<unsigned>::digits10];
     sprintf(threadSelf, "/proc/%u/attr/current", unsigned(tid));
     int fd = open(threadSelf, O_WRONLY);
-    if (fd < 0)
+    if (fd < 0) {
+        if (error_handler)
+            error_handler();
         return -1;
+    }
     int ret = write(fd, g_p_app_label->c_str(), g_p_app_label->length());
     close(fd);
+    if (ret < 0) {
+        if (error_handler)
+            error_handler();
+        return -1;
+    }
 
-    return ret < 0 ? -1 : 0;
+    return 0;
 }
 
 // Checks if caller has required capabilities configured properly.
@@ -669,7 +743,7 @@ static int get_alive_threads(int own_tid, bool abort_on_error) noexcept
 {
     auto handle_error = [&]{
         if (abort_on_error)
-            abort();
+            errnoAbort.call();
 
         return errno;
     };
@@ -684,11 +758,12 @@ static int get_alive_threads(int own_tid, bool abort_on_error) noexcept
     char buf[1024];
     struct dirent64 *d;
     int bpos = 0;
-    for(;;) {
+    int ret = 0;
+    for(;ret == 0;) {
         ssize_t nread = getdents64(dir_fd, buf, sizeof(buf));
         if (nread == -1) {
-            close(dir_fd);
-            return handle_error();
+            ret = handle_error();
+            break;
         }
 
         if (nread == 0)
@@ -710,8 +785,8 @@ static int get_alive_threads(int own_tid, bool abort_on_error) noexcept
             errno = 0;
             long tid = strtol(d->d_name, &endptr, 10);
             if (errno != 0) {
-                close(dir_fd);
-                return handle_error();
+                ret = handle_error();
+                break;
             }
 
             if (tid == own_tid)
@@ -733,7 +808,7 @@ static int get_alive_threads(int own_tid, bool abort_on_error) noexcept
     }
 
     close(dir_fd);
-    return 0;
+    return ret;
 }
 
 static void check_threads(int own_tid) noexcept
@@ -790,14 +865,14 @@ static void signal_and_wait_for_handlers(pid_t own_pid, int own_tid) noexcept
                                 g_thread_alive[i] = false;
                                 continue;
                             } else {
-                                abort();
+                                errnoAbort.call();
                             }
                         }
                     } else if (ESRCH == err) { // thread already gone - noop
                         g_thread_alive[i] = false;
                         continue;
                     } else {
-                        abort();
+                        errnoAbort.call();
                     }
                 }
             }
@@ -950,18 +1025,11 @@ static inline int security_manager_sync_threads_internal(const ProcessId &proces
     /*
      * Change attributes of one last thread, the main thread.
      */
-    if (smack_simple_check()) {
-        if (g_p_app_label && label_for_self_internal(own_tid) != 0)
-            abort();
-    }
+    if (smack_simple_check() && g_p_app_label)
+        label_for_self_internal(own_tid, []{ errnoAbort.call(); });
 
     if (cap_set_proc(g_cap))
-        abort();
-
-    // No allocations allowed before this point
-
-    if (enabled)
-        SecurityManager::Log::LogSystemSingleton::Instance().Enable(true);
+        errnoAbort.call();
 
     if (smack_simple_check()) {
         // This starts signal handlers to proceed once they wake up - logic in app starts in env
@@ -969,6 +1037,11 @@ static inline int security_manager_sync_threads_internal(const ProcessId &proces
         g_th_barrier++;
     }
 
+    // No allocations allowed before this point
+
+    if (enabled)
+        SecurityManager::Log::LogSystemSingleton::Instance().Enable(true);
+
     if (Syscall::sigaction(SIGSETXID, &old, nullptr) < 0) {
         LogError("Error in sigaction()");
         abort();