提交 892c54bb 编写于 作者: R rbackman

8020701: Avoid crashes in WatcherThread

Reviewed-by: acorn, dcubed, dsimms
上级 31bda49d
...@@ -259,3 +259,52 @@ const char* os::get_current_directory(char *buf, size_t buflen) { ...@@ -259,3 +259,52 @@ const char* os::get_current_directory(char *buf, size_t buflen) {
FILE* os::open(int fd, const char* mode) { FILE* os::open(int fd, const char* mode) {
return ::fdopen(fd, mode); return ::fdopen(fd, mode);
} }
os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() {
assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread");
}
/*
* See the caveats for this class in os_posix.hpp
* Protects the callback call so that SIGSEGV / SIGBUS jumps back into this
* method and returns false. If none of the signals are raised, returns true.
* The callback is supposed to provide the method that should be protected.
*/
bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) {
assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread");
assert(!WatcherThread::watcher_thread()->has_crash_protection(),
"crash_protection already set?");
if (sigsetjmp(_jmpbuf, 1) == 0) {
// make sure we can see in the signal handler that we have crash protection
// installed
WatcherThread::watcher_thread()->set_crash_protection(this);
cb.call();
// and clear the crash protection
WatcherThread::watcher_thread()->set_crash_protection(NULL);
return true;
}
// this happens when we siglongjmp() back
WatcherThread::watcher_thread()->set_crash_protection(NULL);
return false;
}
void os::WatcherThreadCrashProtection::restore() {
assert(WatcherThread::watcher_thread()->has_crash_protection(),
"must have crash protection");
siglongjmp(_jmpbuf, 1);
}
void os::WatcherThreadCrashProtection::check_crash_protection(int sig,
Thread* thread) {
if (thread != NULL &&
thread->is_Watcher_thread() &&
WatcherThread::watcher_thread()->has_crash_protection()) {
if (sig == SIGSEGV || sig == SIGBUS) {
WatcherThread::watcher_thread()->crash_protection()->restore();
}
}
}
...@@ -37,5 +37,24 @@ protected: ...@@ -37,5 +37,24 @@ protected:
}; };
/*
* Crash protection for the watcher thread. Wrap the callback
* with a sigsetjmp and in case of a SIGSEGV/SIGBUS we siglongjmp
* back.
* To be able to use this - don't take locks, don't rely on destructors,
* don't make OS library calls, don't allocate memory, don't print,
* don't call code that could leave the heap / memory in an inconsistent state,
* or anything else where we are not in control if we suddenly jump out.
*/
class WatcherThreadCrashProtection : public StackObj {
public:
WatcherThreadCrashProtection();
bool call(os::CrashProtectionCallback& cb);
static void check_crash_protection(int signal, Thread* thread);
private:
void restore();
sigjmp_buf _jmpbuf;
};
#endif #endif
...@@ -4684,6 +4684,34 @@ void os::pause() { ...@@ -4684,6 +4684,34 @@ void os::pause() {
} }
} }
os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() {
assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread");
}
/*
* See the caveats for this class in os_windows.hpp
* Protects the callback call so that raised OS EXCEPTIONS causes a jump back
* into this method and returns false. If no OS EXCEPTION was raised, returns
* true.
* The callback is supposed to provide the method that should be protected.
*/
bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) {
assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread");
assert(!WatcherThread::watcher_thread()->has_crash_protection(),
"crash_protection already set?");
bool success = true;
__try {
WatcherThread::watcher_thread()->set_crash_protection(this);
cb.call();
} __except(EXCEPTION_EXECUTE_HANDLER) {
// only for protection, nothing to do
success = false;
}
WatcherThread::watcher_thread()->set_crash_protection(NULL);
return success;
}
// An Event wraps a win32 "CreateEvent" kernel handle. // An Event wraps a win32 "CreateEvent" kernel handle.
// //
// We have a number of choices regarding "CreateEvent" win32 handle leakage: // We have a number of choices regarding "CreateEvent" win32 handle leakage:
......
...@@ -102,6 +102,20 @@ class win32 { ...@@ -102,6 +102,20 @@ class win32 {
static LONG WINAPI serialize_fault_filter(struct _EXCEPTION_POINTERS* e); static LONG WINAPI serialize_fault_filter(struct _EXCEPTION_POINTERS* e);
}; };
/*
* Crash protection for the watcher thread. Wrap the callback
* with a __try { call() }
* To be able to use this - don't take locks, don't rely on destructors,
* don't make OS library calls, don't allocate memory, don't print,
* don't call code that could leave the heap / memory in an inconsistent state,
* or anything else where we are not in control if we suddenly jump out.
*/
class WatcherThreadCrashProtection : public StackObj {
public:
WatcherThreadCrashProtection();
bool call(os::CrashProtectionCallback& cb);
};
class PlatformEvent : public CHeapObj<mtInternal> { class PlatformEvent : public CHeapObj<mtInternal> {
private: private:
double CachePad [4] ; // increase odds that _Event is sole occupant of cache line double CachePad [4] ; // increase odds that _Event is sole occupant of cache line
......
...@@ -401,6 +401,10 @@ JVM_handle_bsd_signal(int sig, ...@@ -401,6 +401,10 @@ JVM_handle_bsd_signal(int sig,
Thread* t = ThreadLocalStorage::get_thread_slow(); Thread* t = ThreadLocalStorage::get_thread_slow();
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
// (no destructors can be run)
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
SignalHandlerMark shm(t); SignalHandlerMark shm(t);
// Note: it's not uncommon that JNI code uses signal/sigset to install // Note: it's not uncommon that JNI code uses signal/sigset to install
......
...@@ -553,6 +553,10 @@ JVM_handle_linux_signal(int sig, ...@@ -553,6 +553,10 @@ JVM_handle_linux_signal(int sig,
Thread* t = ThreadLocalStorage::get_thread_slow(); Thread* t = ThreadLocalStorage::get_thread_slow();
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
// (no destructors can be run)
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
SignalHandlerMark shm(t); SignalHandlerMark shm(t);
// Note: it's not uncommon that JNI code uses signal/sigset to install // Note: it's not uncommon that JNI code uses signal/sigset to install
......
...@@ -225,6 +225,10 @@ JVM_handle_linux_signal(int sig, ...@@ -225,6 +225,10 @@ JVM_handle_linux_signal(int sig,
Thread* t = ThreadLocalStorage::get_thread_slow(); Thread* t = ThreadLocalStorage::get_thread_slow();
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
// (no destructors can be run)
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
SignalHandlerMark shm(t); SignalHandlerMark shm(t);
// Note: it's not uncommon that JNI code uses signal/sigset to install // Note: it's not uncommon that JNI code uses signal/sigset to install
......
...@@ -315,6 +315,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, ...@@ -315,6 +315,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
Thread* t = ThreadLocalStorage::get_thread_slow(); Thread* t = ThreadLocalStorage::get_thread_slow();
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
// (no destructors can be run)
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
SignalHandlerMark shm(t); SignalHandlerMark shm(t);
if(sig == SIGPIPE || sig == SIGXFSZ) { if(sig == SIGPIPE || sig == SIGXFSZ) {
......
...@@ -374,6 +374,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, ...@@ -374,6 +374,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
// (no destructors can be run)
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
SignalHandlerMark shm(t); SignalHandlerMark shm(t);
if(sig == SIGPIPE || sig == SIGXFSZ) { if(sig == SIGPIPE || sig == SIGXFSZ) {
......
...@@ -1370,6 +1370,10 @@ void Monitor::check_prelock_state(Thread *thread) { ...@@ -1370,6 +1370,10 @@ void Monitor::check_prelock_state(Thread *thread) {
debug_only(if (rank() != Mutex::special) \ debug_only(if (rank() != Mutex::special) \
thread->check_for_valid_safepoint_state(false);) thread->check_for_valid_safepoint_state(false);)
} }
if (thread->is_Watcher_thread()) {
assert(!WatcherThread::watcher_thread()->has_crash_protection(),
"locking not allowed when crash protection is set");
}
} }
void Monitor::check_block_state(Thread *thread) { void Monitor::check_block_state(Thread *thread) {
......
...@@ -595,6 +595,22 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) { ...@@ -595,6 +595,22 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) {
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1)); NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size)); NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
#ifdef ASSERT
// checking for the WatcherThread and crash_protection first
// since os::malloc can be called when the libjvm.{dll,so} is
// first loaded and we don't have a thread yet.
// try to find the thread after we see that the watcher thread
// exists and has crash protection.
WatcherThread *wt = WatcherThread::watcher_thread();
if (wt != NULL && wt->has_crash_protection()) {
Thread* thread = ThreadLocalStorage::get_thread_slow();
if (thread == wt) {
assert(!wt->has_crash_protection(),
"Can't malloc with crash protection from WatcherThread");
}
}
#endif
if (size == 0) { if (size == 0) {
// return a valid pointer if size is zero // return a valid pointer if size is zero
// if NULL is returned the calling functions assume out of memory. // if NULL is returned the calling functions assume out of memory.
......
...@@ -32,15 +32,18 @@ ...@@ -32,15 +32,18 @@
#include "utilities/top.hpp" #include "utilities/top.hpp"
#ifdef TARGET_OS_FAMILY_linux #ifdef TARGET_OS_FAMILY_linux
# include "jvm_linux.h" # include "jvm_linux.h"
# include <setjmp.h>
#endif #endif
#ifdef TARGET_OS_FAMILY_solaris #ifdef TARGET_OS_FAMILY_solaris
# include "jvm_solaris.h" # include "jvm_solaris.h"
# include <setjmp.h>
#endif #endif
#ifdef TARGET_OS_FAMILY_windows #ifdef TARGET_OS_FAMILY_windows
# include "jvm_windows.h" # include "jvm_windows.h"
#endif #endif
#ifdef TARGET_OS_FAMILY_bsd #ifdef TARGET_OS_FAMILY_bsd
# include "jvm_bsd.h" # include "jvm_bsd.h"
# include <setjmp.h>
#endif #endif
// os defines the interface to operating system; this includes traditional // os defines the interface to operating system; this includes traditional
...@@ -730,6 +733,10 @@ class os: AllStatic { ...@@ -730,6 +733,10 @@ class os: AllStatic {
#include "runtime/os_ext.hpp" #include "runtime/os_ext.hpp"
public: public:
class CrashProtectionCallback : public StackObj {
public:
virtual void call() = 0;
};
// Platform dependent stuff // Platform dependent stuff
#ifdef TARGET_OS_FAMILY_linux #ifdef TARGET_OS_FAMILY_linux
...@@ -908,6 +915,7 @@ class os: AllStatic { ...@@ -908,6 +915,7 @@ class os: AllStatic {
char pathSep); char pathSep);
static bool set_boot_path(char fileSep, char pathSep); static bool set_boot_path(char fileSep, char pathSep);
static char** split_path(const char* path, int* n); static char** split_path(const char* path, int* n);
}; };
// Note that "PAUSE" is almost always used with synchronization // Note that "PAUSE" is almost always used with synchronization
......
...@@ -1226,7 +1226,7 @@ WatcherThread* WatcherThread::_watcher_thread = NULL; ...@@ -1226,7 +1226,7 @@ WatcherThread* WatcherThread::_watcher_thread = NULL;
bool WatcherThread::_startable = false; bool WatcherThread::_startable = false;
volatile bool WatcherThread::_should_terminate = false; volatile bool WatcherThread::_should_terminate = false;
WatcherThread::WatcherThread() : Thread() { WatcherThread::WatcherThread() : Thread(), _crash_protection(NULL) {
assert(watcher_thread() == NULL, "we can only allocate one WatcherThread"); assert(watcher_thread() == NULL, "we can only allocate one WatcherThread");
if (os::create_thread(this, os::watcher_thread)) { if (os::create_thread(this, os::watcher_thread)) {
_watcher_thread = this; _watcher_thread = this;
......
...@@ -733,6 +733,8 @@ class WatcherThread: public Thread { ...@@ -733,6 +733,8 @@ class WatcherThread: public Thread {
static bool _startable; static bool _startable;
volatile static bool _should_terminate; // updated without holding lock volatile static bool _should_terminate; // updated without holding lock
os::WatcherThreadCrashProtection* _crash_protection;
public: public:
enum SomeConstants { enum SomeConstants {
delay_interval = 10 // interrupt delay in milliseconds delay_interval = 10 // interrupt delay in milliseconds
...@@ -760,6 +762,14 @@ class WatcherThread: public Thread { ...@@ -760,6 +762,14 @@ class WatcherThread: public Thread {
// Otherwise the first task to enroll will trigger the start // Otherwise the first task to enroll will trigger the start
static void make_startable(); static void make_startable();
void set_crash_protection(os::WatcherThreadCrashProtection* crash_protection) {
assert(Thread::current()->is_Watcher_thread(), "Can only be set by WatcherThread");
_crash_protection = crash_protection;
}
bool has_crash_protection() const { return _crash_protection != NULL; }
os::WatcherThreadCrashProtection* crash_protection() const { return _crash_protection; }
private: private:
int sleep() const; int sleep() const;
}; };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册