#ifndef _WIN32 #define _DEFAULT_SOURCE // MAP_ANONYMOUS under -std=c11 #endif #include "test_util.h" #include #include #include #include #ifdef _WIN32 #include #else #include #include #include #endif // ---- Crash reporting ------------------------------------------------------------------------ static char s_context[200] = "startup"; static const uint8_t *s_crash_bytes; static const int *s_crash_len; void tu_set_context(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(s_context, sizeof s_context, fmt, ap); va_end(ap); } const char *tu_context(void) { return s_context; } void tu_set_crash_input(const uint8_t *bytes, const int *len) { s_crash_bytes = bytes; s_crash_len = len; } static void report_crash(const char *what) { fprintf(stderr, "CRASH (%s) during: %s\n", what, s_context); if (s_crash_bytes && s_crash_len) { fprintf(stderr, "input (%d bytes): ", *s_crash_len); for (int i = 0; i < *s_crash_len; i++) { fprintf(stderr, "%02x", s_crash_bytes[i]); } fputc('\n', stderr); } fflush(stderr); } #ifdef _WIN32 static LONG WINAPI on_crash(EXCEPTION_POINTERS *info) { char what[40]; snprintf(what, sizeof what, "exception 0x%08lx", (unsigned long)info->ExceptionRecord->ExceptionCode); report_crash(what); return EXCEPTION_EXECUTE_HANDLER; // terminate, with the exception code as the exit status } void tu_install_crash_handler(void) { SetUnhandledExceptionFilter(on_crash); } #else static void on_crash(int sig) { char what[40]; snprintf(what, sizeof what, "signal %d", sig); report_crash(what); // not async-signal-safe, but the process is ending anyway _exit(2); } void tu_install_crash_handler(void) { signal(SIGSEGV, on_crash); signal(SIGBUS, on_crash); signal(SIGILL, on_crash); // -fsanitize-undefined-trap-on-error traps with an illegal instruction } #endif // ---- Guard page ----------------------------------------------------------------------------- static uint8_t *s_guard_end; // first byte of the no-access page static size_t s_page_size; void tu_guard_init(void) { #ifdef _WIN32 SYSTEM_INFO si; GetSystemInfo(&si); s_page_size = si.dwPageSize; uint8_t *base = VirtualAlloc(NULL, 2 * s_page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); DWORD old; if (!base || !VirtualProtect(base + s_page_size, s_page_size, PAGE_NOACCESS, &old)) { fprintf(stderr, "guard page setup failed\n"); exit(2); } #else s_page_size = (size_t)sysconf(_SC_PAGESIZE); uint8_t *base = mmap(NULL, 2 * s_page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (base == MAP_FAILED || mprotect(base + s_page_size, s_page_size, PROT_NONE) != 0) { fprintf(stderr, "guard page setup failed\n"); exit(2); } #endif s_guard_end = base + s_page_size; } uint8_t *tu_guard_buf(int len) { if (len < 0 || (size_t)len > s_page_size) { fprintf(stderr, "tu_guard_buf(%d) exceeds one page\n", len); exit(2); } return s_guard_end - len; } const uint8_t *tu_guarded(const uint8_t *frame, int len) { uint8_t *dst = tu_guard_buf(len); if (len > 0) { memcpy(dst, frame, (size_t)len); } return dst; } // ---- pcap ----------------------------------------------------------------------------------- static uint32_t rd_le32(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); } static uint32_t rd_be32(const uint8_t *p) { return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3]; } int tu_pcap_foreach(const char *path, tu_frame_fn fn, void *ctx) { FILE *f = fopen(path, "rb"); if (!f) { return -1; } fseek(f, 0, SEEK_END); const long size = ftell(f); fseek(f, 0, SEEK_SET); if (size < 24) { fclose(f); return size == 0 ? 0 : -1; // the recordings include empty files } uint8_t *d = malloc((size_t)size); if (!d || fread(d, 1, (size_t)size, f) != (size_t)size) { fclose(f); free(d); return -1; } fclose(f); const uint32_t magic = rd_le32(d); const bool swapped = magic == 0xD4C3B2A1u || magic == 0x4D3CB2A1u; if (!swapped && magic != 0xA1B2C3D4u && magic != 0xA1B23C4Du) { free(d); return -1; } uint32_t (*u32)(const uint8_t *) = swapped ? rd_be32 : rd_le32; const uint32_t linktype = u32(d + 20); if (linktype != 127 && linktype != 105) { free(d); return -1; } long off = 24; int index = 0; while (off + 16 <= size) { const uint32_t incl = u32(d + off + 8); if (incl > (uint32_t)(size - off - 16)) { break; // last record cut off } const uint8_t *pkt = d + off + 16; int len = (int)incl; off += 16 + (long)incl; if (linktype == 127) { const int rt_len = len >= 4 ? (pkt[2] | (pkt[3] << 8)) : len + 1; // always little-endian if (rt_len > len) { index++; continue; } pkt += rt_len; len -= rt_len; } fn(pkt, len, index++, ctx); } free(d); return index; } static FILE *s_pcap_out; static void put_le32(uint8_t *p, uint32_t v) { p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); } bool tu_pcap_open(const char *path) { // Global header: magic, version 2.4, zone 0, sigfigs 0, snaplen 65535, linktype 105. static const uint8_t hdr[24] = { 0xD4, 0xC3, 0xB2, 0xA1, 0x02, 0x00, 0x04, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x00, 0x00, 105, 0, 0, 0, }; s_pcap_out = fopen(path, "wb"); return s_pcap_out && fwrite(hdr, 1, sizeof hdr, s_pcap_out) == sizeof hdr; } void tu_pcap_add(const uint8_t *frame, int len) { static uint32_t seq; if (!s_pcap_out || len <= 0) { return; } uint8_t rec[16]; put_le32(rec + 0, seq++); // timestamp seconds: just the frame's sequence number put_le32(rec + 4, 0); put_le32(rec + 8, (uint32_t)len); put_le32(rec + 12, (uint32_t)len); fwrite(rec, 1, sizeof rec, s_pcap_out); fwrite(frame, 1, (size_t)len, s_pcap_out); } void tu_pcap_close(void) { if (s_pcap_out) { fclose(s_pcap_out); s_pcap_out = NULL; } }