GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
mhd_panic_tripwire.h File Reference

Test-only tripwire that turns a fatal libmicrohttpd invariant failure into a loud, greppable, machine-classifiable test error. More...

#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
Include dependency graph for mhd_panic_tripwire.h:

Go to the source code of this file.

Macros

#define MHD_PANIC_TRIPWIRE_MARKER   "MHD-PANIC-TRIPWIRE"
 
#define MHD_PANIC_TRIPWIRE_EXIT_STATUS   99
 
#define MHD_PANIC_TRIPWIRE_UNUSED_
 
#define MHD_PANIC_TRIPWIRE_NORETURN_
 
#define MHD_PANIC_TRIPWIRE_WRS_(str)
 

Functions

static void mhd_panic_tripwire_wr_ (const char *buf, size_t len)
 
static void mhd_panic_tripwire_wr_clean_ (const char *str)
 
static void mhd_panic_tripwire_wr_num_ (unsigned long val)
 
static void mhd_panic_tripwire_backtrace_ (void)
 
static MHD_PANIC_TRIPWIRE_NORETURN_ void mhd_panic_tripwire_report_ (const char *kind, const char *file, unsigned int line, const char *reason)
 
static void mhd_panic_tripwire_panic_cb_ (void *cls, const char *file, unsigned int line, const char *reason)
 
static void mhd_panic_tripwire_abrt_cb_ (int sig)
 
static int mhd_panic_tripwire_is_off_ (const char *val)
 
static MHD_PANIC_TRIPWIRE_UNUSED_ void mhd_panic_tripwire_install (void)
 

Variables

static volatile sig_atomic_t mhd_panic_tripwire_reraise_ = 0
 
static volatile sig_atomic_t mhd_panic_tripwire_status_
 
static volatile sig_atomic_t mhd_panic_tripwire_busy_ = 0
 

Detailed Description

Test-only tripwire that turns a fatal libmicrohttpd invariant failure into a loud, greppable, machine-classifiable test error.

Author
Christian Grothoff

Rationale (TESTING.md, proposal P5): no network input must ever be able to reach MHD_PANIC() or a failing mhd_assert(). When it does, the test suite should say so in a way that a CI job or the option-matrix driver can pick out mechanically, instead of leaving behind an anonymous SIGABRT that is indistinguishable from an ordinary test failure.

USAGE

Add exactly one line to a test program:

#include "mhd_panic_tripwire.h"

Nothing else is needed: on GCC/clang the header installs itself from a __attribute__((constructor)), which runs after the libmicrohttpd shared object has run its own initialiser (and therefore after MHD_init() has installed the stock panic handler), so the tripwire wins. On toolchains without constructor support, call mhd_panic_tripwire_install() as the first statement of main() instead.

WHAT IS INTERCEPTED

  1. MHD_PANIC() - via the public MHD_set_panic_func() hook. The reference to MHD_set_panic_func() is weak, so this header can also be included by the unit tests that compile a couple of library objects directly and do not link libmicrohttpd at all; there the hook is simply skipped.
  2. abort() - via a SIGABRT handler. This matters because in this code base the overwhelming majority of "fatal invariant reached from network input" sites are mhd_assert(), which goes to assert(3)/abort(3) and never passes through mhd_panic(). Set MHD_TEST_PANIC_TRIPWIRE_ABRT=0 to intercept MHD_PANIC() only.

WHY THE HANDLER MUST NOT RETURN, AND WHY IT DOES NOT longjmp()

MHD_PANIC(msg) expands to mhd_panic (...); BUILTIN_NOT_REACHED;, i.e. __builtin_unreachable(). A panic handler that returns therefore drops the caller straight into undefined behaviour - the compiler has already been told that the following code is dead and may have deleted it. So the handler has to terminate the process.

longjmp() back into main() is not an option either: MHD_PANIC() is reached from library code that may be running on a daemon worker thread or on a thread-per-connection thread, and a longjmp() across threads is undefined. Even on the right thread it would leave the daemon's mutexes locked and its connection lists half-updated, so any "recording" done afterwards would run in a process whose state is already corrupt. We therefore report and terminate rather than recover.

TERMINATION STATUS

The default is _exit(99). The automake parallel test harness classifies exit status 99 as a hard ERROR rather than an ordinary FAIL, which is exactly the "this is not a normal test failure, a fatal invariant was reached" signal wanted here. Note that this also applies to tests listed in XFAIL_TESTS - a test that is expected to trip an assertion (such as src/microhttpd/test_known_bugs.c) must therefore NOT include this header.

ENVIRONMENT VARIABLES

