Add an abort handler and preload it on Linux (#1456)

Some native code (CEF) may cause SIGTRAP to be sent on fatal errors,
which brings down the entire server. Instead only kill the thread and
attempt to continue.
This commit is contained in:
Constantin Piber
2025-06-21 18:02:05 +02:00
committed by GitHub
parent 20c850c10b
commit bd7ea64b02
6 changed files with 72 additions and 0 deletions

1
.gitignore vendored
View File

@@ -20,3 +20,4 @@ scripts/OpenJDK*
scripts/zulu* scripts/zulu*
scripts/electron-* scripts/electron-*
scripts/rcedit-* scripts/rcedit-*
scripts/resources/*.so

View File

@@ -38,6 +38,10 @@ main() {
download_launcher download_launcher
if [ ! -f scripts/resources/catch_abort.so ]; then
gcc -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -shared scripts/resources/catch_abort.c -lpthread -o scripts/resources/catch_abort.so
fi
case "$OS" in case "$OS" in
debian-all) debian-all)
RELEASE="$RELEASE_NAME.deb" RELEASE="$RELEASE_NAME.deb"
@@ -184,6 +188,7 @@ make_linux_bundle() {
cp "$JAR" "$RELEASE_NAME/bin/Suwayomi-Server.jar" cp "$JAR" "$RELEASE_NAME/bin/Suwayomi-Server.jar"
cp "scripts/resources/suwayomi-launcher.sh" "$RELEASE_NAME/" cp "scripts/resources/suwayomi-launcher.sh" "$RELEASE_NAME/"
cp "scripts/resources/suwayomi-server.sh" "$RELEASE_NAME/" cp "scripts/resources/suwayomi-server.sh" "$RELEASE_NAME/"
cp "scripts/resources/catch_abort.so" "$RELEASE_NAME/bin/"
tar -I "gzip -9" -cvf "$RELEASE" "$RELEASE_NAME/" tar -I "gzip -9" -cvf "$RELEASE" "$RELEASE_NAME/"
} }
@@ -208,6 +213,7 @@ make_deb_package() {
mv "$RELEASE_NAME/Suwayomi-Launcher.jar" "$RELEASE_NAME/$source_dir/Suwayomi-Launcher.jar" mv "$RELEASE_NAME/Suwayomi-Launcher.jar" "$RELEASE_NAME/$source_dir/Suwayomi-Launcher.jar"
cp "$JAR" "$RELEASE_NAME/$source_dir/Suwayomi-Server.jar" cp "$JAR" "$RELEASE_NAME/$source_dir/Suwayomi-Server.jar"
copy_linux_package_assets_to "$RELEASE_NAME/$source_dir/" copy_linux_package_assets_to "$RELEASE_NAME/$source_dir/"
cp "scripts/resources/catch_abort.so" "$RELEASE_NAME/$source_dir/"
tar -I "gzip" -C "$RELEASE_NAME/" -cvf "$upstream_source" "$source_dir" tar -I "gzip" -C "$RELEASE_NAME/" -cvf "$upstream_source" "$source_dir"
cp -r "scripts/resources/deb/" "$RELEASE_NAME/$source_dir/debian/" cp -r "scripts/resources/deb/" "$RELEASE_NAME/$source_dir/debian/"

View File

@@ -0,0 +1,62 @@
// Linux only:
// Attempts to catch SIGTRAP, inform Java, then exit the thread instead of bringing down the whole process
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <signal.h>
#include <pthread.h>
#include <execinfo.h>
#include <jni.h>
JavaVM *g_vm;
void load_vm() {
if (g_vm) return;
JavaVM *vms[1];
jsize n = 0;
// JNI_OnLoad won't be called when loaded via LD_PRELOAD, so attempt to find the VM now
if (JNI_GetCreatedJavaVMs(vms, 1, &n) == JNI_OK && n > 0) {
g_vm = vms[0];
}
}
jint throwThreadDeath(JNIEnv *env, char *message) {
char *className = "java/lang/UnknownError";
jclass exClass = (*env)->FindClass(env, className);
if (exClass == NULL) return JNI_ERR;
return (*env)->ThrowNew(env, exClass, message);
}
void signalHandler(int signum, siginfo_t* si, void* uc) {
void *retaddrs[64];
int n = backtrace(retaddrs, sizeof(retaddrs) / sizeof(retaddrs[0]));
printf("\n### ABORT :: Backtrace: ###\n");
backtrace_symbols_fd(retaddrs, n, STDERR_FILENO);
printf("### ABORT :: Exiting this thread. If this causes problems, please report the above backtrace to Suwayomi. ###\n\n");
load_vm();
if (g_vm) {
JNIEnv *env;
jint getEnvStat = (*g_vm)->GetEnv(g_vm, (void**) &env, JNI_VERSION_1_2);
if (getEnvStat == JNI_EDETACHED) (*g_vm)->AttachCurrentThread(g_vm, (void**) &env, NULL);
jint exStat = throwThreadDeath(env, "SIGTRAP caught");
if (exStat != 0) printf("Exception throwing failed: %d\n", exStat);
(*g_vm)->DetachCurrentThread(g_vm);
}
pthread_exit(NULL);
}
__attribute__((constructor))
void dlmain() {
struct sigaction sa = {0};
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sa.sa_sigaction = &signalHandler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGTRAP, &sa, NULL) != 0) {
printf("[FATAL] sigaction failed\n");
}
}

View File

@@ -11,3 +11,4 @@ suwayomi-server.tmpfiles => usr/lib/tmpfiles.d/suwayomi-server.conf
suwayomi-server.conf => etc/suwayomi/server.conf suwayomi-server.conf => etc/suwayomi/server.conf
suwayomi-server.sh => usr/bin/suwayomi-server suwayomi-server.sh => usr/bin/suwayomi-server
suwayomi-launcher.sh => usr/bin/suwayomi-launcher suwayomi-launcher.sh => usr/bin/suwayomi-launcher
catch_abort.so usr/share/java/suwayomi-server/bin/

View File

@@ -1,3 +1,4 @@
#!/bin/sh #!/bin/sh
export LD_PRELOAD="/usr/share/java/suwayomi-server/bin/catch_abort.so"
exec /usr/bin/java -jar /usr/share/java/suwayomi-server/bin/Suwayomi-Server.jar exec /usr/bin/java -jar /usr/share/java/suwayomi-server/bin/Suwayomi-Server.jar

View File

@@ -1,3 +1,4 @@
#!/bin/sh #!/bin/sh
export LD_PRELOAD="`realpath ./bin/catch_abort.so`"
exec ./jre/bin/java -jar ./bin/Suwayomi-Server.jar exec ./jre/bin/java -jar ./bin/Suwayomi-Server.jar