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