diff options
Diffstat (limited to 'sigchainlib')
-rw-r--r-- | sigchainlib/Android.mk | 29 | ||||
-rw-r--r-- | sigchainlib/sigchain.cc | 183 | ||||
-rw-r--r-- | sigchainlib/sigchain.h | 29 |
3 files changed, 241 insertions, 0 deletions
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk new file mode 100644 index 0000000000..cb1778dc8e --- /dev/null +++ b/sigchainlib/Android.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2014 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. +# + +LOCAL_PATH:= $(call my-dir) + +include art/build/Android.common.mk + +include $(CLEAR_VARS) +LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) +LOCAL_SRC_FILES := sigchain.cc +LOCAL_MODULE:= libsigchain +LOCAL_SHARED_LIBRARIES += liblog libdl +LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk +include $(BUILD_SHARED_LIBRARY) diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc new file mode 100644 index 0000000000..26e7d319cd --- /dev/null +++ b/sigchainlib/sigchain.cc @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014 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 <android/log.h> +#include <dlfcn.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +namespace art { + +class SignalAction { + public: + SignalAction() : claimed_(false) { + } + + // Claim the signal and keep the action specified. + void Claim(const struct sigaction& action) { + action_ = action; + claimed_ = true; + } + + // Unclaim the signal and restore the old action. + void Unclaim(int signal) { + claimed_ = false; + sigaction(signal, &action_, NULL); // Restore old action. + } + + // Get the action associated with this signal. + const struct sigaction& GetAction() const { + return action_; + } + + // Is the signal claimed? + bool IsClaimed() const { + return claimed_; + } + + // Change the recorded action to that specified. + void SetAction(const struct sigaction& action) { + action_ = action; + } + + private: + struct sigaction action_; // Action to be performed. + bool claimed_; // Whether signal is claimed or not. +}; + +// User's signal handlers +static SignalAction user_sigactions[_NSIG]; + +static void log(const char* format, ...) { + char buf[256]; + va_list ap; + va_start(ap, format); + vsnprintf(buf, sizeof(buf), format, ap); + __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); + va_end(ap); +} + +static void CheckSignalValid(int signal) { + if (signal <= 0 || signal >= _NSIG) { + log("Invalid signal %d", signal); + abort(); + } +} + +// Claim a signal chain for a particular signal. +void ClaimSignalChain(int signal, struct sigaction* oldaction) { + CheckSignalValid(signal); + user_sigactions[signal].Claim(*oldaction); +} + +void UnclaimSignalChain(int signal) { + CheckSignalValid(signal); + + user_sigactions[signal].Unclaim(signal); +} + +// Invoke the user's signal handler. +void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { + // Check the arguments. + CheckSignalValid(sig); + + // The signal must have been claimed in order to get here. Check it. + if (!user_sigactions[sig].IsClaimed()) { + abort(); + } + + const struct sigaction& action = user_sigactions[sig].GetAction(); + + // Only deliver the signal if the signal was not masked out. + if (sigismember(&action.sa_mask, sig)) { + return; + } + if ((action.sa_flags & SA_SIGINFO) == 0) { + if (action.sa_handler != NULL) { + action.sa_handler(sig); + } + } else { + if (action.sa_sigaction != NULL) { + action.sa_sigaction(sig, info, context); + } + } +} + +extern "C" { +// These functions are C linkage since they replace the functions in libc. + +int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) { + // If this signal has been claimed as a signal chain, record the user's + // action but don't pass it on to the kernel. + // Note that we check that the signal number is in range here. An out of range signal + // number should behave exactly as the libc sigaction. + if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) { + if (old_action != NULL) { + *old_action = user_sigactions[signal].GetAction(); + } + if (new_action != NULL) { + user_sigactions[signal].SetAction(*new_action); + } + return 0; + } + + // Will only get here if the signal chain has not been claimed. We want + // to pass the sigaction on to the kernel via the real sigaction in libc. + + void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction"); + if (linked_sigaction_sym == nullptr) { + log("Unable to find next sigaction in signal chain"); + abort(); + } + + typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*); + SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym); + return linked_sigaction(signal, new_action, old_action); +} + + +int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) { + const sigset_t* new_set_ptr = bionic_new_set; + sigset_t tmpset; + if (bionic_new_set != NULL) { + tmpset = *bionic_new_set; + + if (how == SIG_BLOCK) { + // Don't allow claimed signals in the mask. If a signal chain has been claimed + // we can't allow the user to block that signal. + for (int i = 0 ; i < _NSIG; ++i) { + if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) { + sigdelset(&tmpset, i); + } + } + } + new_set_ptr = &tmpset; + } + + void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask"); + if (linked_sigprocmask_sym == nullptr) { + log("Unable to find next sigprocmask in signal chain"); + abort(); + } + + typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*); + SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym); + return linked_sigprocmask(how, new_set_ptr, bionic_old_set); +} +} // extern "C" +} // namespace art + diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h new file mode 100644 index 0000000000..f6f2253d72 --- /dev/null +++ b/sigchainlib/sigchain.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 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 ART_SIGCHAINLIB_SIGCHAIN_H_ +#define ART_SIGCHAINLIB_SIGCHAIN_H_ + +#include <signal.h> +namespace art { + +void ClaimSignalChain(int signal, struct sigaction* oldaction); +void UnclaimSignalChain(int signal); +void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context); + +} // namespace art + +#endif // ART_SIGCHAINLIB_SIGCHAIN_H_ |