Fix segfault from Alpine version mismatch, add crash handler

The builder (rust:1.93-alpine) uses Alpine 3.23 but the runtime used
Alpine 3.19. The net-snmp-dev headers from 3.23 don't match
net-snmp-libs from 3.19, causing a segfault on the first SNMP call.

Also adds a SIGSEGV/SIGBUS/SIGABRT signal handler that prints a
diagnostic message instead of silently exiting with code 139.
This commit is contained in:
Graham McIntire 2026-02-10 14:14:07 -06:00
parent fa46d279a7
commit 29b94c09d4
No known key found for this signature in database
2 changed files with 36 additions and 2 deletions

View file

@ -52,8 +52,9 @@ RUN --mount=type=cache,id=cargo-registry-${TARGETARCH},target=/usr/local/cargo/r
BUILD_VERSION="$VERSION" cargo build --release --target "$RUST_TARGET" && \
cp "target/$RUST_TARGET/release/towerops-agent" /tmp/towerops-agent
# Runtime stage
FROM alpine:3.19
# Runtime stage - must match builder's Alpine version for ABI compatibility
# (rust:1.93-alpine uses Alpine 3.23)
FROM alpine:3.23
# Install runtime dependencies
# iputils provides ping with setuid root (doesn't require CAP_NET_RAW)

View file

@ -116,7 +116,40 @@ struct Args {
snmpv3_priv_pass: String,
}
fn install_crash_handler() {
unsafe {
// Install a signal handler for SIGSEGV/SIGBUS/SIGABRT so we get
// diagnostic output instead of a silent exit code 139.
extern "C" fn crash_handler(sig: libc::c_int) {
let name = match sig {
libc::SIGSEGV => "SIGSEGV (segmentation fault)",
libc::SIGBUS => "SIGBUS (bus error)",
libc::SIGABRT => "SIGABRT (abort)",
_ => "unknown signal",
};
let msg = format!(
"\n*** FATAL: {} (signal {})\n\
*** This is likely a bug in C FFI code (libnetsnmp).\n\
*** Set RUST_BACKTRACE=1 for more info.\n",
name, sig
);
unsafe {
libc::write(libc::STDERR_FILENO, msg.as_ptr() as _, msg.len());
// Re-raise with default handler to get the correct exit code
libc::signal(sig, libc::SIG_DFL);
libc::raise(sig);
}
}
libc::signal(libc::SIGSEGV, crash_handler as libc::sighandler_t);
libc::signal(libc::SIGBUS, crash_handler as libc::sighandler_t);
libc::signal(libc::SIGABRT, crash_handler as libc::sighandler_t);
}
}
fn main() {
install_crash_handler();
// Install ring as the default TLS crypto provider. Required because both
// ring and aws-lc-rs features are enabled transitively, so rustls can't
// auto-detect which one to use.