Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions loader/goroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) {
}

// Find the overrides needed for the goroot.
overrides := pathsToOverride(config.GoMinorVersion, needsSyscallPackage(config.BuildTags()))
overrides := pathsToOverride(config.GoMinorVersion, needsSyscallPackage(config.BuildTags()), needsTLSStubPackage(config.GOOS(), config.BuildTags()))

// Resolve the merge links within the goroot.
merge, err := listGorootMergeLinks(goroot, tinygoroot, overrides)
Expand Down Expand Up @@ -225,14 +225,33 @@ func needsSyscallPackage(buildTags []string) bool {
return false
}

// needsTLSStubPackage returns whether the crypto/tls package should be
// overridden with the TinyGo stub version, whose handshake is a no-op. A target
// with no OS below it has neither the code size for a full TLS implementation
// nor usually a socket to speak it over.
//
// Hosted linux and macOS have both, so they use the real crypto/tls of the Go
// standard library. GOOS alone cannot decide this, because a baremetal target
// reports GOOS=linux, so the build tags decide as well.
func needsTLSStubPackage(goos string, buildTags []string) bool {
if goos != "linux" && goos != "darwin" {
return true
}
for _, tag := range buildTags {
if tag == "baremetal" || tag == "nintendoswitch" || tag == "tinygo.wasm" || tag == "wasm_unknown" {
return true
}
}
return false
}