MHD_TEST_PANIC_TRIPWIRE

  • unset, or any unrecognised value: enabled, terminate with _exit(99).
  • 0 / off / no / disable: completely disabled; the stock libmicrohttpd behaviour (mhd_panic_std(), plain abort()) is left in place. Use this when a debugger or an external tool wants the original behaviour.
  • abort: print the marker and the stack trace, then re-raise SIGABRT with the default disposition so that a core dump is produced.
  • exit:N (0 <= N <= 255): print the marker and the stack trace, then terminate with status N instead of 99.

MHD_TEST_PANIC_TRIPWIRE_ABRT

  • 0 / off / no / disable: do not install the SIGABRT handler; only MHD_PANIC() is intercepted. Failing mhd_assert()s then abort as usual.
  • unset or anything else: the SIGABRT handler is installed.

OUTPUT FORMAT

A single greppable marker line is written to stderr, followed (where available) by a stack trace and a final status line:

MHD-PANIC-TRIPWIRE: kind=MHD_PANIC file=connection.c line=1234 \ reason=Data offset exceeds limit. MHD-PANIC-TRIPWIRE-FRAME: ... MHD-PANIC-TRIPWIRE: terminating with exit status 99

Grep for MHD_PANIC_TRIPWIRE_MARKER ("MHD-PANIC-TRIPWIRE") to classify a failure. Everything is emitted with write(2) and without allocating, so the same code path is usable from the SIGABRT handler.

Definition in file mhd_panic_tripwire.h.

Macro Definition Documentation

◆ MHD_PANIC_TRIPWIRE_EXIT_STATUS

#define MHD_PANIC_TRIPWIRE_EXIT_STATUS   99

The exit status used by default. The automake test harness reports this as a hard ERROR instead of a FAIL.

Definition at line 138 of file mhd_panic_tripwire.h.

◆ MHD_PANIC_TRIPWIRE_MARKER

#define MHD_PANIC_TRIPWIRE_MARKER   "MHD-PANIC-TRIPWIRE"

The distinctive, greppable marker that prefixes every line printed by the tripwire.

Definition at line 132 of file mhd_panic_tripwire.h.

Referenced by mhd_panic_tripwire_backtrace_(), and mhd_panic_tripwire_report_().

◆ MHD_PANIC_TRIPWIRE_NORETURN_

#define MHD_PANIC_TRIPWIRE_NORETURN_

Definition at line 184 of file mhd_panic_tripwire.h.

◆ MHD_PANIC_TRIPWIRE_UNUSED_

#define MHD_PANIC_TRIPWIRE_UNUSED_

Definition at line 183 of file mhd_panic_tripwire.h.

◆ MHD_PANIC_TRIPWIRE_WRS_

#define MHD_PANIC_TRIPWIRE_WRS_ ( str)
Value:
mhd_panic_tripwire_wr_ (str, sizeof(str) - 1)
static void mhd_panic_tripwire_wr_(const char *buf, size_t len)

Write a string literal to stderr.

Parameters
stra string literal

Definition at line 212 of file mhd_panic_tripwire.h.

Referenced by mhd_panic_tripwire_backtrace_(), mhd_panic_tripwire_report_(), and mhd_panic_tripwire_wr_clean_().

Function Documentation

◆ mhd_panic_tripwire_abrt_cb_()

static void mhd_panic_tripwire_abrt_cb_ ( int sig)
static

The SIGABRT handler installed by the tripwire. Catches a failing mhd_assert() (which goes through assert(3) and abort(3)) and any other abort(), for example from the C library's own consistency checks.

Parameters
sigthe signal number, always SIGABRT

Definition at line 397 of file mhd_panic_tripwire.h.

References mhd_panic_tripwire_report_(), and NULL.

Referenced by mhd_panic_tripwire_install().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mhd_panic_tripwire_backtrace_()

static void mhd_panic_tripwire_backtrace_ ( void )
static

Print the stack trace of the calling thread, if the platform provides one. Silently does nothing otherwise.

Definition at line 301 of file mhd_panic_tripwire.h.

References MHD_PANIC_TRIPWIRE_MARKER, mhd_panic_tripwire_wr_num_(), and MHD_PANIC_TRIPWIRE_WRS_.

Referenced by mhd_panic_tripwire_report_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mhd_panic_tripwire_install()

static MHD_PANIC_TRIPWIRE_UNUSED_ void mhd_panic_tripwire_install ( void )
static

Install the panic tripwire. Called automatically from a constructor on GCC-compatible toolchains; call it explicitly as the first statement of main() on any other toolchain. Idempotent.

Definition at line 432 of file mhd_panic_tripwire.h.

References mhd_panic_tripwire_abrt_cb_(), mhd_panic_tripwire_is_off_(), mhd_panic_tripwire_panic_cb_(), mhd_panic_tripwire_reraise_, mhd_panic_tripwire_status_, MHD_set_panic_func(), and NULL.

