static std::atomic<int> g_threads_count;
static cap_t g_cap;
+// needed to keep track which thread did receive signal and which did not
+// additionally keeps information about stage to which each thread got
+#define TID_STATUS_NOT_SYNCED 0
+#define TID_STATUS_RECEIVED_SIGNAL 1
+#define TID_STATUS_SETUP_SMACK 2
+#define TID_STATUS_FULLY_SYNCED 3
+#define TID_STATUS_DEAD 4
+
+struct TidStatus {
+ int tid;
+ int status;
+};
+
+static TidStatus *g_tid_status;
+
#define MAX_SIG_WAIT_TIME 1000
// Hackish, based on glibc's definition in sysdeps/unix/sysv/linux/nptl-signals.h
} // namespace Syscall
-inline static int label_for_self_internal()
+inline static int label_for_self_internal(int tid)
{
- const auto tid = Syscall::gettid();
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);
static inline int security_manager_sync_threads_internal(const std::string &app_label, std::vector<int> &synced_tids)
{
static_assert(ATOMIC_INT_LOCK_FREE == 2, "std::atomic<int> is not always lock free");
+ g_tid_status = NULL;
FS::FileNameVector files = FS::getSubDirectoriesFromDirectory("/proc/self/task", true);
LogDebug("number of threads to sync: " << files.size() - 1);
- const pid_t cur_tid = Syscall::gettid();
+ const int cur_tid = Syscall::gettid();
synced_tids.push_back(cur_tid);
if (files.size() > 1) {
const pid_t cur_pid = getpid();
g_threads_count = files.size() - 1;
+ g_tid_status = new TidStatus[g_threads_count];
+ if (!g_tid_status) {
+ LogError("Cannot allocate g_tid_status");
+ return SECURITY_MANAGER_ERROR_MEMORY;
+ } else {
+ for (int i = 0; i < g_threads_count; ++i) {
+ g_tid_status[i].tid = 0; // to be sure its not yet any actual TID
+ g_tid_status[i].status = TID_STATUS_NOT_SYNCED;
+ }
+ }
struct sigaction act;
struct sigaction old;
act.sa_flags = SA_RESTART;
act.sa_handler = [](int signo) {
(void)signo;
-
std::atomic_thread_fence(std::memory_order_acquire);
+ int tid = Syscall::gettid();
+ int i = 0;
+ for (; i < g_threads_count; ++i) {
+ if (g_tid_status[i].tid == tid) {
+ break;
+ }
+ }
+ g_tid_status[i].status = TID_STATUS_RECEIVED_SIGNAL;
if (g_p_app_label)
- if (label_for_self_internal() != 0)
+ if (label_for_self_internal(tid) != 0)
return;
-
+ g_tid_status[i].status = TID_STATUS_SETUP_SMACK;
if (cap_set_proc(g_cap))
return;
+ g_tid_status[i].status = TID_STATUS_FULLY_SYNCED;
g_threads_count--;
};
std::atomic_thread_fence(std::memory_order_release);
int threads_gone = 0;
+ int tid_index = 0;
for (auto const &e : files) {
const int tid = atoi(e.c_str());
- if (tid == static_cast<int>(cur_tid))
+ if (tid == cur_tid)
continue;
synced_tids.push_back(tid); // this will not add current TID (but its already added)
+ g_tid_status[tid_index++].tid = tid;
+
if (Syscall::tgkill(cur_pid, tid, SIGSETXID) < 0) {
const auto err = errno;
- if (ESRCH == err) // thread already gone
+ if (ESRCH == err) { // thread already gone
threads_gone++;
- else
+ g_tid_status[tid_index-1].status = TID_STATUS_DEAD;
+ } else
LogWithErrno(err, "tgkill()");
continue;
if (g_threads_count) {
LogError("Not all threads synchronized: threads left: " << g_threads_count);
- goto err;
+ for (int i = 0; i < g_threads_count; ++i) {
+ switch (g_tid_status[i].status) {
+ case TID_STATUS_DEAD:
+ /* this is totally fine */
+ LogError("Thread " << g_tid_status[i].tid << " died during attempt to send signal - info can be useful, but this is OK");
+ break;
+ case TID_STATUS_NOT_SYNCED:
+ LogError("Thread " << g_tid_status[i].tid << " didn't receive the signal, serious ERROR!!!");
+ break;
+ case TID_STATUS_RECEIVED_SIGNAL:
+ LogError("Thread " << g_tid_status[i].tid << " received signal but failed at Smack label change, serious ERROR!!!");
+ break;
+ case TID_STATUS_SETUP_SMACK:
+ LogError("Thread " << g_tid_status[i].tid << " received signal, changed Smack label, couldn't change caps, serious ERROR!!!");
+ break;
+ case TID_STATUS_FULLY_SYNCED:
+ /* this is totally fine */
+ LogError("Thread " << g_tid_status[i].tid << " received signal and was fully synchronized - info can be useful, but this is OK");
+ break;
+ default:
+ LogError("Thread " << g_tid_status[i].tid << " has strange status: " << g_tid_status[i].status << " this is an ERROR!!!");
+ break;
+ }
+ }
+ LogError("Aborting - please check coredump and check offending threads!");
+ abort(); // needed to debug the offending thread(s)
}
}
- if (g_p_app_label && label_for_self_internal() != 0) {
+ if (g_p_app_label && label_for_self_internal(cur_tid) != 0) {
LogError("label_for_self_internal failed");
goto err;
}
}
cap_free(g_cap);
+ if (g_tid_status)
+ delete [] g_tid_status;
return SECURITY_MANAGER_SUCCESS;
err:
cap_free(g_cap);
+ if (g_tid_status)
+ delete [] g_tid_status;
return SECURITY_MANAGER_ERROR_UNKNOWN;
}