v1.77.0: nan() and parsing "nan" with strtod or scanf crash with a fatal stack overflow on linux

Summary

In v1.77.0, Xnanf calls itself on linux/386, amd64, arm, arm64, loong64, ppc64le, riscv64 and s390x. A program that does any of the following dies with fatal error: stack overflow, which recover() cannot catch:

  • calls nan(), nanf() or nanl();
  • parses "nan" with strtod(), strtof(), strtold() or atof();
  • parses "nan" with a floating point conversion of the scanf family, e.g. sscanf("nan", "%f", &f).

Parsing "inf" works. darwin, freebsd, netbsd, openbsd, illumos and windows are not affected. v1.76.0 is fine.

Reproducer

package main

import (
	"fmt"

	"modernc.org/libc"
)

func main() {
	tls := libc.NewTLS()
	defer tls.Close()

	s, err := libc.CString("nan")
	if err != nil {
		panic(err)
	}

	fmt.Println(libc.Xstrtod(tls, s, 0))
}

Expected

With v1.76.0:

NaN

Actual

With v1.77.0 on linux/amd64, Go 1.27.0:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow
...
modernc.org/libc.Xnanf(...)
        modernc.org/libc@v1.77.0/ccgo_linux_amd64.go:109130
...
modernc.org/libc.X__floatscan(...)
        modernc.org/libc@v1.77.0/ccgo_linux_amd64.go:25868

Cause

v1.77.0 is the first libc generated by ccgo v4.36.0, and no C source changed. That ccgo links musl's __builtin_nanf("") to musl's own nanf, see ccgo#67. modernc.org/libexpat's tests crash this way since its go.mod moved to v1.77.0.

Workaround

Use v1.76.0. Its API is the same. v1.77.0 only adds e99ffe10 (abort() dies by SIGABRT again, #53) and dependency updates.

Fix

The next release, regenerated with the fixed ccgo, retracts v1.77.0. The new TestNaN calls nan, nanf and nanl, and parses "nan" and "inf" variants with strtod, strtof, strtold and sscanf.