forked from lei.chen/myxce
fix(apiserver): don't free client config C-strings that the C library retains (bug-1501)
tryConnect built the xce_client_config_t with C.CString(host/from/key), then a defer freed all three. But xcecli_ctx_init stores the config POINTER (ctx->conf = config), not a copy — client_failover.c dereferences config->masters/from/key for the client's ENTIRE lifetime (e.g. xce_failover_next_target strncpy's a master host on every write-fail/redirect). Freeing the strings left config->masters[0] dangling → ASAN heap-use-after-free (strncpy in the background C thread) after a few submits. Fix: keep the three C strings for the process lifetime (tryConnect runs once at startup, gCtx is a process-global); free them only on the init-failure path. Verified: 6 consecutive job submits, apiserver stays up, 0 ASAN errors (previously crashed after ~2-3 submits). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5b47ee603f
commit
ff2d784ca9
2 changed files with 33 additions and 5 deletions
|
|
@ -14738,5 +14738,23 @@
|
|||
"related_bugs": [],
|
||||
"occurrences": 3,
|
||||
"last_seen": "2026-08-04"
|
||||
},
|
||||
{
|
||||
"id": "bug-1501",
|
||||
"timestamp": "2026-08-04",
|
||||
"error_message": "apiserver ASAN heap-use-after-free: strncpy (\u540e\u53f0 C \u7ebf\u7a0b, xce_failover_next_target) \u8bfb\u53d6\u4e3b\u7ebf\u7a0b\u5df2\u91ca\u653e\u7684 host \u5b57\u7b26\u4e32 (READ of size 3), \u63d0\u4ea4\u591a\u4e2a\u4f5c\u4e1a\u540e\u95f4\u6b47\u5d29\u6e83",
|
||||
"file": "src/frontend/apiserver/backend/clib.go (tryConnect)",
|
||||
"root_cause": "tryConnect \u7528 defer C.free() \u91ca\u653e\u4e86 cAddr/cFrom/cKey, \u4f46 xcecli_ctx_init \u53ea\u5b58 config \u6307\u9488 (ctx->conf=config), client_failover.c \u7ec8\u8eab\u5f15\u7528 config->masters/from/key. \u4e4b\u540e\u4efb\u610f\u5199\u5931\u8d25/\u91cd\u5b9a\u5411\u65f6 xce_failover_next_target strncpy \u5df2\u91ca\u653e\u7684 master host \u2192 use-after-free",
|
||||
"fix": "FIXED: \u521d\u59cb\u5316\u6210\u529f\u540e\u4e0d\u518d\u91ca\u653e\u8fd9\u4e09\u4e2a C \u5b57\u7b26\u4e32 (\u8fdb\u7a0b\u751f\u547d\u5468\u671f, tryConnect \u4e00\u6b21\u6027\u8c03\u7528, gCtx \u662f\u8fdb\u7a0b\u5168\u5c40); \u4ec5\u521d\u59cb\u5316\u5931\u8d25\u65f6\u91ca\u653e\u9632\u6cc4\u6f0f. \u9a8c\u8bc1: \u8fde\u7eed 6 \u4e2a\u4f5c\u4e1a\u63d0\u4ea4, apiserver \u5b58\u6d3b, 0 ASAN \u9519\u8bef",
|
||||
"tags": [
|
||||
"apiserver",
|
||||
"use-after-free",
|
||||
"cgo",
|
||||
"failover",
|
||||
"fixed"
|
||||
],
|
||||
"related_bugs": [],
|
||||
"occurrences": 1,
|
||||
"last_seen": "2026-08-04"
|
||||
}
|
||||
]
|
||||
|
|
@ -80,11 +80,6 @@ func tryConnect(host string, port int, from, key string) error {
|
|||
cAddr := C.CString(host)
|
||||
cFrom := C.CString(from)
|
||||
cKey := C.CString(key)
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(cAddr))
|
||||
C.free(unsafe.Pointer(cFrom))
|
||||
C.free(unsafe.Pointer(cKey))
|
||||
}()
|
||||
|
||||
var cConfig C.xce_client_config_t
|
||||
cConfig.nmasters = 1
|
||||
|
|
@ -95,8 +90,23 @@ func tryConnect(host string, port int, from, key string) error {
|
|||
|
||||
rc := C.xce_backend_init(&cConfig, &gCtx)
|
||||
if rc < 0 {
|
||||
// Init failed — the client did not retain the config, so the strings
|
||||
// are safe to free here (avoids a leak on the error path).
|
||||
C.free(unsafe.Pointer(cAddr))
|
||||
C.free(unsafe.Pointer(cFrom))
|
||||
C.free(unsafe.Pointer(cKey))
|
||||
return fmt.Errorf("xce_backend_init failed on %s:%d: %d", host, port, int(rc))
|
||||
}
|
||||
|
||||
// bug-1501: xcecli_ctx_init stores the config POINTER (ctx->conf = config),
|
||||
// not a copy — client_failover.c dereferences config->masters/from/key for
|
||||
// the client's ENTIRE lifetime (e.g. xce_failover_next_target strncpy's a
|
||||
// master host on every write-fail/redirect). The three C strings above must
|
||||
// therefore outlive this call: freeing them here (as the old defer did)
|
||||
// left config->masters[0] dangling, and a later failover read the freed
|
||||
// host → ASAN heap-use-after-free (strncpy, background C thread). They are
|
||||
// deliberately leaked for the process lifetime — tryConnect runs once at
|
||||
// startup and gCtx is a process-global.
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue