Repository navigation
internal/godebug: let interp fold settings so unused FIPS self-tests are removed - #5883
davecheney wants to merge 1 commit into
Conversation
…are removed Fixes tinygo-org#5835. TinyGo does not read GODEBUG at startup. A setting only changes when the program calls os.Setenv("GODEBUG", ...). But the upstream internal/godebug keeps its cache in sync.Map and sync.Once, and both use defer. The interp pass cannot run functions with defer frames on setjmp targets, so godebug.Value is left for run time. Because of this, crypto/internal/fips140.Enabled is not known at compile time, and every CAST self-test closure (SHA-3, AES, GCM, SHA-2, HMAC, DRBG) stays in the binary when net/http is imported. A small TinyGo version of the package gives the same values without that machinery, so interp folds Enabled to false and the closures are removed. On waveshare-rp2350-pizero with the reproducer from tinygo-org#5835, flash use goes from 129440 to 75584 bytes (-scheduler=cores -opt=s) and from 122496 to 69072 bytes (-scheduler=tasks -opt=s). Reviewer notes: The bisect #pattern suffix is ignored, IncNonDefault does nothing, and an unknown setting name does not panic. TinyGo did not support metrics or bisect before this change either. The exported API is the same in Go 1.25, 1.26 and 1.27. archive/zip, net/url, mime/multipart and crypto/ecdsa tests pass on darwin/arm64. Deferred: the runtime Setenv hooks call update("GODEBUG", value) where upstream passes the default GODEBUG string as the first argument. The interp limits with defer frames and llvm.memset are a separate and larger change.
|
/cc @jakebailey I know this runs contrary to your "make more of the stdlib pass" work, but the savings by being able to statically eliminate all the crypto code kept alive by the FIPS-140 yak shave are worth the pain I think /cc @deadprogram as the keeper of interp. |
Yes, I do quite literally have a change I haven't sent yet which makes this work as it's required to test things... I think I want to look to see if something like this PR is possible after my change. I could just send it now and we can cross compare |
|
#5885 is the code I had; maybe for baremetal we can just say that one can't set anything at runtime and do this? |
Yeah, that’s an idea, for baremetal make godebug read only, I’ll play with it this evening to see if i can do this with an overlay |
|
Without tinygo-org#5883 the size of any program using a godebug setting changes with the Go version, so these savings could not be tracked. With it, math/rand drops from about 15.3K/10.3K to 3857/1727 code/rodata bytes on cortex-m-qemu at opt=z, and the size is the same on Go 1.25 to 1.27.
Fixes #5835.
TinyGo does not read GODEBUG at startup. A setting only changes when the program calls os.Setenv("GODEBUG", ...). But the upstream internal/godebug keeps its cache in sync.Map and sync.Once, and both use defer. The interp pass cannot run functions with defer frames, which the compiler emits on all targets that support recover(), so godebug.Value is left for run time. Because of this, crypto/internal/fips140.Enabled is not known at compile time, and every CAST self-test closure (SHA-3, AES, GCM, SHA-2, HMAC, DRBG) stays in the binary when net/http is imported. A small TinyGo version of the package gives the same values without that machinery, so interp folds Enabled to false and the closures are removed.
Results
Target waveshare-rp2350-pizero with the reproducer from #5835. Both builds use the same source tree, with and without this change. "fips140 symbols" counts every symbol in the ELF whose name contains
fips140.All crypto/internal/fips140 symbols are removed in every configuration. This includes the CAST self-test closures and the code that only they use:
fips140.CAST,sha3.keccakF1600Generic(10734 bytes at -opt=s), and theinit#1$1closures inaes,aes/gcm,drbg,hmac,sha256,sha3andsha512. It also includes the related functions, strings, and type data infips140,subtle,checkandfips140deps/byteorder.Reviewer notes
The bisect #pattern suffix is ignored, IncNonDefault does nothing, and an unknown setting name does not panic. TinyGo did not support metrics or bisect before this change either. The exported API is the same in Go 1.25, 1.26 and 1.27. archive/zip, net/url, mime/multipart and crypto/ecdsa tests pass on darwin/arm64.