You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
V fork: -gc e generated free fns dereference reference fields with unsafe { nil } defaults — no nil guard (SIGSEGV)
Problem
Under -gc e, cgen's generated <T>_free functions call the pointee's free function through a reference field unconditionally, even when the field's default is unsafe { nil } and the pointee type contains heap-bearing fields (so a free fn is generated for it). Freeing any value whose reference field is still nil dereferences NULL.
Generated C (from the repro below):
staticvoidp5__ElementMeta_free(p5__ElementMeta*it) {
if (it->id.state!=2) { // it == NULL herebuiltin__string_free((string*)&(it->id.data));
}
}
staticvoidp5__Element_free(p5__Element*it) {
builtin__string_free(&(it->name));
p5__ElementMeta_free((it->meta)); // no nil guard; meta defaults to unsafe { nil }
}
Minimal repro (pure V, 2 files)
m.v:
modulep5pubstructElementMeta {
pubmut:
id ?string
}
pubstructElement {
pubmut:
name string
meta &ElementMeta=unsafe { nil }
}
pubstructDocument {
pubmut:
elements []Element
}
pubstructParseResult {
pubmut:
multi ?[]Document
}
pub fnparse(src string) ParseResult {
mutdocs:= []Document{}
for part in src.split('---') {
docs << Document{
elements: [Element{
name: part
}]
}
}
return ParseResult{
multi: docs
}
}
Run: v -cc cc -gc e test <dir> → SIGSEGV in Element_free (via Array_Document_free on the option-unwrap copy at test scope exit).
Boundary probes (all verified)
meta: &ElementMeta{} (allocated) instead of nil → green. So this is a nil deref, not a double-free.
ElementMeta { id int } (pointee needs no free fn) → green — no free call emitted through the pointer.
Same code, -gc boehm → green (no free insertion).
The option-field unwrap (res.multi or {}) is the trigger context (it's where a free of the unwrap copy gets inserted in module-internal v test compilation); returning ?[]Document or []Document directly from a fn does not insert the free and stays green. The defect itself is in the emitted free fn, which is wrong for ANY context that frees such a value.
Field impact (cx-private)
cx's Element carries two such fields (meta &ElementMeta = unsafe { nil }, table &TableData = unsafe { nil }) and nearly all parsed elements leave both nil. cx-private#737 (module-internal v test segfault on multi-doc parse) is this defect — previously misclassified as an option-unwrap double-free; the minimization disproved that. cx-private#749 (crash freeing a held []cx.Element under -gc e, ASAN-clean under -gc none) crashes in the same Element_free walk and plausibly shares this root cause — to be re-verified once the guard lands.
Fix direction
cgen free-fn emission: guard the call through any reference-typed field with a possible nil value — if (it->meta != 0) { ElementMeta_free(it->meta); }. (Whether refcount bookkeeping for the pointee also needs the guard should be checked in the same pass.)
CX-agnostic; candidate for the next upstream bundle (precedent: vlang#27458, vlang#27658).
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.
V fork:
-gc egenerated free fns dereference reference fields withunsafe { nil }defaults — no nil guard (SIGSEGV)Problem
Under
-gc e, cgen's generated<T>_freefunctions call the pointee's free function through a reference field unconditionally, even when the field's default isunsafe { nil }and the pointee type contains heap-bearing fields (so a free fn is generated for it). Freeing any value whose reference field is still nil dereferences NULL.Generated C (from the repro below):
Minimal repro (pure V, 2 files)
m.v:m_test.v:Run:
v -cc cc -gc e test <dir>→ SIGSEGV inElement_free(viaArray_Document_freeon the option-unwrap copy at test scope exit).Boundary probes (all verified)
meta: &ElementMeta{}(allocated) instead of nil → green. So this is a nil deref, not a double-free.ElementMeta { id int }(pointee needs no free fn) → green — no free call emitted through the pointer.-gc boehm→ green (no free insertion).res.multi or {}) is the trigger context (it's where a free of the unwrap copy gets inserted in module-internalv testcompilation); returning?[]Documentor[]Documentdirectly from a fn does not insert the free and stays green. The defect itself is in the emitted free fn, which is wrong for ANY context that frees such a value.Field impact (cx-private)
cx's
Elementcarries two such fields (meta &ElementMeta = unsafe { nil },table &TableData = unsafe { nil }) and nearly all parsed elements leave both nil. cx-private#737 (module-internalv testsegfault on multi-doc parse) is this defect — previously misclassified as an option-unwrap double-free; the minimization disproved that. cx-private#749 (crash freeing a held[]cx.Elementunder-gc e, ASAN-clean under-gc none) crashes in the sameElement_freewalk and plausibly shares this root cause — to be re-verified once the guard lands.Fix direction
cgen free-fn emission: guard the call through any reference-typed field with a possible nil value —
if (it->meta != 0) { ElementMeta_free(it->meta); }. (Whether refcount bookkeeping for the pointee also needs the guard should be checked in the same pass.)CX-agnostic; candidate for the next upstream bundle (precedent: vlang#27458, vlang#27658).
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.