Here is the call graph for this function:

◆ mhd_panic_tripwire_is_off_()

static int mhd_panic_tripwire_is_off_ ( const char * val)
static

Test whether an environment variable value means "off".

Parameters
valthe value to test, may be NULL
Returns
non-zero if val explicitly disables a feature

Definition at line 415 of file mhd_panic_tripwire.h.

References NULL.

Referenced by mhd_panic_tripwire_install().

Here is the caller graph for this function:

◆ mhd_panic_tripwire_panic_cb_()

static void mhd_panic_tripwire_panic_cb_ ( void * cls,
const char * file,
unsigned int line,
const char * reason )
static

The MHD_PanicCallback installed by the tripwire.

Parameters
clsunused
filethe name of the file with the problem
linethe line number with the problem
reasonthe error message with details

Definition at line 376 of file mhd_panic_tripwire.h.

References mhd_panic_tripwire_report_().

Referenced by mhd_panic_tripwire_install().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mhd_panic_tripwire_report_()

static MHD_PANIC_TRIPWIRE_NORETURN_ void mhd_panic_tripwire_report_ ( const char * kind,
const char * file,
unsigned int line,
const char * reason )
static

Print the marker line and the stack trace, then terminate the process. Never returns; see the file comment for why recovering is not an option.

Parameters
kinda short token naming what was intercepted
filethe source file of the failure, may be NULL
linethe source line of the failure
reasonthe human-readable reason, may be NULL

Definition at line 329 of file mhd_panic_tripwire.h.

References mhd_panic_tripwire_backtrace_(), mhd_panic_tripwire_busy_, MHD_PANIC_TRIPWIRE_MARKER, mhd_panic_tripwire_reraise_, mhd_panic_tripwire_status_, mhd_panic_tripwire_wr_clean_(), mhd_panic_tripwire_wr_num_(), MHD_PANIC_TRIPWIRE_WRS_, and NULL.

Referenced by mhd_panic_tripwire_abrt_cb_(), and mhd_panic_tripwire_panic_cb_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mhd_panic_tripwire_wr_()

static void mhd_panic_tripwire_wr_ ( const char * buf,
size_t len )
static

Write a buffer to stderr. Async-signal-safe: no locking, no allocation, no stdio.

Parameters
bufthe bytes to write
lenthe number of bytes to write

Definition at line 224 of file mhd_panic_tripwire.h.

Referenced by mhd_panic_tripwire_wr_clean_(), and mhd_panic_tripwire_wr_num_().

Here is the caller graph for this function:

◆ mhd_panic_tripwire_wr_clean_()

static void mhd_panic_tripwire_wr_clean_ ( const char * str)
static

Write a NUL-terminated string to stderr, replacing every control character by a space so that the marker always stays on a single line.

Parameters
strthe string to write, may be NULL

Definition at line 250 of file mhd_panic_tripwire.h.

References mhd_panic_tripwire_wr_(), MHD_PANIC_TRIPWIRE_WRS_, and NULL.

Referenced by mhd_panic_tripwire_report_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mhd_panic_tripwire_wr_num_()

static void mhd_panic_tripwire_wr_num_ ( unsigned long val)
static

Write an unsigned number in decimal to stderr.

Parameters
valthe value to print

Definition at line 282 of file mhd_panic_tripwire.h.

References mhd_panic_tripwire_wr_().

Referenced by mhd_panic_tripwire_backtrace_(), and mhd_panic_tripwire_report_().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ mhd_panic_tripwire_busy_

volatile sig_atomic_t mhd_panic_tripwire_busy_ = 0
static

Set once a report is in progress, so that a second thread tripping at the same time does not interleave its output with the first one.

Definition at line 204 of file mhd_panic_tripwire.h.

Referenced by mhd_panic_tripwire_report_().

◆ mhd_panic_tripwire_reraise_

volatile sig_atomic_t mhd_panic_tripwire_reraise_ = 0
static

Selected action, resolved once by the installer so that the handlers themselves never have to call getenv(). 0: terminate with mhd_panic_tripwire_status_; 1: re-raise SIGABRT.

Definition at line 192 of file mhd_panic_tripwire.h.

Referenced by mhd_panic_tripwire_install(), and mhd_panic_tripwire_report_().

◆ mhd_panic_tripwire_status_

volatile sig_atomic_t mhd_panic_tripwire_status_
static
Initial value:

The exit status to use when mhd_panic_tripwire_reraise_ is zero.

Definition at line 197 of file mhd_panic_tripwire.h.

Referenced by mhd_panic_tripwire_install(), and mhd_panic_tripwire_report_().