Keep teardown traces per thread and retain history across wrap
This commit is contained in:
1 parent
9a13989c7e
commit
c0365ebb38
2 files changed
+46
-14
No files matched your search
@@ -1,16 +1,35 @@
|
||||
// call_ring.c — статический кольцевой трейс вызовов (диагностика UAF в teardown)
|
||||
#include "call_ring.h"
|
||||
#include "../lib/platform_compat.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
callring_entry_t g_callring[CALLRING_SIZE];
|
||||
uint32_t g_callring_idx = 0;
|
||||
__thread callring_entry_t g_callring[CALLRING_SIZE];
|
||||
__thread uint32_t g_callring_idx = 0;
|
||||
__thread uint32_t g_callring_count = 0;
|
||||
|
||||
/* Никакого logger/malloc: путь вызывается из аварийного обработчика. */
|
||||
static void cr_write(int* fd, const char* data, size_t len) {
|
||||
while (len) {
|
||||
ssize_t n = write(*fd, data, len);
|
||||
if (n < 0 && errno == EINTR) continue;
|
||||
if (n <= 0) {
|
||||
if (*fd == 2) return;
|
||||
close(*fd); *fd = 2;
|
||||
static const char error[] = "callring: dump write failed, continuing on stderr\n";
|
||||
ssize_t ignored = write(2, error, sizeof(error) - 1); (void)ignored;
|
||||
continue;
|
||||
}
|
||||
data += n; len -= (size_t)n;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *cr_name(uint32_t code) {
|
||||
switch (code) {
|
||||
@@ -50,19 +69,22 @@ void callring_dump(void) {
|
||||
snprintf(path, sizeof(path), "/tmp/utun_callring.%d", (int)getpid());
|
||||
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
if (fd < 0) fd = 2;
|
||||
if (fd < 0) {
|
||||
fd = 2;
|
||||
static const char error[] = "callring: cannot open dump file, using stderr\n";
|
||||
ssize_t ignored = write(2, error, sizeof(error) - 1); (void)ignored;
|
||||
}
|
||||
#endif
|
||||
|
||||
int n = snprintf(buf, sizeof(buf), "callring dump idx=%u\n", g_callring_idx);
|
||||
if (n > 0) { ssize_t w = write(fd, buf, (size_t)n); (void)w; }
|
||||
if (n > 0 && (size_t)n < sizeof(buf)) cr_write(&fd, buf, (size_t)n);
|
||||
|
||||
uint32_t total = g_callring_idx < CALLRING_SIZE ? g_callring_idx : CALLRING_SIZE;
|
||||
uint32_t start = g_callring_idx >= CALLRING_SIZE ? g_callring_idx - CALLRING_SIZE : 0;
|
||||
uint32_t total = g_callring_count;
|
||||
uint32_t start = g_callring_idx - total;
|
||||
for (uint32_t k = 0; k < total; k++) {
|
||||
const callring_entry_t *e = &g_callring[(start + k) % CALLRING_SIZE];
|
||||
n = snprintf(buf, sizeof(buf), "%s a=0x%lx b=0x%lx\n", cr_name(e->code),
|
||||
(unsigned long)e->a, (unsigned long)e->b);
|
||||
if (n > 0) { ssize_t w = write(fd, buf, (size_t)n); (void)w; }
|
||||
n = snprintf(buf, sizeof(buf), "%s a=0x%" PRIxPTR " b=0x%" PRIxPTR "\n", cr_name(e->code), e->a, e->b);
|
||||
if (n > 0 && (size_t)n < sizeof(buf)) cr_write(&fd, buf, (size_t)n);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// call_ring.h — статический кольцевой трейс вызовов (диагностика UAF в teardown)
|
||||
//
|
||||
// Каждая контрольная точка пишет в общий кольцевой буфер номер (code) и два
|
||||
// Каждая контрольная точка пишет в кольцевой буфер текущего потока номер (code) и два
|
||||
// указателя-объекта (a, b). По последовательности записей + адресам из core-дампа
|
||||
// можно размотать, кто кого вызвал и кто освободил линк без обнуления обратных ссылок.
|
||||
#ifndef CALL_RING_H
|
||||
@@ -17,15 +17,25 @@ typedef struct {
|
||||
uintptr_t b; // объект 2 (close_arg/tcp_link/etcp_link)
|
||||
} callring_entry_t;
|
||||
|
||||
extern callring_entry_t g_callring[CALLRING_SIZE];
|
||||
extern uint32_t g_callring_idx;
|
||||
/* TLS отделяет одновременно работающие uasync-потоки; без аллокаций и блокировок.
|
||||
* Индекс циклический uint32_t, count насыщается на CALLRING_SIZE; переменные доступны для core dump. */
|
||||
extern __thread callring_entry_t g_callring[CALLRING_SIZE];
|
||||
extern __thread uint32_t g_callring_idx;
|
||||
extern __thread uint32_t g_callring_count;
|
||||
|
||||
/* Добавляет code/a/b без обращения по указателям a/b. Перезаписывает старейшую запись.
|
||||
* Асинхронный сигнал может прервать запись: аварийный dump остаётся best effort. */
|
||||
static inline void callring_put(uint32_t code, uintptr_t a, uintptr_t b) {
|
||||
callring_entry_t *e = &g_callring[g_callring_idx % CALLRING_SIZE];
|
||||
e->code = code; e->a = a; e->b = b;
|
||||
g_callring_idx++;
|
||||
if (g_callring_count < CALLRING_SIZE) g_callring_count++;
|
||||
}
|
||||
|
||||
/* Последние count записей текущего потока в хронологическом порядке.
|
||||
* POSIX: /tmp/utun_callring.<pid> с перезаписью, при отказе — stderr; Windows: stderr.
|
||||
* Ошибки I/O отражаются напрямую в stderr (обычный logger в crash-handler не используется).
|
||||
* Собственных аллокаций нет; snprintf не даёт общей гарантии async-signal-safety libc. */
|
||||
void callring_dump(void);
|
||||
|
||||
/* коды контрольных точек */
|
||||
@@ -46,7 +56,7 @@ enum {
|
||||
CR_CONN_FREE = 23, /* stcp_conn_free → a=c */
|
||||
CR_CCLOSE_IN = 30, /* etcp_connection_close вход → a=etcp */
|
||||
CR_CFREE_RES = 31, /* etcp_connection_free_resources → a=etcp */
|
||||
CR_NCD_DEFER = 40, /* ncd_deferred_close_conn → a=conn */
|
||||
CR_NCD_DEFER = 40, /* зарезервирован, текущий NCD не пишет эту точку */
|
||||
};
|
||||
|
||||
#endif /* CALL_RING_H */
|
||||
Reference in new issue
Block a user