mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Fix uninitialized template-arg arrays binding stack garbage into type graphs
The C/C++ LSP's explicit-template-argument call path (template_function, e.g. Using<Fmt>(v)) filled a stack array const CBMType *targs[16] without NULL-terminating it before cbm_type_substitute. With more declared type params than explicit args (bitcoin src/serialize.h: Using's Wrapper<Formatter, T&> with 2 params, 1 arg), the bounded args walk read uninitialized stack; any non-NULL garbage got bound to the type param and woven into the REGISTERED return-type graph, then dereferenced at a later call site - SIGSEGV (crash shape: string bytes misread as a CBMType*). All c_lsp targs arrays are now zero-filled, and cbm_type_substitute's args walk treats implausible values (misaligned / below the first page) as the terminator so contract violations can never leak garbage into a type graph. Reproduce-first: deterministic poison-args unit test (typerep_substitute_rejects_garbage_args_entries) + real-repo verification (bitcoin indexes clean under ASan; was a deterministic SIGSEGV).
This commit is contained in:
1 parent
9f981a1612
commit
bb7341dc07
3 files changed
+54
-5
No files matched your search
@@ -985,7 +985,10 @@ const CBMType *c_parse_type_node(CLSPContext *ctx, TSNode node) {
|
||||
}
|
||||
|
||||
// Parse template arguments
|
||||
const CBMType *targs[16];
|
||||
const CBMType *targs[16] = {
|
||||
NULL}; /* zero-fill: cbm_type_substitute requires NULL-terminated args
|
||||
(uninitialized tail bound T to stack garbage -> corrupt type graph,
|
||||
bitcoin serialize.h) */
|
||||
int targ_count = 0;
|
||||
if (!ts_node_is_null(tmpl_args)) {
|
||||
uint32_t nc = ts_node_named_child_count(tmpl_args);
|
||||
@@ -1087,7 +1090,9 @@ const CBMType *c_parse_type_node(CLSPContext *ctx, TSNode node) {
|
||||
}
|
||||
|
||||
// Parse template arguments
|
||||
const CBMType *targs[16];
|
||||
const CBMType *targs[16] = {
|
||||
NULL}; /* zero-fill: cbm_type_substitute requires NULL-terminated args (uninitialized
|
||||
tail bound T to stack garbage -> corrupt type graph, bitcoin serialize.h) */
|
||||
int targ_count = 0;
|
||||
if (!ts_node_is_null(args_node)) {
|
||||
uint32_t nc = ts_node_named_child_count(args_node);
|
||||
@@ -1585,7 +1590,10 @@ static const CBMType *c_eval_expr_type_inner(CLSPContext *ctx, TSNode node) {
|
||||
f->signature->kind == CBM_TYPE_FUNC && f->signature->data.func.return_types &&
|
||||
f->signature->data.func.return_types[0]) {
|
||||
const CBMType *base_ret = f->signature->data.func.return_types[0];
|
||||
const CBMType *targs[16];
|
||||
const CBMType *targs[16] = {
|
||||
NULL}; /* zero-fill: cbm_type_substitute requires NULL-terminated args
|
||||
(uninitialized tail bound T to stack garbage -> corrupt type graph,
|
||||
bitcoin serialize.h) */
|
||||
int targ_count = 0;
|
||||
uint32_t tnc = ts_node_named_child_count(qi_tmpl_args);
|
||||
for (uint32_t ti = 0; ti < tnc && targ_count < 15; ti++) {
|
||||
@@ -1742,7 +1750,9 @@ static const CBMType *c_eval_expr_type_inner(CLSPContext *ctx, TSNode node) {
|
||||
return f->signature;
|
||||
|
||||
// Parse explicit template arguments from <...>
|
||||
const CBMType *targs[16];
|
||||
const CBMType *targs[16] = {
|
||||
NULL}; /* zero-fill: cbm_type_substitute requires NULL-terminated args (uninitialized
|
||||
tail bound T to stack garbage -> corrupt type graph, bitcoin serialize.h) */
|
||||
int targ_count = 0;
|
||||
if (!ts_node_is_null(args_node)) {
|
||||
uint32_t nc = ts_node_named_child_count(args_node);
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* No real allocation lives in the first page; values below this are garbage
|
||||
* (e.g. small integers or truncated string bytes misread as pointers). */
|
||||
enum { TR_MIN_PLAUSIBLE_PTR = 4096 };
|
||||
|
||||
// Singleton UNKNOWN type (no allocation needed).
|
||||
static const CBMType unknown_singleton = {.kind = CBM_TYPE_UNKNOWN};
|
||||
|
||||
@@ -769,8 +773,17 @@ const CBMType *cbm_type_substitute(CBMArena *a, const CBMType *t, const char **t
|
||||
while (type_params[nparams]) {
|
||||
nparams++;
|
||||
}
|
||||
/* Contract: type_args must be NULL-terminated (it may be shorter than
|
||||
* type_params). A misaligned or null-page value can never be a real
|
||||
* CBMType* — it means a caller passed an unterminated array and the walk
|
||||
* is reading uninitialized memory (seen on bitcoin's serialize.h: an
|
||||
* explicit-template-arg call bound T to stack garbage that was woven into
|
||||
* the registered type graph and dereferenced later -> SIGSEGV). Treat such
|
||||
* values as the terminator so garbage can never enter a type graph. */
|
||||
int args_len = 0;
|
||||
while (args_len < nparams && type_args[args_len]) {
|
||||
while (args_len < nparams && type_args[args_len] &&
|
||||
((uintptr_t)type_args[args_len] & (sizeof(void *) - 1)) == 0 &&
|
||||
(uintptr_t)type_args[args_len] >= TR_MIN_PLAUSIBLE_PTR) {
|
||||
args_len++;
|
||||
}
|
||||
|
||||
|
||||
@@ -266,6 +266,31 @@ TEST(typerep_substitute_short_args_no_oob_issue427) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A caller that forgets to NULL-terminate type_args (a stack array) makes the
|
||||
* bounded walk read uninitialized memory — and a garbage "pointer" within the
|
||||
* param count would be BOUND to a type param and woven into the resulting type
|
||||
* graph (bitcoin serialize.h: Using<Fmt>(v) with explicit args bound T to stack
|
||||
* garbage; the corrupt graph was dereferenced later -> SIGSEGV). Implausible
|
||||
* values (misaligned / null-page) must act as the terminator instead. */
|
||||
TEST(typerep_substitute_rejects_garbage_args_entries) {
|
||||
CBMArena a;
|
||||
cbm_arena_init(&a);
|
||||
const CBMType *t =
|
||||
cbm_type_reference(&a, cbm_type_type_param(&a, "T")); /* T& as in Wrapper<F, T&> */
|
||||
const char *params[] = {"F", "T", NULL};
|
||||
const CBMType *args[2];
|
||||
args[0] = cbm_type_named(&a, "proj.Fmt"); /* explicit arg for F */
|
||||
args[1] = (const CBMType *)0x37; /* simulated uninitialized stack garbage */
|
||||
const CBMType *sub = cbm_type_substitute(&a, t, params, args);
|
||||
ASSERT_NOT_NULL(sub);
|
||||
ASSERT_EQ(sub->kind, CBM_TYPE_REFERENCE);
|
||||
/* T has no real binding: it must be preserved, never the garbage value. */
|
||||
ASSERT_TRUE(sub->data.reference.elem != (const CBMType *)0x37);
|
||||
ASSERT_EQ(sub->data.reference.elem->kind, CBM_TYPE_TYPE_PARAM);
|
||||
cbm_arena_destroy(&a);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ── Suite ─────────────────────────────────────────────────────── */
|
||||
|
||||
SUITE(type_rep) {
|
||||
@@ -293,4 +318,5 @@ SUITE(type_rep) {
|
||||
/* SUBSTITUTION */
|
||||
RUN_TEST(typerep_substitute_unbound_param_preserved);
|
||||
RUN_TEST(typerep_substitute_short_args_no_oob_issue427);
|
||||
RUN_TEST(typerep_substitute_rejects_garbage_args_entries);
|
||||
}
|
||||
Reference in new issue
Block a user