// The boolean indicates whether to merge the subdirs. True means merge, false
// means use the TinyGo version.
func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
func pathsToOverride(goMinor int, needsSyscallPackage, needsTLSStubPackage bool) map[string]bool {
paths := map[string]bool{
"": true,
"crypto/": true,
"crypto/rand/": false,
"crypto/tls/": false,
"crypto/x509/": true,
"crypto/x509/internal/": true,
"crypto/x509/internal/macos/": false,
Expand Down Expand Up @@ -263,6 +282,12 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"unique/": false,
}

if needsTLSStubPackage {
// Without this entry crypto/tls falls under the "crypto/" merge above,
// which links in the package of the standard library.
paths["crypto/tls/"] = false
}

if goMinor >= 19 {
paths["crypto/internal/"] = true
paths["crypto/internal/boring/"] = true
Expand Down
27 changes: 27 additions & 0 deletions loader/goroot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package loader

import "testing"

func TestNeedsTLSStubPackage(t *testing.T) {
tests := []struct {
name string
goos string
buildTags []string
want bool
}{
{"hosted linux", "linux", []string{"linux", "amd64"}, false},
{"hosted darwin", "darwin", []string{"darwin", "arm64"}, false},
{"windows", "windows", []string{"windows", "amd64"}, true},
{"wasip1", "wasip1", []string{"wasip1", "tinygo.wasm"}, true},
// A baremetal target reports GOOS=linux, so the build tags have to
// keep the stub for it.
{"baremetal", "linux", []string{"linux", "arm", "baremetal"}, true},
{"nintendoswitch", "linux", []string{"linux", "nintendoswitch"}, true},
{"wasm_unknown", "linux", []string{"linux", "wasm_unknown"}, true},
}
for _, test := range tests {
if got := needsTLSStubPackage(test.goos, test.buildTags); got != test.want {
t.Errorf("%s: wanted %v, got %v", test.name, test.want, got)
}
}
}
2 changes: 1 addition & 1 deletion loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func (p *Program) getOriginalPath(path string) string {
originalPath = realgorootPath
}
maybeInTinyGoRoot := false
for prefix := range pathsToOverride(p.config.GoMinorVersion, needsSyscallPackage(p.config.BuildTags())) {
for prefix := range pathsToOverride(p.config.GoMinorVersion, needsSyscallPackage(p.config.BuildTags()), needsTLSStubPackage(p.config.GOOS(), p.config.BuildTags())) {
if runtime.GOOS == "windows" {
prefix = strings.ReplaceAll(prefix, "/", "\\")
}
Expand Down
14 changes: 14 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func TestBuild(t *testing.T) {
}
if minor >= 24 {
tests = append(tests, "typealias.go")
tests = append(tests, "weak.go")
}

if *testTarget != "" {
Expand Down Expand Up @@ -283,6 +284,19 @@ func TestTimerStopResetRace(t *testing.T) {
runTest("timer_stop_reset_race.go", optionsFromTarget("", sema), t, nil, nil)
}

// TestHostCryptoTLS checks that a hosted target gets the real crypto/tls and
// not the stub, whose handshake does nothing. Only linux and macOS do.
func TestHostCryptoTLS(t *testing.T) {
t.Parallel()

switch runtime.GOOS {
case "darwin", "linux":
default:
t.Skipf("host GOOS %s keeps the crypto/tls stub", runtime.GOOS)
}
runTest("hostcryptotls.go", optionsFromTarget("", sema), t, nil, nil)
}

func TestESP32QEMU(t *testing.T) {
t.Parallel()

Expand Down
7 changes: 7 additions & 0 deletions src/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ func registerWeakPointer(ptr unsafe.Pointer) unsafe.Pointer {
return ptr
}

//go:linkname makeStrongFromWeak weak.runtime_makeStrongFromWeak
func makeStrongFromWeak(ptr unsafe.Pointer) unsafe.Pointer {
// Weak pointers are not weak here. registerWeakPointer above returns the
// pointer that it got, so the value stays and this is the identity too.
return ptr
}

var godebugUpdate func(string, string)

//go:linkname godebug_setUpdate internal/godebug.setUpdate
Expand Down
100 changes: 100 additions & 0 deletions testdata/hostcryptotls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package main

// A real TLS handshake over an in-memory pipe. The stub crypto/tls has a
// handshake that does nothing, so it cannot pass this.

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"io"
"math/big"
"net"
"time"
)

func main() {
cert, pool := selfSigned()

// A client that trusts the certificate completes the handshake and
// exchanges data.
client, server := net.Pipe()
go serve(server, cert)
conn := tls.Client(client, &tls.Config{RootCAs: pool, ServerName: "tinygo.test"})
if err := conn.Handshake(); err != nil {
println("handshake failed:", err.Error())
return
}
if v := conn.ConnectionState().Version; v < tls.VersionTLS12 {
println("negotiated an unexpected version:", v)
return
}
if _, err := conn.Write([]byte("ping")); err != nil {
println("write failed:", err.Error())
return
}
buf := make([]byte, 4)
if _, err := io.ReadFull(conn, buf); err != nil {
println("read failed:", err.Error())
return
}
println("got:", string(buf))
conn.Close()

// A client that does not trust the certificate must refuse it.
client, server = net.Pipe()
go serve(server, cert)
conn = tls.Client(client, &tls.Config{ServerName: "tinygo.test"})
if err := conn.Handshake(); err == nil {
println("an unknown certificate was accepted")
return
}
conn.Close()
println("unknown certificate refused")
}

func serve(conn net.Conn, cert tls.Certificate) {
server := tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{cert}})
if err := server.Handshake(); err != nil {
conn.Close()
return
}
buf := make([]byte, 4)
if _, err := io.ReadFull(server, buf); err != nil {
server.Close()
return
}
server.Write([]byte("pong"))
}

func selfSigned() (tls.Certificate, *x509.CertPool) {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "tinygo.test"},
DNSNames: []string{"tinygo.test"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
panic(err)
}
leaf, err := x509.ParseCertificate(der)
if err != nil {
panic(err)
}
pool := x509.NewCertPool()
pool.AddCert(leaf)
return tls.Certificate{Certificate: [][]byte{der}, PrivateKey: key, Leaf: leaf}, pool
}
2 changes: 2 additions & 0 deletions testdata/hostcryptotls.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
got: pong
unknown certificate refused
21 changes: 21 additions & 0 deletions testdata/weak.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"runtime"
"weak"
)

type value struct {
n int
}

func main() {
v := &value{n: 42}
p := weak.Make(v)
if got := p.Value(); got == nil {
println("weak pointer lost its value")
} else {
println("weak value:", got.n)
}
runtime.KeepAlive(v)
}
1 change: 1 addition & 0 deletions testdata/weak.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
weak value: 42