guest_os_primitives: Add guest_signal.h
Initial skeleton of guest signal declarations. Needs followup to
implement fully.
Bug: 283499233
Bug: 283697533
Test: m berberis_all
Change-Id: Id492404c889f0262f61449b603624be7b19ba86a
diff --git a/guest_os_primitives/Android.bp b/guest_os_primitives/Android.bp
index 3e8e249..f7f31bd 100644
--- a/guest_os_primitives/Android.bp
+++ b/guest_os_primitives/Android.bp
@@ -41,6 +41,7 @@
host_supported: true,
srcs: [
"guest_map_shadow.cc",
+ "guest_signal_action.cc",
"guest_signal_handling.cc",
"guest_thread.cc",
"guest_thread_manager.cc",
diff --git a/guest_os_primitives/guest_signal_action.cc b/guest_os_primitives/guest_signal_action.cc
new file mode 100644
index 0000000..ee5176d
--- /dev/null
+++ b/guest_os_primitives/guest_signal_action.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "guest_signal_action.h"
+
+#include "berberis/base/macros.h"
+#include "berberis/guest_os_primitives/guest_signal.h"
+
+#include "host_signal.h"
+
+namespace berberis {
+
+bool GuestSignalAction::Change(int sig,
+ const Guest_sigaction* new_sa,
+ host_sa_sigaction_t claimed_host_sa_sigaction,
+ Guest_sigaction* old_sa,
+ int* error) {
+ // TODO(b/283499233): Implement.
+ UNUSED(sig, new_sa, claimed_host_sa_sigaction, old_sa, error);
+ return false;
+}
+
+} // namespace berberis
\ No newline at end of file
diff --git a/guest_os_primitives/guest_signal_action.h b/guest_os_primitives/guest_signal_action.h
new file mode 100644
index 0000000..205fb55
--- /dev/null
+++ b/guest_os_primitives/guest_signal_action.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BERBERIS_GUEST_OS_PRIMITIVES_GUEST_SIGNAL_ACTION_H_
+#define BERBERIS_GUEST_OS_PRIMITIVES_GUEST_SIGNAL_ACTION_H_
+
+#include <csignal>
+
+#include "berberis/base/checks.h"
+#include "berberis/base/macros.h"
+#include "berberis/guest_os_primitives/guest_signal.h"
+
+namespace berberis {
+
+// Signal is 'claimed' when it has guest handler/action. For a claimed signal, actual host action
+// is a wrapper that invokes guest code (or suspends handling until region exit).
+class GuestSignalAction {
+ public:
+ typedef void (*host_sa_sigaction_t)(int, siginfo_t*, void*);
+
+ constexpr GuestSignalAction()
+ : claimed_guest_sa_{.guest_sa_sigaction = Guest_SIG_DFL, .sa_flags = 0, .sa_mask = {}} {}
+
+ bool Change(int sig,
+ const Guest_sigaction* new_sa,
+ host_sa_sigaction_t claimed_host_sa_sigaction,
+ Guest_sigaction* old_sa,
+ int* error);
+
+ const Guest_sigaction& GetClaimedGuestAction() const {
+ CHECK(IsClaimed());
+ return claimed_guest_sa_;
+ }
+
+ private:
+ bool IsClaimed() const { return claimed_guest_sa_.guest_sa_sigaction != Guest_SIG_DFL; }
+
+ void Claim(const Guest_sigaction* sa) {
+ CHECK_NE(Guest_SIG_DFL, sa->guest_sa_sigaction);
+ claimed_guest_sa_ = *sa;
+ }
+
+ void Unclaim() { claimed_guest_sa_.guest_sa_sigaction = Guest_SIG_DFL; }
+
+ Guest_sigaction claimed_guest_sa_; // Guest_SIG_DFL when not claimed.
+
+ DISALLOW_COPY_AND_ASSIGN(GuestSignalAction);
+};
+
+} // namespace berberis
+
+#endif // BERBERIS_GUEST_OS_PRIMITIVES_GUEST_SIGNAL_ACTION_H_
\ No newline at end of file
diff --git a/guest_os_primitives/guest_signal_handling.cc b/guest_os_primitives/guest_signal_handling.cc
index a8f2800..a851b5a 100644
--- a/guest_os_primitives/guest_signal_handling.cc
+++ b/guest_os_primitives/guest_signal_handling.cc
@@ -14,10 +14,47 @@
* limitations under the License.
*/
+#include <mutex>
+
+#include "berberis/base/macros.h"
+#include "berberis/base/tracing.h"
+#include "berberis/guest_os_primitives/guest_signal.h"
#include "berberis/guest_os_primitives/guest_thread.h"
+#include "guest_signal_action.h"
+
namespace berberis {
+namespace {
+
+GuestSignalAction g_signal_actions[Guest__KERNEL__NSIG];
+std::mutex g_signal_actions_guard_mutex;
+
+// Can be interrupted by another HandleHostSignal!
+void HandleHostSignal(int sig, siginfo_t* info, void* context) {
+ // TODO(b/283499233): Implement.
+ UNUSED(sig, info, context);
+}
+
+bool IsReservedSignal(int signal) {
+ switch (signal) {
+ // Disallow guest action for SIGABRT to simplify debugging (b/32167022).
+ case SIGABRT:
+#if defined(__BIONIC__)
+ // Disallow overwriting the host profiler handler from guest code. Otherwise
+ // guest __libc_init_profiling_handlers() would install its own handler, which
+ // is not yet supported for guest code (at least need a proxy for
+ // heapprofd_client.so) and fundamentally cannot be supported for host code.
+ // TODO(b/167966989): Instead intercept __libc_init_profiling_handlers.
+ case BIONIC_SIGNAL_PROFILER:
+#endif
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
bool GuestThread::ProcessAndDisablePendingSignals() {
// TODO(b/280551353): Implement.
return true; // Previous state: enabled.
@@ -28,4 +65,22 @@
return false; // Previous state: disabled.
};
+bool SetGuestSignalHandler(int signal,
+ const Guest_sigaction* act,
+ Guest_sigaction* old_act,
+ int* error) {
+ if (signal < 1 || signal > Guest__KERNEL__NSIG) {
+ *error = EINVAL;
+ return false;
+ }
+
+ if (act && IsReservedSignal(signal)) {
+ TRACE("sigaction for reserved signal %d not set", signal);
+ act = nullptr;
+ }
+
+ std::lock_guard<std::mutex> lock(g_signal_actions_guard_mutex);
+ return g_signal_actions[signal - 1].Change(signal, act, HandleHostSignal, old_act, error);
+}
+
} // namespace berberis
\ No newline at end of file
diff --git a/guest_os_primitives/include/berberis/guest_os_primitives/guest_signal.h b/guest_os_primitives/include/berberis/guest_os_primitives/guest_signal.h
new file mode 100644
index 0000000..fae0812
--- /dev/null
+++ b/guest_os_primitives/include/berberis/guest_os_primitives/guest_signal.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BERBERIS_GUEST_OS_PRIMITIVES_GUEST_SIGNAL_H_
+#define BERBERIS_GUEST_OS_PRIMITIVES_GUEST_SIGNAL_H_
+
+#include <csignal>
+#include <cstring>
+
+#include "berberis/base/struct_check.h"
+#include "berberis/guest_state/guest_addr.h"
+
+namespace berberis {
+
+// Maximum number of signals for the guest kernel.
+constexpr int Guest__KERNEL__NSIG = 64;
+
+const GuestAddr Guest_SIG_DFL = 0;
+const GuestAddr Guest_SIG_IGN = 1;
+const GuestAddr Guest_SIG_ERR = -1;
+
+// Guest siginfo_t, as expected by guest rt_sigqueueinfo syscall.
+using Guest_siginfo_t = siginfo_t;
+
+// Guest sigset_t, as expected by guest rt_sigprocmask syscall.
+#if defined(BERBERIS_GUEST_LP64)
+typedef struct {
+ unsigned long __bits[1];
+} Guest_sigset_t;
+CHECK_STRUCT_LAYOUT(Guest_sigset_t, 64, 64);
+#else
+// TODO(b/283352810): Explicitly support ILP32 guest data model.
+// This condition currently assumes ILP32 support.
+typedef struct {
+ unsigned long __bits[2];
+} Guest_sigset_t;
+CHECK_STRUCT_LAYOUT(Guest_sigset_t, 64, 32);
+#endif
+
+// TODO(b/280551353): check other SA_* flags!
+static_assert(SA_NODEFER == 0x40000000, "Host and guest SA_NODEFER don't match");
+
+template <typename SmallSigset, typename BigSigset>
+inline void ConvertToSmallSigset(const BigSigset& big_sigset, SmallSigset* small_sigset) {
+ static_assert(sizeof(SmallSigset) <= sizeof(BigSigset), "wrong sigset size");
+ memcpy(small_sigset, &big_sigset, sizeof(SmallSigset));
+}
+
+template <typename SmallSigset, typename BigSigset>
+inline void ConvertToBigSigset(const SmallSigset& small_sigset, BigSigset* big_sigset) {
+ static_assert(sizeof(SmallSigset) <= sizeof(BigSigset), "wrong sigset size");
+ memset(big_sigset, 0, sizeof(BigSigset));
+ memcpy(big_sigset, &small_sigset, sizeof(SmallSigset));
+}
+
+// TODO(b/283697533): Define Guest_sigaction differently depending on guest platform.
+// Guest struct (__kernel_)sigaction, as expected by rt_sigaction syscall.
+struct Guest_sigaction {
+ // Prefix avoids conflict with original 'sa_sigaction' defined as macro.
+ GuestAddr guest_sa_sigaction;
+ unsigned long sa_flags;
+ Guest_sigset_t sa_mask;
+};
+#if defined(BERBERIS_GUEST_LP64)
+CHECK_STRUCT_LAYOUT(Guest_sigaction, 192, 64);
+CHECK_FIELD_LAYOUT(Guest_sigaction, guest_sa_sigaction, 0, 64);
+CHECK_FIELD_LAYOUT(Guest_sigaction, sa_flags, 64, 64);
+CHECK_FIELD_LAYOUT(Guest_sigaction, sa_mask, 128, 64);
+#endif
+// TODO(b/283352810): Add checks for ILP32 guest data model.
+
+struct Guest_sigaction;
+bool SetGuestSignalHandler(int signal,
+ const Guest_sigaction* act,
+ Guest_sigaction* old_act,
+ int* error);
+
+} // namespace berberis
+
+#endif // BERBERIS_GUEST_OS_PRIMITIVES_GUEST_SIGNAL_H_
\ No newline at end of file