Skip to content

Commit

Permalink
Fixes skupperproject#1254: add function name to panic call stack output
Browse files Browse the repository at this point in the history
  • Loading branch information
kgiusti committed Oct 24, 2023
1 parent 42bdc2d commit 812fb69
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/clang-analyzer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
run: |
sudo pip3 install codechecker
# disable IPO to make compilation faster
cmake -S . -B build -DBUILD_TESTING=OFF -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF -DQD_ENABLE_ASSERTIONS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake -S . -B build -DBUILD_TESTING=OFF -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF -DQD_ENABLE_ASSERTIONS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DENABLE_WARNING_ERROR=OFF
# perform build so that generated sources get generated
cmake --build build
CodeChecker analyze build/compile_commands.json -o ./reports
Expand Down
45 changes: 37 additions & 8 deletions router/src/panic.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,24 +166,32 @@ static void print(const char *str)
fsync(STDERR_FILENO);
}

// print a register as a hex string
// async signal safe
// Convert an unsigned value into a hex string and print it. Async signal safe. If trim then discard leading zeros.
//
static void print_reg(uintptr_t reg)
static void print_hex_trim(uintptr_t value, bool trim)
{
int i = sizeof(reg) * 2;
int i = sizeof(value) * 2;
unsigned char *ptr = &buffer[BUFFER_SIZE - 1];

*ptr = 0;
while (i--) {
uint8_t nybble = reg & 0x0F;
uint8_t nybble = value & 0x0F;

*--ptr = nybble > 9 ? (nybble - 10) + 'a' : nybble + '0';
reg >>= 4;
value >>= 4;
if (value == 0 && trim)
break;
}
print((char *) ptr);
}

// Print a register in hex. No leading '0x' added.
//
static void print_reg(uintptr_t reg)
{
print_hex_trim(reg, false);
}

// print a base-ten unsigned integer
// async signal safe
//
Expand All @@ -205,6 +213,13 @@ static void print_uint(uintptr_t num)

#ifdef HAVE_LIBUNWIND

// Print an address offset, stripping leading zeros
//
static void print_offset(uintptr_t offset)
{
print_hex_trim(offset, true);
}

// given an address find it's mapping and offset
// async signal safe
//
Expand Down Expand Up @@ -320,11 +335,25 @@ static void print_stack_frame(int index, unw_cursor_t *cursor)
print("] IP: 0x");
print_reg(ip);

// Cannot reliably invoke unw_get_proc_name() from a signal handler due to a bug:
// https://github.com/libunwind/libunwind/issues/123
// v1.8.0 hasn't been released yet but I'm assuming it will contain the fix
//
if (UNW_VERSION_MAJOR > 1 || (UNW_VERSION_MAJOR == 1 && UNW_VERSION_MINOR >= 8)) {
unw_word_t off = 0;
if (unw_get_proc_name(cursor, (char *) buffer, BUFFER_SIZE, &off) == 0) {
print(":");
print((char *) buffer);
print("+0x");
print_offset((uintptr_t) off);
}
}

