Commits on Source 2

  • cznic's avatar
    modernc.org/libc: reject a Go string in VaList instead of copying its header · 6596c7da
    cznic authored
    VaList takes anything outside int/uint/int32/.../float64/uintptr through a
    default case that memcpy's reflect.TypeOf(v).Size() bytes of the value. For a
    Go string that copies the 16 byte {ptr, len} header into the va_list. C reads
    the first word as a char* and, Go strings not being NUL terminated, vsnprintf
    runs past the end of the string, appending whatever sits next to it on the Go
    heap. The header is also wider than the 8 bytes a caller sizes a va_list slot
    at, so the write itself overruns the allocation.
    
    That is how modernc.org/quickjs leaked Go runtime internals into a
    script-visible Error.message, see quickjs#14.
    C has no string type, so ccgo never emits one here; every occurrence is hand
    written and always a bug. Panic instead of corrupting silently, in both the
    ccgo/v3 (etc.go) and ccgo/v4 (rtl.go) copies. The default case stays as it is,
    that is what carries a C struct or array passed by value.
    
    Callers must pass CString(s). Note that a modernc.org/quickjs older than
    v0.22.0 trips this panic on any host function error.
    
    Co-Authored-By: default avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    6596c7da
  • cznic's avatar
    libc_musl: keep ___lock state in the C lock word so it cannot outlive it · 04bd0c66
    cznic authored
    852ba3a8 moved musl lock state out of the C lock word *p and into a
    process-global map keyed on the word's address. musl's freeaddrinfo
    deliberately frees the buffer holding its lock word while still holding
    that lock and never unlocks:
    
    	LOCK(b->lock);
    	if (!(b->ref -= cnt)) free(b);
    	else UNLOCK(b->lock);
    
    That is correct in C, because the word is inside the block and dies with
    it. With the state in a global map the entry outlived the free, still
    locked, so when the allocator handed the same address to a later
    getaddrinfo the next freeaddrinfo blocked forever in ___lock at zero CPU,
    with no error. Deterministic on linux/386 and linux/arm, where the
    allocator recycles the address; latent on linux/amd64, which escapes on
    allocator behaviour rather than by design. Reported as libtcl9.0's 386 and
    arm builders no longer completing test runs.
    
    Put all lock state back in *p, as musl does: 0 free, 1 held, 2 held with a
    waiter possibly parked, driven by a_cas/swap. Freeing the memory then
    discards the state exactly as C expects, and a recycled calloc'd block
    starts out unlocked. Blocking uses a parking lot keyed by address in place
    of musl's futex: lockWait re-checks *p under lockParkMu and ___unlock
    stores to *p before lockWake takes that mutex, so a release landing before
    the waiter parks is seen as a value change rather than lost, which is what
    made #51 reachable. Parking lot entries exist only while a goroutine is
    really parked, so an abandoned lock leaves nothing behind and the hazard is
    gone generically, not just for freeaddrinfo. The global mutex is off the
    fast path, and an unbalanced ___unlock is now inert instead of a nil
    dereference.
    
    Add TestMuslLockLifetime: a recycled lock word, parking lot reclamation
    over the uncontended, contended and abandoned paths, an unbalanced unlock,
    and a bounded getaddrinfo/freeaddrinfo loop. TestIssue51 keeps its lock
    word in a package-level Go var; the race detector ignores atomics outside
    the Go arena and data segments, so a word in libc-allocated memory would
    carry no happens-before edge to the Go state a critical section guards.
    The real static musl lock words are package-level [1]int32, so this also
    matches them.
    
    Verified on linux/386 and linux/arm, where the reproducer completes and the
    unfixed ___lock hangs on the second iteration: go test -race, TestLibc
    (473 files, 0 build and 0 exec failures), build_all_targets across all
    targets and tags, and modernc.org/libtcl9.0 both for httpProxy.test and for
    its full suite (170 test files, 68391 assertions, 0 failed on each).
    
    Retract v1.74.2 and v1.74.3, which carry the defect.
    
    Co-Authored-By: default avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    04bd0c66
Loading
Loading