Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend integration tests for coredump/backtrace #1318

Merged
merged 9 commits into from
Feb 14, 2023
13 changes: 7 additions & 6 deletions src/transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,13 @@ shutdownTlsSession(transport_t *trans)
}
}


void
transportInit(void)
{
OPENSSL_init_ssl(OPENSSL_INIT_NO_ATEXIT, NULL);
}

static void
handle_tls_destroy(void)
{
Expand Down Expand Up @@ -410,12 +417,6 @@ establishTlsSession(transport_t *trans)
if (!trans || trans->net.sock == -1) return FALSE;
scopeLogInfo("fd:%d establishing tls session", trans->net.sock);

static int init_called = FALSE;
if (!init_called) {
OPENSSL_init_ssl(OPENSSL_INIT_NO_ATEXIT, NULL);
init_called = TRUE;
}

trans->net.tls.ctx = SSL_CTX_new(TLS_method());
if (!trans->net.tls.ctx) {
char err[256] = {0};
Expand Down
1 change: 1 addition & 0 deletions src/transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ bool transportSupportsCommandControl(transport_t *);
transport_status_t transportConnectionStatus(transport_t *);

// Misc
void transportInit(void);
void transportRegisterForExitNotification(void (*fn)(void));

#endif // __TRANSPORT_H__
15 changes: 14 additions & 1 deletion src/wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,11 @@ init(void)
cfg = cfgRead(path);
}

// on aarch64, the crypto subsystem installs handlers for SIGILL
// (contrib/openssl/crypto/armcap.c) to determine which version of
// ARM processor we're on. Do this before enableSnapshot() below.
transportInit();

cfgProcessEnvironment(cfg);

doConfig(cfg);
Expand Down Expand Up @@ -1831,7 +1836,11 @@ init(void)
* Therefore, until that is investigated we don't
* enable a timer/signal.
*/
threadInit();
if (attachedFlag) {
threadNow(0);
} else {
threadInit();
}
}
} else {
/*
Expand Down Expand Up @@ -1888,7 +1897,11 @@ sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
* Condition below must be inline with `snapshotErrorsSignals` array
*/
if ((snapshotIsEnabled() == TRUE) && (act != NULL)) {
// if signum is part of the snapshotErrorsSignals, save the handler
// and set oldact, then return (without changing away from the
// snapshot handler)
if (snapshotBackupAppSignalHandler(signum, act->sa_handler) == TRUE) {
oldact->sa_handler = act->sa_handler; // equivalent to signal above
return 0;
}
}
Expand Down
6 changes: 3 additions & 3 deletions test/integration/cli/test_inspect.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interface_prop_check() {
local expected_state=$3
jq '.interfaces[] | select(.name=='\"$interface_name\"') | .'$interface_prop'' $INSPECT_FILE | grep -q "$expected_state"
if [ $? != 0 ]; then
echo "interface_check fails, params: $interface_name $interface_prop $interface_expected_state"
echo "interface_check fails, params: $interface_name $interface_prop $expected_state"
cat $INSPECT_FILE
ERR+=1
fi
Expand Down Expand Up @@ -100,7 +100,7 @@ nc -lU $CRIBL_SOCKET 1> /dev/null 2> /dev/null &
NC_PID=`pidof nc`

# Give time to connect
sleep 5
sleep 6
inspect_file_redirect_to_file $PYTHON_PID

interface_prop_check "log" "connected" "true"
Expand Down Expand Up @@ -136,7 +136,7 @@ nc -l -p 9109 1> /dev/null 2> /dev/null &
NC_PID=`pidof nc`

# # Give time to connect
sleep 5
sleep 6

inspect_file_redirect_to_file $PYTHON_PID

Expand Down
4 changes: 4 additions & 0 deletions test/integration/glibc/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ RUN gcc -o /opt/errno_test/errno_test /opt/errno_test/hello_errno.c
COPY ./glibc/hello_fault.c /opt/fault_test/hello_fault.c
RUN gcc -O0 -g -o /opt/fault_test/fault_test /opt/fault_test/hello_fault.c -lrt

RUN mkdir -p /opt/sig_test
COPY ./glibc/sighandler.c /opt/sig_test/sighandler.c
RUN gcc -g -o /opt/sig_test/sighandler /opt/sig_test/sighandler.c

RUN mkdir -p /opt/extract_scope && \
mkdir -p /opt/patch_libscope

Expand Down
60 changes: 60 additions & 0 deletions test/integration/glibc/scope-test
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,66 @@ fi

endtest


#
# attach_...
# Tests that libscope.so calls the the signal handler of the
# test application (./sighandler) after calling libscope.so's handler.
# In this attach case, it proves we're able to figure out what test app
# handlers to call after the test app has already registered them.
#
cd /opt/sig_test

for SIGNAL in SIGBUS SIGFPE SIGSEGV SIGILL
do
for FUNCTION in signal sigaction
do
starttest attach_${SIGNAL}_using_${FUNCTION}
./sighandler $FUNCTION > standardout.txt &
PID=`echo $!`
sleep 1
SCOPE_SNAPSHOT_COREDUMP=true scope attach $PID
sleep 1
kill -s $SIGNAL $PID
sleep 1
kill -s SIGCONT $PID
sleep 0.5
grep "Handling $SIGNAL from ./sighandler" standardout.txt
ERR+=$?
rm standardout.txt
endtest
done
done


#
# preloaded_...
# Tests that libscope.so calls the the signal handler of the
# test application (./sighandler) after calling libscope.so's handler.
# In this preloaded case, it proves we're interposing "signal()"
# and "sigaction()" correctly.
#
cd /opt/sig_test

for SIGNAL in SIGBUS SIGFPE SIGSEGV SIGILL
do
for FUNCTION in signal sigaction
do
starttest preloaded_${SIGNAL}_using_${FUNCTION}
SCOPE_SNAPSHOT_COREDUMP=true scope run --passthrough -- ./sighandler $FUNCTION > standardout.txt &
PID=`echo $!`
sleep 1
kill -s $SIGNAL $PID
sleep 1
kill -s SIGCONT $PID
sleep 0.5
grep "Handling $SIGNAL from ./sighandler" standardout.txt
ERR+=$?
rm standardout.txt
endtest
done
done

if (( $FAILED_TEST_COUNT == 0 )); then
echo ""
echo ""
Expand Down
134 changes: 134 additions & 0 deletions test/integration/glibc/sighandler.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// gcc -g -o sighandler sighandler.c

// This test app was created to handle a set of signals that the library
// also has an interest in. It can register for signals in different ways:
// signal() or sigaction().
//
// The goal is to test that our library can manage to call original
// test app signal handlers after the library's own handlers.
// This will be of interest in a couple senarios. When:
// 1) the library is preloaded and
// 2) when our library is injected at a later time.

#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

const char *progname;

#define toStdout(str) (write(STDOUT_FILENO, str, strlen(str)))
#define failure(str) do { printf(str); exit(1); } while (0)


typedef enum {SIGNAL, SIGACTION} func_t;
static const char * const funcToName[] = {
[SIGNAL] = "signal",
[SIGACTION] = "sigaction",
};
#define FUNC_COUNT (sizeof(funcToName)/sizeof(funcToName[0]))


// a handler that just prints something to stdout to show it's been run
void
handleit(char *signame)
{
// Ugly, but is a sig-safe alternative to:
// printf(" Handling %s from %s\n", signame, progname);
toStdout(" Handling ");
toStdout(signame);
toStdout(" from ");
toStdout(progname);
toStdout("\n");
fsync(STDOUT_FILENO);
}

void handleIll(int signum) {handleit("SIGILL");}
void handleBus(int signum) {handleit("SIGBUS");}
void handleFpe(int signum) {handleit("SIGFPE");}
void handleSegv(int signum) {handleit("SIGSEGV");}
void handleUsr2(int signum) {handleit("SIGUSR2");}

typedef struct {
int num;
char *str;
sighandler_t fn;
} sigList_t;

// These are the ones which libscope.so is interested in.
// So, these are ones we should test for interactions
sigList_t sigList[] = {
{.num = SIGILL, .str = "SIGILL", .fn = handleIll},
{.num = SIGBUS, .str = "SIGBUS", .fn = handleBus},
{.num = SIGFPE, .str = "SIGFPE", .fn = handleFpe},
{.num = SIGSEGV, .str = "SIGSEGV", .fn = handleSegv},
{.num = SIGUSR2, .str = "SIGUSR2", .fn = handleUsr2},
};
#define SIG_COUNT (sizeof(sigList)/sizeof(sigList[0]))

// registers the handler, using the func specified
void
registerSigHandlers(func_t func)
{
printf(" Executing %s using %s()\n", __func__, funcToName[func]);

int i;
for (i=0; i<SIG_COUNT; i++) {
printf(" Registering %s\n", sigList[i].str);
if (func == SIGNAL) {
if (signal(sigList[i].num, sigList[i].fn) == SIG_ERR) {
failure("signal() call failed\n");
}
} else if (func == SIGACTION) {
struct sigaction act = {.sa_handler = sigList[i].fn,
.sa_mask = 0,
.sa_flags = 0};
struct sigaction oldact;
if (sigaction(sigList[i].num, &act, &oldact) == -1) {
failure("sigaction() call failed\n");
}
} else {
failure("Unexpected function\n");
}
}
}


int
main(int argc, char *argv[])
{
progname = argv[0];

// Read command line argument to determine which function to use
// when registering our signal handler
printf("Starting execution of %s\n", progname);
if (argc != 2) {
failure("expected one argument, either \"signal\" or \"sigaction\"\n");
}

func_t function;
int i;
for (i=0; i<FUNC_COUNT; i++) {
if (!strcmp(argv[1], funcToName[i])) {
function = i;
break;
}
}
if (i >= FUNC_COUNT) {
failure("expected one argument, either \"signal\" or \"sigaction\"\n");
}

// register signal handler using the specified function
printf("Registering to handle signals\n");
registerSigHandlers(function);

// wait a while for signal from the outside
struct timespec time = {.tv_sec = 90, .tv_nsec = 0};
nanosleep(&time, NULL);

printf("Ending execution of %s\n", progname);
return 0;
}