if (get_offset((uintptr_t) ip, &offset, &path)) {
print(" (");
print(path);
print(" + 0x");
print_reg(offset);
print("+0x");
print_offset(offset);
print(")");
}
print("\n");
Expand Down
10 changes: 0 additions & 10 deletions src/adaptors/http1/http1_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,16 +269,6 @@ static void _handle_listener_accept(qd_adaptor_listener_t *adaptor_listener, pn_
//
qd_http_listener_t *qd_http1_configure_listener(qd_http_listener_t *li, qd_dispatch_t *qd, qd_entity_t *entity)
{
if (qd_router_test_hooks_enabled()) {
// Check for test command to trigger core dump functionality. This is only used in CI.
// See ISSUE-1083
if (strcmp(li->config->adaptor_config->host, "$FORCE$CRASH$SEGV$") == 0) {
volatile int *badptr = (int *) 0xBEEFFACE;

*badptr = 42; // this is expected to cause the router to crash!
}
}

if (li->config->adaptor_config->ssl_profile_name) {
li->tls_domain = qd_tls_domain(li->config->adaptor_config, qd, LOG_HTTP_ADAPTOR, http1_alpn_protocols,
HTTP1_NUM_ALPN_PROTOCOLS, true);
Expand Down
75 changes: 74 additions & 1 deletion src/router_core/modules/test_hooks/core_test_hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>

typedef enum {
TEST_NODE_ECHO,
Expand All @@ -37,7 +40,8 @@ typedef enum {
TEST_NODE_SOURCE,
TEST_NODE_SOURCE_PS,
TEST_NODE_DISCARD,
TEST_NODE_LOG, // trigger a log message
TEST_NODE_LOG, // trigger a log message
TEST_NODE_CRASH, // force the router to crash
} test_node_behavior_t;

typedef struct test_module_t test_module_t;
Expand Down Expand Up @@ -76,12 +80,14 @@ struct test_module_t {
test_node_t *source_ps_node;
test_node_t *discard_node;
test_node_t *log_node;
test_node_t *crash_node;
test_client_t *test_client;
};


static void endpoint_action(qdr_core_t *core, qdr_action_t *action, bool discard);
static void _handle_log_request(qdr_core_t *core, qd_message_t *message);
static void _handle_crash_request(qdr_core_t *core, qd_message_t *message);

static void source_send(test_endpoint_t *ep, bool presettled)
{
Expand Down Expand Up @@ -165,6 +171,7 @@ static void endpoint_action(qdr_core_t *core, qdr_action_t *action, bool discard

case TEST_NODE_ECHO :
case TEST_NODE_LOG :
case TEST_NODE_CRASH :
break;
}
}
Expand Down Expand Up @@ -219,6 +226,14 @@ static void on_first_attach(void *bind_context,
error = qdr_error("qd:forbidden", "Test log function only accepts incoming links");
}
break;

case TEST_NODE_CRASH:
if (incoming) {
qdrc_endpoint_flow_CT(node->core, endpoint, 1, false);
} else {
error = qdr_error("qd:forbidden", "Test crash function only accepts incoming links");
}
break;
}

if (!!error) {
Expand Down Expand Up @@ -302,6 +317,7 @@ static void on_flow(void *link_context,

case TEST_NODE_ECHO :
case TEST_NODE_LOG :
case TEST_NODE_CRASH :
break;
}
}
Expand Down Expand Up @@ -348,6 +364,13 @@ static void on_transfer(void *link_context,
_handle_log_request(ep->node->core, message);
qdrc_endpoint_settle_CT(ep->node->core, delivery, PN_ACCEPTED);
qdrc_endpoint_flow_CT(ep->node->core, ep->ep, 1, false);
break;

case TEST_NODE_CRASH:
_handle_crash_request(ep->node->core, message); // not expected to return
qdrc_endpoint_settle_CT(ep->node->core, delivery, PN_ACCEPTED);
qdrc_endpoint_flow_CT(ep->node->core, ep->ep, 1, false);
break;
}
}

Expand Down Expand Up @@ -415,6 +438,7 @@ static test_module_t *qdrc_test_hooks_core_endpoint_setup(qdr_core_t *core, test
char *source_ps_address = "io.skupper.router.router/test/source_ps";
char *discard_address = "io.skupper.router.router/test/discard";
char *log_address = "io.skupper.router.router/test/log";
char *crash_address = "io.skupper.router.router/test/crash";

module->echo_node = NEW(test_node_t);
module->deny_node = NEW(test_node_t);
Expand All @@ -423,6 +447,7 @@ static test_module_t *qdrc_test_hooks_core_endpoint_setup(qdr_core_t *core, test
module->source_ps_node = NEW(test_node_t);
module->discard_node = NEW(test_node_t);
module->log_node = NEW(test_node_t);
module->crash_node = NEW(test_node_t);

module->echo_node->core = core;
module->echo_node->module = module;
Expand Down Expand Up @@ -480,6 +505,14 @@ static test_module_t *qdrc_test_hooks_core_endpoint_setup(qdr_core_t *core, test
DEQ_INIT(module->log_node->out_links);
qdrc_endpoint_bind_mobile_address_CT(core, log_address, &descriptor, module->log_node);

module->crash_node->core = core;
module->crash_node->module = module;
module->crash_node->behavior = TEST_NODE_CRASH;
module->crash_node->desc = &descriptor;
DEQ_INIT(module->crash_node->in_links);
DEQ_INIT(module->crash_node->out_links);
qdrc_endpoint_bind_mobile_address_CT(core, crash_address, &descriptor, module->crash_node);

return module;
}

Expand All @@ -493,6 +526,7 @@ static void qdrc_test_hooks_core_endpoint_finalize(test_module_t *module)
free(module->source_ps_node);
free(module->discard_node);
free(module->log_node);
free(module->crash_node);
}


Expand Down Expand Up @@ -711,6 +745,45 @@ static void _handle_log_request(qdr_core_t *core, qd_message_t *message)
qd_log(LOG_ROUTER, level, "This is a test log message.");
}

// TEST_NODE_CRASH - Force the router to crash. This is for testing the panic handler (see panic.c)
//
static void _handle_crash_request(qdr_core_t *core, qd_message_t *message)
{
// Test hooks are only enabled in CI. If anyone is crazy enough to enable them in production they get the keep the
// pieces. Dump a log just in case someone reports this as a bug:
qd_log(LOG_ROUTER, QD_LOG_CRITICAL, "Forced Router CRASH via Test Hooks");
sleep(2);
qd_iterator_t *s_itr = qd_message_field_iterator(message, QD_FIELD_SUBJECT);
if (s_itr) {
if (qd_iterator_equal(s_itr, (unsigned char *) "ABORT")) {
abort();
} else if (qd_iterator_equal(s_itr, (unsigned char *) "SEGV")) {
volatile int *badptr = (int *) 0xBEEFFACE;
*badptr = 42; // this is expected to cause the router to crash!
} else if (qd_iterator_equal(s_itr, (unsigned char *) "HEAP")) {
// keep overwriting the heap until we crash
while (true) {
// tell the code analyzer that it should mind its own business...
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wunknown-warning-option"
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
#pragma GCC diagnostic ignored "-Wanalyzer-possible-null-dereference"
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
// NOLINTBEGIN
volatile unsigned long *ptr = (unsigned long *) qd_malloc(sizeof(unsigned long));
for (int k = 0; k < 2 * 1024 * 1024; k++)
*ptr++ = ~0UL;
// NOLINTEND
#pragma GCC diagnostic pop
sched_yield(); // let other threads try to malloc...
}
}
qd_iterator_free(s_itr);
}

}

static bool qdrc_test_hooks_enable_CT(qdr_core_t *core)
{
return qd_router_test_hooks_enabled();
Expand Down
Loading

0 comments on commit 812fb69

Please sign in to comment.