From baf7e4fa559735bf2086f80a8c602350f2e7e9c8 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Mon, 2 Sep 2024 11:58:44 +0200 Subject: [PATCH 01/14] link Signed-off-by: leongross <leon.gross@9elements.com> --- src/syscall/syscall_libc.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 0ef9784283..5cacc0c8bb 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -215,7 +215,11 @@ func Truncate(path string, length int64) (err error) { func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) func Kill(pid int, sig Signal) (err error) { - return ENOSYS // TODO + result := libc_kill(int32(pid), int32(sig)) + if result < 0 { + err = getErrno() + } + return } type SysProcAttr struct{} @@ -418,3 +422,8 @@ func libc_execve(filename *byte, argv **byte, envp **byte) int // //export truncate func libc_truncate(path *byte, length int64) int32 + +// int kill(pid_t pid, int sig); +// +//export kill +func libc_kill(pid, sig int32) int32 From 329fef6b4a6394ebe7e5ec49e5cacfa15f61b006 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Tue, 10 Sep 2024 13:45:08 +0200 Subject: [PATCH 02/14] linux support for net.Dial by stubbing out runtime polling Signed-off-by: leongross <leon.gross@9elements.com> --- .gitmodules | 4 - loader/goroot.go | 6 + src/crypto/tls/common.go | 460 --------------------------------- src/crypto/tls/ticket.go | 16 -- src/crypto/tls/tls.go | 114 -------- src/net | 1 - src/runtime/netpoll.go | 45 ++++ src/runtime/netpoll_generic.go | 31 +++ src/runtime/poll.go | 15 +- src/runtime/sync.go | 8 +- src/syscall/forklock_tinygo.go | 38 +++ 11 files changed, 134 insertions(+), 604 deletions(-) delete mode 100644 src/crypto/tls/common.go delete mode 100644 src/crypto/tls/ticket.go delete mode 100644 src/crypto/tls/tls.go delete mode 160000 src/net create mode 100644 src/runtime/netpoll.go create mode 100644 src/runtime/netpoll_generic.go create mode 100644 src/syscall/forklock_tinygo.go diff --git a/.gitmodules b/.gitmodules index 91bd14a7d7..e4f627ae1c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,10 +32,6 @@ [submodule "lib/macos-minimal-sdk"] path = lib/macos-minimal-sdk url = https://github.com/aykevl/macos-minimal-sdk.git -[submodule "src/net"] - path = src/net - url = https://github.com/tinygo-org/net.git - branch = dev [submodule "lib/wasi-cli"] path = lib/wasi-cli url = https://github.com/WebAssembly/wasi-cli diff --git a/loader/goroot.go b/loader/goroot.go index 5442df9b5d..7a48ecbb1f 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -270,6 +270,12 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool { if needsSyscallPackage { paths["syscall/"] = true // include syscall/js } + + // to enable network support for linux systems, reuse the Go version of the net package + // and the according runtime functions + // if runtime.GOOS == "linux" { + // paths["runtime/netpoll/"] = true + // } return paths } diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go deleted file mode 100644 index caf0198e15..0000000000 --- a/src/crypto/tls/common.go +++ /dev/null @@ -1,460 +0,0 @@ -// TINYGO: The following is copied and modified from Go 1.19.3 official implementation. - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tls - -import ( - "context" - "crypto" - "crypto/x509" - "fmt" - "io" - "net" - "sync" - "time" -) - -// CurveID is the type of a TLS identifier for an elliptic curve. See -// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8. -// -// In TLS 1.3, this type is called NamedGroup, but at this time this library -// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7. -type CurveID uint16 - -// CipherSuiteName returns the standard name for the passed cipher suite ID -// -// Not Implemented. -func CipherSuiteName(id uint16) string { - return fmt.Sprintf("0x%04X", id) -} - -// ConnectionState records basic TLS details about the connection. -type ConnectionState struct { - // TINYGO: empty; TLS connection offloaded to device - // - // Minimum (empty) fields for fortio.org/log http logging and others - // to compile and run. - PeerCertificates []*x509.Certificate - CipherSuite uint16 -} - -// ClientAuthType declares the policy the server will follow for -// TLS Client Authentication. -type ClientAuthType int - -// ClientSessionCache is a cache of ClientSessionState objects that can be used -// by a client to resume a TLS session with a given server. ClientSessionCache -// implementations should expect to be called concurrently from different -// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not -// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which -// are supported via this interface. -type ClientSessionCache interface { - // Get searches for a ClientSessionState associated with the given key. - // On return, ok is true if one was found. - Get(sessionKey string) (session *ClientSessionState, ok bool) - - // Put adds the ClientSessionState to the cache with the given key. It might - // get called multiple times in a connection if a TLS 1.3 server provides - // more than one session ticket. If called with a nil *ClientSessionState, - // it should remove the cache entry. - Put(sessionKey string, cs *ClientSessionState) -} - -//go:generate stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go - -// SignatureScheme identifies a signature algorithm supported by TLS. See -// RFC 8446, Section 4.2.3. -type SignatureScheme uint16 - -// ClientHelloInfo contains information from a ClientHello message in order to -// guide application logic in the GetCertificate and GetConfigForClient callbacks. -type ClientHelloInfo struct { - // CipherSuites lists the CipherSuites supported by the client (e.g. - // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256). - CipherSuites []uint16 - - // ServerName indicates the name of the server requested by the client - // in order to support virtual hosting. ServerName is only set if the - // client is using SNI (see RFC 4366, Section 3.1). - ServerName string - - // SupportedCurves lists the elliptic curves supported by the client. - // SupportedCurves is set only if the Supported Elliptic Curves - // Extension is being used (see RFC 4492, Section 5.1.1). - SupportedCurves []CurveID - - // SupportedPoints lists the point formats supported by the client. - // SupportedPoints is set only if the Supported Point Formats Extension - // is being used (see RFC 4492, Section 5.1.2). - SupportedPoints []uint8 - - // SignatureSchemes lists the signature and hash schemes that the client - // is willing to verify. SignatureSchemes is set only if the Signature - // Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1). - SignatureSchemes []SignatureScheme - - // SupportedProtos lists the application protocols supported by the client. - // SupportedProtos is set only if the Application-Layer Protocol - // Negotiation Extension is being used (see RFC 7301, Section 3.1). - // - // Servers can select a protocol by setting Config.NextProtos in a - // GetConfigForClient return value. - SupportedProtos []string - - // SupportedVersions lists the TLS versions supported by the client. - // For TLS versions less than 1.3, this is extrapolated from the max - // version advertised by the client, so values other than the greatest - // might be rejected if used. - SupportedVersions []uint16 - - // Conn is the underlying net.Conn for the connection. Do not read - // from, or write to, this connection; that will cause the TLS - // connection to fail. - Conn net.Conn - - // config is embedded by the GetCertificate or GetConfigForClient caller, - // for use with SupportsCertificate. - config *Config - - // ctx is the context of the handshake that is in progress. - ctx context.Context -} - -// CertificateRequestInfo contains information from a server's -// CertificateRequest message, which is used to demand a certificate and proof -// of control from a client. -type CertificateRequestInfo struct { - // AcceptableCAs contains zero or more, DER-encoded, X.501 - // Distinguished Names. These are the names of root or intermediate CAs - // that the server wishes the returned certificate to be signed by. An - // empty slice indicates that the server has no preference. - AcceptableCAs [][]byte - - // SignatureSchemes lists the signature schemes that the server is - // willing to verify. - SignatureSchemes []SignatureScheme - - // Version is the TLS version that was negotiated for this connection. - Version uint16 - - // ctx is the context of the handshake that is in progress. - ctx context.Context -} - -// RenegotiationSupport enumerates the different levels of support for TLS -// renegotiation. TLS renegotiation is the act of performing subsequent -// handshakes on a connection after the first. This significantly complicates -// the state machine and has been the source of numerous, subtle security -// issues. Initiating a renegotiation is not supported, but support for -// accepting renegotiation requests may be enabled. -// -// Even when enabled, the server may not change its identity between handshakes -// (i.e. the leaf certificate must be the same). Additionally, concurrent -// handshake and application data flow is not permitted so renegotiation can -// only be used with protocols that synchronise with the renegotiation, such as -// HTTPS. -// -// Renegotiation is not defined in TLS 1.3. -type RenegotiationSupport int - -// A Config structure is used to configure a TLS client or server. -// After one has been passed to a TLS function it must not be -// modified. A Config may be reused; the tls package will also not -// modify it. -type Config struct { - // Rand provides the source of entropy for nonces and RSA blinding. - // If Rand is nil, TLS uses the cryptographic random reader in package - // crypto/rand. - // The Reader must be safe for use by multiple goroutines. - Rand io.Reader - - // Time returns the current time as the number of seconds since the epoch. - // If Time is nil, TLS uses time.Now. - Time func() time.Time - - // Certificates contains one or more certificate chains to present to the - // other side of the connection. The first certificate compatible with the - // peer's requirements is selected automatically. - // - // Server configurations must set one of Certificates, GetCertificate or - // GetConfigForClient. Clients doing client-authentication may set either - // Certificates or GetClientCertificate. - // - // Note: if there are multiple Certificates, and they don't have the - // optional field Leaf set, certificate selection will incur a significant - // per-handshake performance cost. - Certificates []Certificate - - // NameToCertificate maps from a certificate name to an element of - // Certificates. Note that a certificate name can be of the form - // '*.example.com' and so doesn't have to be a domain name as such. - // - // Deprecated: NameToCertificate only allows associating a single - // certificate with a given name. Leave this field nil to let the library - // select the first compatible chain from Certificates. - NameToCertificate map[string]*Certificate - - // GetCertificate returns a Certificate based on the given - // ClientHelloInfo. It will only be called if the client supplies SNI - // information or if Certificates is empty. - // - // If GetCertificate is nil or returns nil, then the certificate is - // retrieved from NameToCertificate. If NameToCertificate is nil, the - // best element of Certificates will be used. - // - // Once a Certificate is returned it should not be modified. - GetCertificate func(*ClientHelloInfo) (*Certificate, error) - - // GetClientCertificate, if not nil, is called when a server requests a - // certificate from a client. If set, the contents of Certificates will - // be ignored. - // - // If GetClientCertificate returns an error, the handshake will be - // aborted and that error will be returned. Otherwise - // GetClientCertificate must return a non-nil Certificate. If - // Certificate.Certificate is empty then no certificate will be sent to - // the server. If this is unacceptable to the server then it may abort - // the handshake. - // - // GetClientCertificate may be called multiple times for the same - // connection if renegotiation occurs or if TLS 1.3 is in use. - // - // Once a Certificate is returned it should not be modified. - GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error) - - // GetConfigForClient, if not nil, is called after a ClientHello is - // received from a client. It may return a non-nil Config in order to - // change the Config that will be used to handle this connection. If - // the returned Config is nil, the original Config will be used. The - // Config returned by this callback may not be subsequently modified. - // - // If GetConfigForClient is nil, the Config passed to Server() will be - // used for all connections. - // - // If SessionTicketKey was explicitly set on the returned Config, or if - // SetSessionTicketKeys was called on the returned Config, those keys will - // be used. Otherwise, the original Config keys will be used (and possibly - // rotated if they are automatically managed). - GetConfigForClient func(*ClientHelloInfo) (*Config, error) - - // VerifyPeerCertificate, if not nil, is called after normal - // certificate verification by either a TLS client or server. It - // receives the raw ASN.1 certificates provided by the peer and also - // any verified chains that normal processing found. If it returns a - // non-nil error, the handshake is aborted and that error results. - // - // If normal verification fails then the handshake will abort before - // considering this callback. If normal verification is disabled (on the - // client when InsecureSkipVerify is set, or on a server when ClientAuth is - // RequestClientCert or RequireAnyClientCert), then this callback will be - // considered but the verifiedChains argument will always be nil. When - // ClientAuth is NoClientCert, this callback is not called on the server. - // rawCerts may be empty on the server if ClientAuth is RequestClientCert or - // VerifyClientCertIfGiven. - // - // This callback is not invoked on resumed connections, as certificates are - // not re-verified on resumption. - // - // verifiedChains and its contents should not be modified. - VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error - - // VerifyConnection, if not nil, is called after normal certificate - // verification and after VerifyPeerCertificate by either a TLS client - // or server. If it returns a non-nil error, the handshake is aborted - // and that error results. - // - // If normal verification fails then the handshake will abort before - // considering this callback. This callback will run for all connections, - // including resumptions, regardless of InsecureSkipVerify or ClientAuth - // settings. - VerifyConnection func(ConnectionState) error - - // RootCAs defines the set of root certificate authorities - // that clients use when verifying server certificates. - // If RootCAs is nil, TLS uses the host's root CA set. - RootCAs *x509.CertPool - - // NextProtos is a list of supported application level protocols, in - // order of preference. If both peers support ALPN, the selected - // protocol will be one from this list, and the connection will fail - // if there is no mutually supported protocol. If NextProtos is empty - // or the peer doesn't support ALPN, the connection will succeed and - // ConnectionState.NegotiatedProtocol will be empty. - NextProtos []string - - // ServerName is used to verify the hostname on the returned - // certificates unless InsecureSkipVerify is given. It is also included - // in the client's handshake to support virtual hosting unless it is - // an IP address. - ServerName string - - // ClientAuth determines the server's policy for - // TLS Client Authentication. The default is NoClientCert. - ClientAuth ClientAuthType - - // ClientCAs defines the set of root certificate authorities - // that servers use if required to verify a client certificate - // by the policy in ClientAuth. - ClientCAs *x509.CertPool - - // InsecureSkipVerify controls whether a client verifies the server's - // certificate chain and host name. If InsecureSkipVerify is true, crypto/tls - // accepts any certificate presented by the server and any host name in that - // certificate. In this mode, TLS is susceptible to machine-in-the-middle - // attacks unless custom verification is used. This should be used only for - // testing or in combination with VerifyConnection or VerifyPeerCertificate. - InsecureSkipVerify bool - - // CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of - // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable. - // - // If CipherSuites is nil, a safe default list is used. The default cipher - // suites might change over time. - CipherSuites []uint16 - - // PreferServerCipherSuites is a legacy field and has no effect. - // - // It used to control whether the server would follow the client's or the - // server's preference. Servers now select the best mutually supported - // cipher suite based on logic that takes into account inferred client - // hardware, server hardware, and security. - // - // Deprecated: PreferServerCipherSuites is ignored. - PreferServerCipherSuites bool - - // SessionTicketsDisabled may be set to true to disable session ticket and - // PSK (resumption) support. Note that on clients, session ticket support is - // also disabled if ClientSessionCache is nil. - SessionTicketsDisabled bool - - // SessionTicketKey is used by TLS servers to provide session resumption. - // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled - // with random data before the first server handshake. - // - // Deprecated: if this field is left at zero, session ticket keys will be - // automatically rotated every day and dropped after seven days. For - // customizing the rotation schedule or synchronizing servers that are - // terminating connections for the same host, use SetSessionTicketKeys. - SessionTicketKey [32]byte - - // ClientSessionCache is a cache of ClientSessionState entries for TLS - // session resumption. It is only used by clients. - ClientSessionCache ClientSessionCache - - // UnwrapSession is called on the server to turn a ticket/identity - // previously produced by [WrapSession] into a usable session. - // - // UnwrapSession will usually either decrypt a session state in the ticket - // (for example with [Config.EncryptTicket]), or use the ticket as a handle - // to recover a previously stored state. It must use [ParseSessionState] to - // deserialize the session state. - // - // If UnwrapSession returns an error, the connection is terminated. If it - // returns (nil, nil), the session is ignored. crypto/tls may still choose - // not to resume the returned session. - UnwrapSession func(identity []byte, cs ConnectionState) (*SessionState, error) - - // WrapSession is called on the server to produce a session ticket/identity. - // - // WrapSession must serialize the session state with [SessionState.Bytes]. - // It may then encrypt the serialized state (for example with - // [Config.DecryptTicket]) and use it as the ticket, or store the state and - // return a handle for it. - // - // If WrapSession returns an error, the connection is terminated. - // - // Warning: the return value will be exposed on the wire and to clients in - // plaintext. The application is in charge of encrypting and authenticating - // it (and rotating keys) or returning high-entropy identifiers. Failing to - // do so correctly can compromise current, previous, and future connections - // depending on the protocol version. - WrapSession func(ConnectionState, *SessionState) ([]byte, error) - - // MinVersion contains the minimum TLS version that is acceptable. - // - // By default, TLS 1.2 is currently used as the minimum when acting as a - // client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum - // supported by this package, both as a client and as a server. - // - // The client-side default can temporarily be reverted to TLS 1.0 by - // including the value "x509sha1=1" in the GODEBUG environment variable. - // Note that this option will be removed in Go 1.19 (but it will still be - // possible to set this field to VersionTLS10 explicitly). - MinVersion uint16 - - // MaxVersion contains the maximum TLS version that is acceptable. - // - // By default, the maximum version supported by this package is used, - // which is currently TLS 1.3. - MaxVersion uint16 - - // CurvePreferences contains the elliptic curves that will be used in - // an ECDHE handshake, in preference order. If empty, the default will - // be used. The client will use the first preference as the type for - // its key share in TLS 1.3. This may change in the future. - CurvePreferences []CurveID - - // DynamicRecordSizingDisabled disables adaptive sizing of TLS records. - // When true, the largest possible TLS record size is always used. When - // false, the size of TLS records may be adjusted in an attempt to - // improve latency. - DynamicRecordSizingDisabled bool - - // Renegotiation controls what types of renegotiation are supported. - // The default, none, is correct for the vast majority of applications. - Renegotiation RenegotiationSupport - - // KeyLogWriter optionally specifies a destination for TLS master secrets - // in NSS key log format that can be used to allow external programs - // such as Wireshark to decrypt TLS connections. - // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. - // Use of KeyLogWriter compromises security and should only be - // used for debugging. - KeyLogWriter io.Writer - - // mutex protects sessionTicketKeys and autoSessionTicketKeys. - mutex sync.RWMutex - // sessionTicketKeys contains zero or more ticket keys. If set, it means - // the keys were set with SessionTicketKey or SetSessionTicketKeys. The - // first key is used for new tickets and any subsequent keys can be used to - // decrypt old tickets. The slice contents are not protected by the mutex - // and are immutable. - sessionTicketKeys []ticketKey - // autoSessionTicketKeys is like sessionTicketKeys but is owned by the - // auto-rotation logic. See Config.ticketKeys. - autoSessionTicketKeys []ticketKey -} - -// ticketKey is the internal representation of a session ticket key. -type ticketKey struct { - aesKey [16]byte - hmacKey [16]byte - // created is the time at which this ticket key was created. See Config.ticketKeys. - created time.Time -} - -// A Certificate is a chain of one or more certificates, leaf first. -type Certificate struct { - Certificate [][]byte - // PrivateKey contains the private key corresponding to the public key in - // Leaf. This must implement crypto.Signer with an RSA, ECDSA or Ed25519 PublicKey. - // For a server up to TLS 1.2, it can also implement crypto.Decrypter with - // an RSA PublicKey. - PrivateKey crypto.PrivateKey - // SupportedSignatureAlgorithms is an optional list restricting what - // signature algorithms the PrivateKey can be used for. - SupportedSignatureAlgorithms []SignatureScheme - // OCSPStaple contains an optional OCSP response which will be served - // to clients that request it. - OCSPStaple []byte - // SignedCertificateTimestamps contains an optional list of Signed - // Certificate Timestamps which will be served to clients that request it. - SignedCertificateTimestamps [][]byte - // Leaf is the parsed form of the leaf certificate, which may be initialized - // using x509.ParseCertificate to reduce per-handshake processing. If nil, - // the leaf certificate will be parsed as needed. - Leaf *x509.Certificate -} diff --git a/src/crypto/tls/ticket.go b/src/crypto/tls/ticket.go deleted file mode 100644 index 152efb7824..0000000000 --- a/src/crypto/tls/ticket.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tls - -// A SessionState is a resumable session. -type SessionState struct { -} - -// ClientSessionState contains the state needed by a client to -// resume a previous TLS session. -type ClientSessionState struct { - ticket []byte - session *SessionState -} diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go deleted file mode 100644 index 1520c30fca..0000000000 --- a/src/crypto/tls/tls.go +++ /dev/null @@ -1,114 +0,0 @@ -// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package tls partially implements TLS 1.2, as specified in RFC 5246, -// and TLS 1.3, as specified in RFC 8446. -package tls - -// BUG(agl): The crypto/tls package only implements some countermeasures -// against Lucky13 attacks on CBC-mode encryption, and only on SHA1 -// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and -// https://www.imperialviolet.org/2013/02/04/luckythirteen.html. - -import ( - "context" - "errors" - "fmt" - "net" -) - -// Client returns a new TLS client side connection -// using conn as the underlying transport. -// The config cannot be nil: users must set either ServerName or -// InsecureSkipVerify in the config. -func Client(conn net.Conn, config *Config) *net.TLSConn { - panic("tls.Client() not implemented") - return nil -} - -// A listener implements a network listener (net.Listener) for TLS connections. -type listener struct { - net.Listener - config *Config -} - -// NewListener creates a Listener which accepts connections from an inner -// Listener and wraps each connection with Server. -// The configuration config must be non-nil and must include -// at least one certificate or else set GetCertificate. -func NewListener(inner net.Listener, config *Config) net.Listener { - l := new(listener) - l.Listener = inner - l.config = config - return l -} - -// DialWithDialer connects to the given network address using dialer.Dial and -// then initiates a TLS handshake, returning the resulting TLS connection. Any -// timeout or deadline given in the dialer apply to connection and TLS -// handshake as a whole. -// -// DialWithDialer interprets a nil configuration as equivalent to the zero -// configuration; see the documentation of Config for the defaults. -// -// DialWithDialer uses context.Background internally; to specify the context, -// use Dialer.DialContext with NetDialer set to the desired dialer. -func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*net.TLSConn, error) { - switch network { - case "tcp", "tcp4": - default: - return nil, fmt.Errorf("Network %s not supported", network) - } - - return net.DialTLS(addr) -} - -// Dial connects to the given network address using net.Dial -// and then initiates a TLS handshake, returning the resulting -// TLS connection. -// Dial interprets a nil configuration as equivalent to -// the zero configuration; see the documentation of Config -// for the defaults. -func Dial(network, addr string, config *Config) (*net.TLSConn, error) { - return DialWithDialer(new(net.Dialer), network, addr, config) -} - -// Dialer dials TLS connections given a configuration and a Dialer for the -// underlying connection. -type Dialer struct { - // NetDialer is the optional dialer to use for the TLS connections' - // underlying TCP connections. - // A nil NetDialer is equivalent to the net.Dialer zero value. - NetDialer *net.Dialer - - // Config is the TLS configuration to use for new connections. - // A nil configuration is equivalent to the zero - // configuration; see the documentation of Config for the - // defaults. - Config *Config -} - -// DialContext connects to the given network address and initiates a TLS -// handshake, returning the resulting TLS connection. -// -// The provided Context must be non-nil. If the context expires before -// the connection is complete, an error is returned. Once successfully -// connected, any expiration of the context will not affect the -// connection. -// -// The returned Conn, if any, will always be of type *Conn. -func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { - return nil, errors.New("tls:DialContext not implemented") -} - -// LoadX509KeyPair reads and parses a public/private key pair from a pair -// of files. The files must contain PEM encoded data. The certificate file -// may contain intermediate certificates following the leaf certificate to -// form a certificate chain. On successful return, Certificate.Leaf will -// be nil because the parsed form of the certificate is not retained. -func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { - return Certificate{}, errors.New("tls:LoadX509KeyPair not implemented") -} diff --git a/src/net b/src/net deleted file mode 160000 index a237059610..0000000000 --- a/src/net +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a2370596106a621a9b9dd6ad930f0ec24cfee8d0 diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go new file mode 100644 index 0000000000..bd0c32f168 --- /dev/null +++ b/src/runtime/netpoll.go @@ -0,0 +1,45 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +// For debugging purposes this is used for all target architectures, but this is only valid for linux systems. +// TODO: add linux specific build tags +type pollDesc struct { + runtimeCtx uintptr +} + +func (pd *pollDesc) wait(mode int, isFile bool) error { + return nil +} + +const ( + pollNoError = 0 // no error + pollErrClosing = 1 // descriptor is closed + pollErrTimeout = 2 // I/O timeout + pollErrNotPollable = 3 // general error polling descriptor +) + +//go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset +func poll_runtime_pollReset(pd *pollDesc, mode int) int { + println("poll_runtime_pollReset not implemented", pd, mode) + return pollNoError +} + +//go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait +func poll_runtime_pollWait(pd *pollDesc, mode int) int { + println("poll_runtime_pollWait not implemented", pd, mode) + return pollNoError +} + +//go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline +func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { + println("poll_runtime_pollSetDeadline not implemented", pd, d, mode) +} + +//go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen +func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { + println("poll_runtime_pollOpen not implemented", fd) + return &pollDesc{runtimeCtx: 0x13371337}, pollNoError +} diff --git a/src/runtime/netpoll_generic.go b/src/runtime/netpoll_generic.go new file mode 100644 index 0000000000..6a41d61cbc --- /dev/null +++ b/src/runtime/netpoll_generic.go @@ -0,0 +1,31 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (js && wasm) || wasip1 || windows + +package runtime + +// Network poller descriptor. +// +// No heap pointers. +// For linux to call create Fds with a pollDesc, it needs a ctxRuntime pointer, so use the original pollDesc struct. +// On linux we have a heap. +type pollDesc struct{} + +//go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset +func poll_runtime_pollReset(pd *pollDesc, mode int) int { + println("poll_runtime_pollReset not implemented", pd, mode) + return 1 +} + +//go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait +func poll_runtime_pollWait(pd *pollDesc, mode int) int { + println("poll_runtime_pollWait not implemented", pd, mode) + return 1 +} + +//go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline +func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { + println("poll_runtime_pollSetDeadline not implemented", pd, d, mode) +} diff --git a/src/runtime/poll.go b/src/runtime/poll.go index 2713bbea7e..a880272c87 100644 --- a/src/runtime/poll.go +++ b/src/runtime/poll.go @@ -4,20 +4,21 @@ package runtime //go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit func poll_runtime_pollServerInit() { - panic("todo: runtime_pollServerInit") + // fmt.Printf("poll_runtime_pollServerInit not implemented, skipping panic\n") } -//go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen -func poll_runtime_pollOpen(fd uintptr) (uintptr, int) { - panic("todo: runtime_pollOpen") -} +// //go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen +// func poll_runtime_pollOpen(fd uintptr) (uintptr, int) { +// // fmt.Printf("poll_runtime_pollOpen not implemented, skipping panic\n") +// return 0, 0 +// } //go:linkname poll_runtime_pollClose internal/poll.runtime_pollClose func poll_runtime_pollClose(ctx uintptr) { - panic("todo: runtime_pollClose") + // fmt.Printf("poll_runtime_pollClose not implemented, skipping panic\n") } //go:linkname poll_runtime_pollUnblock internal/poll.runtime_pollUnblock func poll_runtime_pollUnblock(ctx uintptr) { - panic("todo: runtime_pollUnblock") + // fmt.Printf("poll_runtime_pollUnblock not implemented, skipping panic\n") } diff --git a/src/runtime/sync.go b/src/runtime/sync.go index a0851401a0..98c7cf92e1 100644 --- a/src/runtime/sync.go +++ b/src/runtime/sync.go @@ -4,10 +4,14 @@ package runtime //go:linkname semacquire internal/poll.runtime_Semacquire func semacquire(sema *uint32) { - panic("todo: semacquire") + // TODO the "net" pkg calls this, so panic() isn't an option. Right + // now, just ignore the call. + // panic("todo: semacquire") } //go:linkname semrelease internal/poll.runtime_Semrelease func semrelease(sema *uint32) { - panic("todo: semrelease") + // TODO the "net" pkg calls this, so panic() isn't an option. Right + // now, just ignore the call. + // panic("todo: semrelease") } diff --git a/src/syscall/forklock_tinygo.go b/src/syscall/forklock_tinygo.go new file mode 100644 index 0000000000..bdca5a21ed --- /dev/null +++ b/src/syscall/forklock_tinygo.go @@ -0,0 +1,38 @@ +//go:build tinygo + +package syscall + +// This is the original ForkLock: +// +// var ForkLock sync.RWMutex +// +// This requires importing sync, but importing sync causes an import loop: +// +// package tinygo.org/x/drivers/examples/net/tcpclient +// imports bytes +// imports io +// imports sync +// imports internal/task +// imports runtime/interrupt +// imports device/arm +// imports syscall +// imports sync: import cycle not allowed +// +// So for now, make our own stubbed-out ForkLock that doesn't use sync.. + +type forklock struct{} + +func (f forklock) RLock() {} +func (f forklock) RUnlock() {} + +var ForkLock forklock + +func CloseOnExec(fd int) { + system.CloseOnExec(fd) +} + +func SetNonblock(fd int, nonblocking bool) (err error) { + return system.SetNonblock(fd, nonblocking) +} + +type SysProcAttr struct{} From 3c34bbdb62ae56c7f1cedbbadece367a7b8e18ea Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Wed, 11 Sep 2024 14:22:33 +0200 Subject: [PATCH 03/14] remove print statements for cleaner execution Signed-off-by: leongross <leon.gross@9elements.com> --- src/runtime/netpoll.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index bd0c32f168..ee6a95d3b3 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -23,23 +23,23 @@ const ( //go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset func poll_runtime_pollReset(pd *pollDesc, mode int) int { - println("poll_runtime_pollReset not implemented", pd, mode) + // println("poll_runtime_pollReset not implemented", pd, mode) return pollNoError } //go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait func poll_runtime_pollWait(pd *pollDesc, mode int) int { - println("poll_runtime_pollWait not implemented", pd, mode) + // println("poll_runtime_pollWait not implemented", pd, mode) return pollNoError } //go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { - println("poll_runtime_pollSetDeadline not implemented", pd, d, mode) + // println("poll_runtime_pollSetDeadline not implemented", pd, d, mode) } //go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { - println("poll_runtime_pollOpen not implemented", fd) + // println("poll_runtime_pollOpen not implemented", fd) return &pollDesc{runtimeCtx: 0x13371337}, pollNoError } From facd62e3220c8b235292281b5ee5633d7a3116a6 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Wed, 18 Sep 2024 14:30:21 +0200 Subject: [PATCH 04/14] fix uncommited net exclusion if this does not work, do the folliwing steps: 1. remove net submodule 2. remove symlink in local ~/.cache/tinygo/goroot-<hash>/net 3. manual symlink yo local golang /usr/local/bin/src/net Signed-off-by: leongross <leon.gross@9elements.com> --- loader/goroot.go | 28 ++++++++++++++++++++-------- loader/loader.go | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/loader/goroot.go b/loader/goroot.go index 7a48ecbb1f..ecf4ac51e2 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -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, config.BuildTags()) // Resolve the merge links within the goroot. merge, err := listGorootMergeLinks(goroot, tinygoroot, overrides) @@ -225,9 +225,20 @@ func needsSyscallPackage(buildTags []string) bool { return false } +// linuxNetworking returns whether the unmodified go linux net stack should be used +// until the full rework of the net package is done. +func linuxNetworking(buildTags []string) bool { + for _, tag := range buildTags { + if tag == "linux" { + 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, buildTags []string) map[string]bool { paths := map[string]bool{ "": true, "crypto/": true, @@ -267,15 +278,16 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool { paths["crypto/internal/boring/sig/"] = false } - if needsSyscallPackage { + if needsSyscallPackage(buildTags) { paths["syscall/"] = true // include syscall/js } - // to enable network support for linux systems, reuse the Go version of the net package - // and the according runtime functions - // if runtime.GOOS == "linux" { - // paths["runtime/netpoll/"] = true - // } + if linuxNetworking(buildTags) { + for _, v := range []string{"crypto/tls/", "net/http/", "net/"} { + delete(paths, v) // remote entries so go stdlib is used + } + } + return paths } diff --git a/loader/loader.go b/loader/loader.go index e935a9de3a..f94fc70f8e 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -279,7 +279,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, p.config.BuildTags()) { if runtime.GOOS == "windows" { prefix = strings.ReplaceAll(prefix, "/", "\\") } From 90532f1069eb3d3d13cd6eb4afb2adb47d57072a Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Thu, 3 Oct 2024 17:36:40 +0200 Subject: [PATCH 05/14] bring back old net --- .gitmodules | 50 ++--- src/crypto/tls/common.go | 460 +++++++++++++++++++++++++++++++++++++++ src/crypto/tls/ticket.go | 16 ++ src/crypto/tls/tls.go | 114 ++++++++++ src/net | 1 + 5 files changed, 616 insertions(+), 25 deletions(-) create mode 100644 src/crypto/tls/common.go create mode 100644 src/crypto/tls/ticket.go create mode 100644 src/crypto/tls/tls.go create mode 160000 src/net diff --git a/.gitmodules b/.gitmodules index e4f627ae1c..2749d21f6a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,37 +1,37 @@ [submodule "lib/nrfx"] - path = lib/nrfx - url = https://github.com/NordicSemiconductor/nrfx.git + path = lib/nrfx + url = https://github.com/NordicSemiconductor/nrfx.git [submodule "lib/CMSIS"] - path = lib/CMSIS - url = https://github.com/ARM-software/CMSIS.git + path = lib/CMSIS + url = https://github.com/ARM-software/CMSIS.git [submodule "lib/avr"] - path = lib/avr - url = https://github.com/avr-rust/avr-mcu.git + path = lib/avr + url = https://github.com/avr-rust/avr-mcu.git [submodule "lib/cmsis-svd"] - path = lib/cmsis-svd - url = https://github.com/cmsis-svd/cmsis-svd-data.git - branch = main + path = lib/cmsis-svd + url = https://github.com/cmsis-svd/cmsis-svd-data.git + branch = main [submodule "lib/wasi-libc"] - path = lib/wasi-libc - url = https://github.com/WebAssembly/wasi-libc + path = lib/wasi-libc + url = https://github.com/WebAssembly/wasi-libc [submodule "lib/picolibc"] - path = lib/picolibc - url = https://github.com/keith-packard/picolibc.git + path = lib/picolibc + url = https://github.com/keith-packard/picolibc.git [submodule "lib/stm32-svd"] - path = lib/stm32-svd - url = https://github.com/tinygo-org/stm32-svd + path = lib/stm32-svd + url = https://github.com/tinygo-org/stm32-svd [submodule "lib/musl"] - path = lib/musl - url = git://git.musl-libc.org/musl + path = lib/musl + url = git://git.musl-libc.org/musl [submodule "lib/binaryen"] - path = lib/binaryen - url = https://github.com/WebAssembly/binaryen.git + path = lib/binaryen + url = https://github.com/WebAssembly/binaryen.git [submodule "lib/mingw-w64"] - path = lib/mingw-w64 - url = https://github.com/mingw-w64/mingw-w64.git + path = lib/mingw-w64 + url = https://github.com/mingw-w64/mingw-w64.git [submodule "lib/macos-minimal-sdk"] - path = lib/macos-minimal-sdk - url = https://github.com/aykevl/macos-minimal-sdk.git + path = lib/macos-minimal-sdk + url = https://github.com/aykevl/macos-minimal-sdk.git [submodule "lib/wasi-cli"] - path = lib/wasi-cli - url = https://github.com/WebAssembly/wasi-cli + path = lib/wasi-cli + url = https://github.com/WebAssembly/wasi-cli diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go new file mode 100644 index 0000000000..caf0198e15 --- /dev/null +++ b/src/crypto/tls/common.go @@ -0,0 +1,460 @@ +// TINYGO: The following is copied and modified from Go 1.19.3 official implementation. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "context" + "crypto" + "crypto/x509" + "fmt" + "io" + "net" + "sync" + "time" +) + +// CurveID is the type of a TLS identifier for an elliptic curve. See +// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8. +// +// In TLS 1.3, this type is called NamedGroup, but at this time this library +// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7. +type CurveID uint16 + +// CipherSuiteName returns the standard name for the passed cipher suite ID +// +// Not Implemented. +func CipherSuiteName(id uint16) string { + return fmt.Sprintf("0x%04X", id) +} + +// ConnectionState records basic TLS details about the connection. +type ConnectionState struct { + // TINYGO: empty; TLS connection offloaded to device + // + // Minimum (empty) fields for fortio.org/log http logging and others + // to compile and run. + PeerCertificates []*x509.Certificate + CipherSuite uint16 +} + +// ClientAuthType declares the policy the server will follow for +// TLS Client Authentication. +type ClientAuthType int + +// ClientSessionCache is a cache of ClientSessionState objects that can be used +// by a client to resume a TLS session with a given server. ClientSessionCache +// implementations should expect to be called concurrently from different +// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not +// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which +// are supported via this interface. +type ClientSessionCache interface { + // Get searches for a ClientSessionState associated with the given key. + // On return, ok is true if one was found. + Get(sessionKey string) (session *ClientSessionState, ok bool) + + // Put adds the ClientSessionState to the cache with the given key. It might + // get called multiple times in a connection if a TLS 1.3 server provides + // more than one session ticket. If called with a nil *ClientSessionState, + // it should remove the cache entry. + Put(sessionKey string, cs *ClientSessionState) +} + +//go:generate stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go + +// SignatureScheme identifies a signature algorithm supported by TLS. See +// RFC 8446, Section 4.2.3. +type SignatureScheme uint16 + +// ClientHelloInfo contains information from a ClientHello message in order to +// guide application logic in the GetCertificate and GetConfigForClient callbacks. +type ClientHelloInfo struct { + // CipherSuites lists the CipherSuites supported by the client (e.g. + // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256). + CipherSuites []uint16 + + // ServerName indicates the name of the server requested by the client + // in order to support virtual hosting. ServerName is only set if the + // client is using SNI (see RFC 4366, Section 3.1). + ServerName string + + // SupportedCurves lists the elliptic curves supported by the client. + // SupportedCurves is set only if the Supported Elliptic Curves + // Extension is being used (see RFC 4492, Section 5.1.1). + SupportedCurves []CurveID + + // SupportedPoints lists the point formats supported by the client. + // SupportedPoints is set only if the Supported Point Formats Extension + // is being used (see RFC 4492, Section 5.1.2). + SupportedPoints []uint8 + + // SignatureSchemes lists the signature and hash schemes that the client + // is willing to verify. SignatureSchemes is set only if the Signature + // Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1). + SignatureSchemes []SignatureScheme + + // SupportedProtos lists the application protocols supported by the client. + // SupportedProtos is set only if the Application-Layer Protocol + // Negotiation Extension is being used (see RFC 7301, Section 3.1). + // + // Servers can select a protocol by setting Config.NextProtos in a + // GetConfigForClient return value. + SupportedProtos []string + + // SupportedVersions lists the TLS versions supported by the client. + // For TLS versions less than 1.3, this is extrapolated from the max + // version advertised by the client, so values other than the greatest + // might be rejected if used. + SupportedVersions []uint16 + + // Conn is the underlying net.Conn for the connection. Do not read + // from, or write to, this connection; that will cause the TLS + // connection to fail. + Conn net.Conn + + // config is embedded by the GetCertificate or GetConfigForClient caller, + // for use with SupportsCertificate. + config *Config + + // ctx is the context of the handshake that is in progress. + ctx context.Context +} + +// CertificateRequestInfo contains information from a server's +// CertificateRequest message, which is used to demand a certificate and proof +// of control from a client. +type CertificateRequestInfo struct { + // AcceptableCAs contains zero or more, DER-encoded, X.501 + // Distinguished Names. These are the names of root or intermediate CAs + // that the server wishes the returned certificate to be signed by. An + // empty slice indicates that the server has no preference. + AcceptableCAs [][]byte + + // SignatureSchemes lists the signature schemes that the server is + // willing to verify. + SignatureSchemes []SignatureScheme + + // Version is the TLS version that was negotiated for this connection. + Version uint16 + + // ctx is the context of the handshake that is in progress. + ctx context.Context +} + +// RenegotiationSupport enumerates the different levels of support for TLS +// renegotiation. TLS renegotiation is the act of performing subsequent +// handshakes on a connection after the first. This significantly complicates +// the state machine and has been the source of numerous, subtle security +// issues. Initiating a renegotiation is not supported, but support for +// accepting renegotiation requests may be enabled. +// +// Even when enabled, the server may not change its identity between handshakes +// (i.e. the leaf certificate must be the same). Additionally, concurrent +// handshake and application data flow is not permitted so renegotiation can +// only be used with protocols that synchronise with the renegotiation, such as +// HTTPS. +// +// Renegotiation is not defined in TLS 1.3. +type RenegotiationSupport int + +// A Config structure is used to configure a TLS client or server. +// After one has been passed to a TLS function it must not be +// modified. A Config may be reused; the tls package will also not +// modify it. +type Config struct { + // Rand provides the source of entropy for nonces and RSA blinding. + // If Rand is nil, TLS uses the cryptographic random reader in package + // crypto/rand. + // The Reader must be safe for use by multiple goroutines. + Rand io.Reader + + // Time returns the current time as the number of seconds since the epoch. + // If Time is nil, TLS uses time.Now. + Time func() time.Time + + // Certificates contains one or more certificate chains to present to the + // other side of the connection. The first certificate compatible with the + // peer's requirements is selected automatically. + // + // Server configurations must set one of Certificates, GetCertificate or + // GetConfigForClient. Clients doing client-authentication may set either + // Certificates or GetClientCertificate. + // + // Note: if there are multiple Certificates, and they don't have the + // optional field Leaf set, certificate selection will incur a significant + // per-handshake performance cost. + Certificates []Certificate + + // NameToCertificate maps from a certificate name to an element of + // Certificates. Note that a certificate name can be of the form + // '*.example.com' and so doesn't have to be a domain name as such. + // + // Deprecated: NameToCertificate only allows associating a single + // certificate with a given name. Leave this field nil to let the library + // select the first compatible chain from Certificates. + NameToCertificate map[string]*Certificate + + // GetCertificate returns a Certificate based on the given + // ClientHelloInfo. It will only be called if the client supplies SNI + // information or if Certificates is empty. + // + // If GetCertificate is nil or returns nil, then the certificate is + // retrieved from NameToCertificate. If NameToCertificate is nil, the + // best element of Certificates will be used. + // + // Once a Certificate is returned it should not be modified. + GetCertificate func(*ClientHelloInfo) (*Certificate, error) + + // GetClientCertificate, if not nil, is called when a server requests a + // certificate from a client. If set, the contents of Certificates will + // be ignored. + // + // If GetClientCertificate returns an error, the handshake will be + // aborted and that error will be returned. Otherwise + // GetClientCertificate must return a non-nil Certificate. If + // Certificate.Certificate is empty then no certificate will be sent to + // the server. If this is unacceptable to the server then it may abort + // the handshake. + // + // GetClientCertificate may be called multiple times for the same + // connection if renegotiation occurs or if TLS 1.3 is in use. + // + // Once a Certificate is returned it should not be modified. + GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error) + + // GetConfigForClient, if not nil, is called after a ClientHello is + // received from a client. It may return a non-nil Config in order to + // change the Config that will be used to handle this connection. If + // the returned Config is nil, the original Config will be used. The + // Config returned by this callback may not be subsequently modified. + // + // If GetConfigForClient is nil, the Config passed to Server() will be + // used for all connections. + // + // If SessionTicketKey was explicitly set on the returned Config, or if + // SetSessionTicketKeys was called on the returned Config, those keys will + // be used. Otherwise, the original Config keys will be used (and possibly + // rotated if they are automatically managed). + GetConfigForClient func(*ClientHelloInfo) (*Config, error) + + // VerifyPeerCertificate, if not nil, is called after normal + // certificate verification by either a TLS client or server. It + // receives the raw ASN.1 certificates provided by the peer and also + // any verified chains that normal processing found. If it returns a + // non-nil error, the handshake is aborted and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. If normal verification is disabled (on the + // client when InsecureSkipVerify is set, or on a server when ClientAuth is + // RequestClientCert or RequireAnyClientCert), then this callback will be + // considered but the verifiedChains argument will always be nil. When + // ClientAuth is NoClientCert, this callback is not called on the server. + // rawCerts may be empty on the server if ClientAuth is RequestClientCert or + // VerifyClientCertIfGiven. + // + // This callback is not invoked on resumed connections, as certificates are + // not re-verified on resumption. + // + // verifiedChains and its contents should not be modified. + VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + + // VerifyConnection, if not nil, is called after normal certificate + // verification and after VerifyPeerCertificate by either a TLS client + // or server. If it returns a non-nil error, the handshake is aborted + // and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. This callback will run for all connections, + // including resumptions, regardless of InsecureSkipVerify or ClientAuth + // settings. + VerifyConnection func(ConnectionState) error + + // RootCAs defines the set of root certificate authorities + // that clients use when verifying server certificates. + // If RootCAs is nil, TLS uses the host's root CA set. + RootCAs *x509.CertPool + + // NextProtos is a list of supported application level protocols, in + // order of preference. If both peers support ALPN, the selected + // protocol will be one from this list, and the connection will fail + // if there is no mutually supported protocol. If NextProtos is empty + // or the peer doesn't support ALPN, the connection will succeed and + // ConnectionState.NegotiatedProtocol will be empty. + NextProtos []string + + // ServerName is used to verify the hostname on the returned + // certificates unless InsecureSkipVerify is given. It is also included + // in the client's handshake to support virtual hosting unless it is + // an IP address. + ServerName string + + // ClientAuth determines the server's policy for + // TLS Client Authentication. The default is NoClientCert. + ClientAuth ClientAuthType + + // ClientCAs defines the set of root certificate authorities + // that servers use if required to verify a client certificate + // by the policy in ClientAuth. + ClientCAs *x509.CertPool + + // InsecureSkipVerify controls whether a client verifies the server's + // certificate chain and host name. If InsecureSkipVerify is true, crypto/tls + // accepts any certificate presented by the server and any host name in that + // certificate. In this mode, TLS is susceptible to machine-in-the-middle + // attacks unless custom verification is used. This should be used only for + // testing or in combination with VerifyConnection or VerifyPeerCertificate. + InsecureSkipVerify bool + + // CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of + // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable. + // + // If CipherSuites is nil, a safe default list is used. The default cipher + // suites might change over time. + CipherSuites []uint16 + + // PreferServerCipherSuites is a legacy field and has no effect. + // + // It used to control whether the server would follow the client's or the + // server's preference. Servers now select the best mutually supported + // cipher suite based on logic that takes into account inferred client + // hardware, server hardware, and security. + // + // Deprecated: PreferServerCipherSuites is ignored. + PreferServerCipherSuites bool + + // SessionTicketsDisabled may be set to true to disable session ticket and + // PSK (resumption) support. Note that on clients, session ticket support is + // also disabled if ClientSessionCache is nil. + SessionTicketsDisabled bool + + // SessionTicketKey is used by TLS servers to provide session resumption. + // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled + // with random data before the first server handshake. + // + // Deprecated: if this field is left at zero, session ticket keys will be + // automatically rotated every day and dropped after seven days. For + // customizing the rotation schedule or synchronizing servers that are + // terminating connections for the same host, use SetSessionTicketKeys. + SessionTicketKey [32]byte + + // ClientSessionCache is a cache of ClientSessionState entries for TLS + // session resumption. It is only used by clients. + ClientSessionCache ClientSessionCache + + // UnwrapSession is called on the server to turn a ticket/identity + // previously produced by [WrapSession] into a usable session. + // + // UnwrapSession will usually either decrypt a session state in the ticket + // (for example with [Config.EncryptTicket]), or use the ticket as a handle + // to recover a previously stored state. It must use [ParseSessionState] to + // deserialize the session state. + // + // If UnwrapSession returns an error, the connection is terminated. If it + // returns (nil, nil), the session is ignored. crypto/tls may still choose + // not to resume the returned session. + UnwrapSession func(identity []byte, cs ConnectionState) (*SessionState, error) + + // WrapSession is called on the server to produce a session ticket/identity. + // + // WrapSession must serialize the session state with [SessionState.Bytes]. + // It may then encrypt the serialized state (for example with + // [Config.DecryptTicket]) and use it as the ticket, or store the state and + // return a handle for it. + // + // If WrapSession returns an error, the connection is terminated. + // + // Warning: the return value will be exposed on the wire and to clients in + // plaintext. The application is in charge of encrypting and authenticating + // it (and rotating keys) or returning high-entropy identifiers. Failing to + // do so correctly can compromise current, previous, and future connections + // depending on the protocol version. + WrapSession func(ConnectionState, *SessionState) ([]byte, error) + + // MinVersion contains the minimum TLS version that is acceptable. + // + // By default, TLS 1.2 is currently used as the minimum when acting as a + // client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum + // supported by this package, both as a client and as a server. + // + // The client-side default can temporarily be reverted to TLS 1.0 by + // including the value "x509sha1=1" in the GODEBUG environment variable. + // Note that this option will be removed in Go 1.19 (but it will still be + // possible to set this field to VersionTLS10 explicitly). + MinVersion uint16 + + // MaxVersion contains the maximum TLS version that is acceptable. + // + // By default, the maximum version supported by this package is used, + // which is currently TLS 1.3. + MaxVersion uint16 + + // CurvePreferences contains the elliptic curves that will be used in + // an ECDHE handshake, in preference order. If empty, the default will + // be used. The client will use the first preference as the type for + // its key share in TLS 1.3. This may change in the future. + CurvePreferences []CurveID + + // DynamicRecordSizingDisabled disables adaptive sizing of TLS records. + // When true, the largest possible TLS record size is always used. When + // false, the size of TLS records may be adjusted in an attempt to + // improve latency. + DynamicRecordSizingDisabled bool + + // Renegotiation controls what types of renegotiation are supported. + // The default, none, is correct for the vast majority of applications. + Renegotiation RenegotiationSupport + + // KeyLogWriter optionally specifies a destination for TLS master secrets + // in NSS key log format that can be used to allow external programs + // such as Wireshark to decrypt TLS connections. + // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. + // Use of KeyLogWriter compromises security and should only be + // used for debugging. + KeyLogWriter io.Writer + + // mutex protects sessionTicketKeys and autoSessionTicketKeys. + mutex sync.RWMutex + // sessionTicketKeys contains zero or more ticket keys. If set, it means + // the keys were set with SessionTicketKey or SetSessionTicketKeys. The + // first key is used for new tickets and any subsequent keys can be used to + // decrypt old tickets. The slice contents are not protected by the mutex + // and are immutable. + sessionTicketKeys []ticketKey + // autoSessionTicketKeys is like sessionTicketKeys but is owned by the + // auto-rotation logic. See Config.ticketKeys. + autoSessionTicketKeys []ticketKey +} + +// ticketKey is the internal representation of a session ticket key. +type ticketKey struct { + aesKey [16]byte + hmacKey [16]byte + // created is the time at which this ticket key was created. See Config.ticketKeys. + created time.Time +} + +// A Certificate is a chain of one or more certificates, leaf first. +type Certificate struct { + Certificate [][]byte + // PrivateKey contains the private key corresponding to the public key in + // Leaf. This must implement crypto.Signer with an RSA, ECDSA or Ed25519 PublicKey. + // For a server up to TLS 1.2, it can also implement crypto.Decrypter with + // an RSA PublicKey. + PrivateKey crypto.PrivateKey + // SupportedSignatureAlgorithms is an optional list restricting what + // signature algorithms the PrivateKey can be used for. + SupportedSignatureAlgorithms []SignatureScheme + // OCSPStaple contains an optional OCSP response which will be served + // to clients that request it. + OCSPStaple []byte + // SignedCertificateTimestamps contains an optional list of Signed + // Certificate Timestamps which will be served to clients that request it. + SignedCertificateTimestamps [][]byte + // Leaf is the parsed form of the leaf certificate, which may be initialized + // using x509.ParseCertificate to reduce per-handshake processing. If nil, + // the leaf certificate will be parsed as needed. + Leaf *x509.Certificate +} diff --git a/src/crypto/tls/ticket.go b/src/crypto/tls/ticket.go new file mode 100644 index 0000000000..152efb7824 --- /dev/null +++ b/src/crypto/tls/ticket.go @@ -0,0 +1,16 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +// A SessionState is a resumable session. +type SessionState struct { +} + +// ClientSessionState contains the state needed by a client to +// resume a previous TLS session. +type ClientSessionState struct { + ticket []byte + session *SessionState +} diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go new file mode 100644 index 0000000000..1520c30fca --- /dev/null +++ b/src/crypto/tls/tls.go @@ -0,0 +1,114 @@ +// TINYGO: The following is copied and modified from Go 1.21.4 official implementation. + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tls partially implements TLS 1.2, as specified in RFC 5246, +// and TLS 1.3, as specified in RFC 8446. +package tls + +// BUG(agl): The crypto/tls package only implements some countermeasures +// against Lucky13 attacks on CBC-mode encryption, and only on SHA1 +// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and +// https://www.imperialviolet.org/2013/02/04/luckythirteen.html. + +import ( + "context" + "errors" + "fmt" + "net" +) + +// Client returns a new TLS client side connection +// using conn as the underlying transport. +// The config cannot be nil: users must set either ServerName or +// InsecureSkipVerify in the config. +func Client(conn net.Conn, config *Config) *net.TLSConn { + panic("tls.Client() not implemented") + return nil +} + +// A listener implements a network listener (net.Listener) for TLS connections. +type listener struct { + net.Listener + config *Config +} + +// NewListener creates a Listener which accepts connections from an inner +// Listener and wraps each connection with Server. +// The configuration config must be non-nil and must include +// at least one certificate or else set GetCertificate. +func NewListener(inner net.Listener, config *Config) net.Listener { + l := new(listener) + l.Listener = inner + l.config = config + return l +} + +// DialWithDialer connects to the given network address using dialer.Dial and +// then initiates a TLS handshake, returning the resulting TLS connection. Any +// timeout or deadline given in the dialer apply to connection and TLS +// handshake as a whole. +// +// DialWithDialer interprets a nil configuration as equivalent to the zero +// configuration; see the documentation of Config for the defaults. +// +// DialWithDialer uses context.Background internally; to specify the context, +// use Dialer.DialContext with NetDialer set to the desired dialer. +func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*net.TLSConn, error) { + switch network { + case "tcp", "tcp4": + default: + return nil, fmt.Errorf("Network %s not supported", network) + } + + return net.DialTLS(addr) +} + +// Dial connects to the given network address using net.Dial +// and then initiates a TLS handshake, returning the resulting +// TLS connection. +// Dial interprets a nil configuration as equivalent to +// the zero configuration; see the documentation of Config +// for the defaults. +func Dial(network, addr string, config *Config) (*net.TLSConn, error) { + return DialWithDialer(new(net.Dialer), network, addr, config) +} + +// Dialer dials TLS connections given a configuration and a Dialer for the +// underlying connection. +type Dialer struct { + // NetDialer is the optional dialer to use for the TLS connections' + // underlying TCP connections. + // A nil NetDialer is equivalent to the net.Dialer zero value. + NetDialer *net.Dialer + + // Config is the TLS configuration to use for new connections. + // A nil configuration is equivalent to the zero + // configuration; see the documentation of Config for the + // defaults. + Config *Config +} + +// DialContext connects to the given network address and initiates a TLS +// handshake, returning the resulting TLS connection. +// +// The provided Context must be non-nil. If the context expires before +// the connection is complete, an error is returned. Once successfully +// connected, any expiration of the context will not affect the +// connection. +// +// The returned Conn, if any, will always be of type *Conn. +func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { + return nil, errors.New("tls:DialContext not implemented") +} + +// LoadX509KeyPair reads and parses a public/private key pair from a pair +// of files. The files must contain PEM encoded data. The certificate file +// may contain intermediate certificates following the leaf certificate to +// form a certificate chain. On successful return, Certificate.Leaf will +// be nil because the parsed form of the certificate is not retained. +func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { + return Certificate{}, errors.New("tls:LoadX509KeyPair not implemented") +} diff --git a/src/net b/src/net new file mode 160000 index 0000000000..a237059610 --- /dev/null +++ b/src/net @@ -0,0 +1 @@ +Subproject commit a2370596106a621a9b9dd6ad930f0ec24cfee8d0 From 8224b0e3a4fdd2488fd065325af96e77e609e4a3 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Fri, 4 Oct 2024 16:01:00 +0200 Subject: [PATCH 06/14] switch net via build tags Signed-off-by: leongross <leon.gross@9elements.com> --- .gitmodules | 3 +++ loader/goroot.go | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2749d21f6a..29778d6709 100644 --- a/.gitmodules +++ b/.gitmodules @@ -35,3 +35,6 @@ [submodule "lib/wasi-cli"] path = lib/wasi-cli url = https://github.com/WebAssembly/wasi-cli +[submodule "src/net"] + path = src/net + url = https://github.com/tinygo-org/net diff --git a/loader/goroot.go b/loader/goroot.go index ecf4ac51e2..40f46877d0 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -236,8 +236,10 @@ func linuxNetworking(buildTags []string) bool { return false } -// The boolean indicates whether to merge the subdirs. True means merge, false -// means use the TinyGo version. +// The boolean indicates whether to merge the subdirs. +// +// True: Merge the golang and tinygo source directories. +// False: Uses the TinyGo version exclusively. func pathsToOverride(goMinor int, buildTags []string) map[string]bool { paths := map[string]bool{ "": true, @@ -261,7 +263,7 @@ func pathsToOverride(goMinor int, buildTags []string) map[string]bool { "internal/task/": false, "internal/wasi/": false, "machine/": false, - "net/": true, + "net/": true, // this is important if the GOOS != linux "net/http/": false, "os/": true, "reflect/": false, @@ -282,6 +284,8 @@ func pathsToOverride(goMinor int, buildTags []string) map[string]bool { paths["syscall/"] = true // include syscall/js } + // To make sure the correct version of the net package is used, it is advised + // to clean the go cache before building if linuxNetworking(buildTags) { for _, v := range []string{"crypto/tls/", "net/http/", "net/"} { delete(paths, v) // remote entries so go stdlib is used From 82f1b96c289de753caf864286818ff52574fe8a5 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Fri, 4 Oct 2024 16:12:57 +0200 Subject: [PATCH 07/14] adjust target os specific build tags --- src/runtime/netpoll.go | 4 +++- src/runtime/netpoll_generic.go | 2 +- src/syscall/forklock_tinygo.go | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index ee6a95d3b3..1a182e3491 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build linux + package runtime // For debugging purposes this is used for all target architectures, but this is only valid for linux systems. @@ -41,5 +43,5 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { //go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { // println("poll_runtime_pollOpen not implemented", fd) - return &pollDesc{runtimeCtx: 0x13371337}, pollNoError + return &pollDesc{runtimeCtx: uintptr(0xdeadbeef)}, pollNoError } diff --git a/src/runtime/netpoll_generic.go b/src/runtime/netpoll_generic.go index 6a41d61cbc..123d190770 100644 --- a/src/runtime/netpoll_generic.go +++ b/src/runtime/netpoll_generic.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (js && wasm) || wasip1 || windows +//go:build !linux package runtime diff --git a/src/syscall/forklock_tinygo.go b/src/syscall/forklock_tinygo.go index bdca5a21ed..db9469e2fa 100644 --- a/src/syscall/forklock_tinygo.go +++ b/src/syscall/forklock_tinygo.go @@ -1,4 +1,4 @@ -//go:build tinygo +//go:build tinygo && linux && !wasip1 && !wasip2 && !darwin package syscall @@ -35,4 +35,4 @@ func SetNonblock(fd int, nonblocking bool) (err error) { return system.SetNonblock(fd, nonblocking) } -type SysProcAttr struct{} +// type SysProcAttr struct{} From 46394c1bb0654fa311aeb19fb0557b69727168dc Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Tue, 8 Oct 2024 11:46:13 +0200 Subject: [PATCH 08/14] chnage macos build --- src/syscall/{forklock_tinygo.go => forklock.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/syscall/{forklock_tinygo.go => forklock.go} (100%) diff --git a/src/syscall/forklock_tinygo.go b/src/syscall/forklock.go similarity index 100% rename from src/syscall/forklock_tinygo.go rename to src/syscall/forklock.go From 42348c87e5580e601b5513ea8c469f086f907729 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Fri, 11 Oct 2024 13:14:50 +0200 Subject: [PATCH 09/14] add integration tests for net --- testdata/net.go | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ testdata/net.txt | 2 ++ 2 files changed, 84 insertions(+) create mode 100644 testdata/net.go create mode 100644 testdata/net.txt diff --git a/testdata/net.go b/testdata/net.go new file mode 100644 index 0000000000..746becbce4 --- /dev/null +++ b/testdata/net.go @@ -0,0 +1,82 @@ +package main + +import ( + "bytes" + "fmt" + "net" + "strconv" + "time" +) + +// Test golang network package integration for tinygo. +// This test is not exhaustive and only tests the basic functionality of the package. + +const ( + TEST_PORT = 9000 +) + +var ( + testsPassed uint + err error + sendBuf = &bytes.Buffer{} + recvBuf = &bytes.Buffer{} +) + +var ( + testDialListenData = []byte("Hello tinygo :)") +) + +func TestDialListen() { + // listen thread + go func() { + ln, err := net.Listen("tcp", ":"+strconv.FormatInt(TEST_PORT, 10)) + if err != nil { + fmt.Printf("error listening: %v\n", err) + return + } + + conn, err := ln.Accept() + if err != nil { + fmt.Printf("error accepting: %v\n", err) + return + } + + recvBuf.Reset() + _, err = conn.Read(recvBuf.Bytes()) + if err != nil { + fmt.Printf("error reading: %v\n", err) + return + } + + // TODO: this is racy + if recvBuf.String() != string(testDialListenData) { + fmt.Printf("error: received data does not match sent data: '%s' != '%s'\n", recvBuf.String(), string(testDialListenData)) + return + } + conn.Close() + + return + }() + + // hacky way to wait for the listener to start + time.Sleep(1 * time.Second) + + sendBuf.Reset() + fmt.Fprint(sendBuf, testDialListenData) + conn, err := net.Dial("tcp4", "127.0.0.1:"+strconv.FormatInt(TEST_PORT, 10)) + if err != nil { + fmt.Printf("error dialing: %v\n", err) + return + } + + if _, err = conn.Write(sendBuf.Bytes()); err != nil { + fmt.Printf("error writing: %v\n", err) + return + } +} + +func main() { + fmt.Printf("test: net start\n") + TestDialListen() + fmt.Printf("test: net end\n") +} diff --git a/testdata/net.txt b/testdata/net.txt new file mode 100644 index 0000000000..82681e45a0 --- /dev/null +++ b/testdata/net.txt @@ -0,0 +1,2 @@ +test: net start +test: net end \ No newline at end of file From a695ab6b2ca3a07953d348701a100e4ff4761544 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Fri, 11 Oct 2024 16:33:19 +0200 Subject: [PATCH 10/14] further restrict net package usage Signed-off-by: leongross <leon.gross@9elements.com> --- loader/goroot.go | 10 ++++++++-- src/runtime/netpoll.go | 2 +- src/syscall/forklock.go | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/loader/goroot.go b/loader/goroot.go index 40f46877d0..ca56c4d49b 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -227,13 +227,19 @@ func needsSyscallPackage(buildTags []string) bool { // linuxNetworking returns whether the unmodified go linux net stack should be used // until the full rework of the net package is done. +// To ensure the correct build target, check for the following tags: +// linux && !baremetal && !nintendoswitch && !tinygo.wasm func linuxNetworking(buildTags []string) bool { + targetLinux := false for _, tag := range buildTags { if tag == "linux" { - return true + targetLinux = true + } + if tag == "baremetal" || tag == "nintendoswitch" || tag == "tinygo.wasm" { + return false } } - return false + return targetLinux } // The boolean indicates whether to merge the subdirs. diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index 1a182e3491..e4157b994f 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -43,5 +43,5 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { //go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { // println("poll_runtime_pollOpen not implemented", fd) - return &pollDesc{runtimeCtx: uintptr(0xdeadbeef)}, pollNoError + return &pollDesc{runtimeCtx: uintptr(0x1337)}, pollNoError } diff --git a/src/syscall/forklock.go b/src/syscall/forklock.go index db9469e2fa..cab4721400 100644 --- a/src/syscall/forklock.go +++ b/src/syscall/forklock.go @@ -1,4 +1,4 @@ -//go:build tinygo && linux && !wasip1 && !wasip2 && !darwin +//go:build tinygo && linux && !wasip1 && !wasip2 && !darwin && !baremetal package syscall From a39597ca455f7262377f15cd2e30c09f5d017d64 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Mon, 4 Nov 2024 10:57:00 +0100 Subject: [PATCH 11/14] add preliminary semaphore implementation Signed-off-by: leongross <leon.gross@9elements.com> --- .gitmodules | 55 ++++++++++++++-------------- src/runtime/netpoll_generic.go | 11 +++++- src/runtime/sync.go | 67 +++++++++++++++++++++++++++++++--- testdata/net.go | 51 +++++++++++--------------- 4 files changed, 119 insertions(+), 65 deletions(-) diff --git a/.gitmodules b/.gitmodules index 29778d6709..91bd14a7d7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,40 +1,41 @@ [submodule "lib/nrfx"] - path = lib/nrfx - url = https://github.com/NordicSemiconductor/nrfx.git + path = lib/nrfx + url = https://github.com/NordicSemiconductor/nrfx.git [submodule "lib/CMSIS"] - path = lib/CMSIS - url = https://github.com/ARM-software/CMSIS.git + path = lib/CMSIS + url = https://github.com/ARM-software/CMSIS.git [submodule "lib/avr"] - path = lib/avr - url = https://github.com/avr-rust/avr-mcu.git + path = lib/avr + url = https://github.com/avr-rust/avr-mcu.git [submodule "lib/cmsis-svd"] - path = lib/cmsis-svd - url = https://github.com/cmsis-svd/cmsis-svd-data.git - branch = main + path = lib/cmsis-svd + url = https://github.com/cmsis-svd/cmsis-svd-data.git + branch = main [submodule "lib/wasi-libc"] - path = lib/wasi-libc - url = https://github.com/WebAssembly/wasi-libc + path = lib/wasi-libc + url = https://github.com/WebAssembly/wasi-libc [submodule "lib/picolibc"] - path = lib/picolibc - url = https://github.com/keith-packard/picolibc.git + path = lib/picolibc + url = https://github.com/keith-packard/picolibc.git [submodule "lib/stm32-svd"] - path = lib/stm32-svd - url = https://github.com/tinygo-org/stm32-svd + path = lib/stm32-svd + url = https://github.com/tinygo-org/stm32-svd [submodule "lib/musl"] - path = lib/musl - url = git://git.musl-libc.org/musl + path = lib/musl + url = git://git.musl-libc.org/musl [submodule "lib/binaryen"] - path = lib/binaryen - url = https://github.com/WebAssembly/binaryen.git + path = lib/binaryen + url = https://github.com/WebAssembly/binaryen.git [submodule "lib/mingw-w64"] - path = lib/mingw-w64 - url = https://github.com/mingw-w64/mingw-w64.git + path = lib/mingw-w64 + url = https://github.com/mingw-w64/mingw-w64.git [submodule "lib/macos-minimal-sdk"] - path = lib/macos-minimal-sdk - url = https://github.com/aykevl/macos-minimal-sdk.git -[submodule "lib/wasi-cli"] - path = lib/wasi-cli - url = https://github.com/WebAssembly/wasi-cli + path = lib/macos-minimal-sdk + url = https://github.com/aykevl/macos-minimal-sdk.git [submodule "src/net"] path = src/net - url = https://github.com/tinygo-org/net + url = https://github.com/tinygo-org/net.git + branch = dev +[submodule "lib/wasi-cli"] + path = lib/wasi-cli + url = https://github.com/WebAssembly/wasi-cli diff --git a/src/runtime/netpoll_generic.go b/src/runtime/netpoll_generic.go index 123d190770..f5f26d84d9 100644 --- a/src/runtime/netpoll_generic.go +++ b/src/runtime/netpoll_generic.go @@ -6,6 +6,13 @@ package runtime +const ( + pollNoError = 0 // no error + pollErrClosing = 1 // descriptor is closed + pollErrTimeout = 2 // I/O timeout + pollErrNotPollable = 3 // general error polling descriptor +) + // Network poller descriptor. // // No heap pointers. @@ -16,13 +23,13 @@ type pollDesc struct{} //go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset func poll_runtime_pollReset(pd *pollDesc, mode int) int { println("poll_runtime_pollReset not implemented", pd, mode) - return 1 + return pollErrClosing } //go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait func poll_runtime_pollWait(pd *pollDesc, mode int) int { println("poll_runtime_pollWait not implemented", pd, mode) - return 1 + return pollErrClosing } //go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline diff --git a/src/runtime/sync.go b/src/runtime/sync.go index 98c7cf92e1..3d302ec7d2 100644 --- a/src/runtime/sync.go +++ b/src/runtime/sync.go @@ -1,17 +1,72 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package runtime +import ( + "sync/atomic" + "unsafe" +) + // This file contains stub implementations for internal/poll. +// The official golang implementation states: +// +// "That is, don't think of these as semaphores. +// Think of them as a way to implement sleep and wakeup +// such that every sleep is paired with a single wakeup, +// even if, due to races, the wakeup happens before the sleep." +// +// This is an experimental and probably incomplete implementation of the +// semaphore system, tailed to the network use case. That means, that it does not +// implement the modularity that the semacquire/semacquire1 implementation model +// offers, which in fact is emitted here entirely. +// This means we assume the following constant settings from the golang standard +// library: lifo=false,profile=semaBlock,skipframe=0,reason=waitReasonSemaquire + +type semaRoot struct { + nwait atomic.Uint32 +} + +var semtable semTable + +// Prime to not correlate with any user patterns. +const semTabSize = 251 + +type semTable [semTabSize]struct { + root semaRoot + pad [64 - unsafe.Sizeof(semaRoot{})]byte // only 64 x86_64, make this variable +} + +func (t *semTable) rootFor(addr *uint32) *semaRoot { + return &t[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root +} //go:linkname semacquire internal/poll.runtime_Semacquire func semacquire(sema *uint32) { - // TODO the "net" pkg calls this, so panic() isn't an option. Right - // now, just ignore the call. - // panic("todo: semacquire") + if cansemacquire(sema) { + return + } +} + +// Copied from src/runtime/sema.go +func cansemacquire(addr *uint32) bool { + for { + v := atomic.LoadUint32(addr) + if v == 0 { + return false + } + if atomic.CompareAndSwapUint32(addr, v, v-1) { + return true + } + } } //go:linkname semrelease internal/poll.runtime_Semrelease func semrelease(sema *uint32) { - // TODO the "net" pkg calls this, so panic() isn't an option. Right - // now, just ignore the call. - // panic("todo: semrelease") + root := semtable.rootFor(sema) + atomic.AddUint32(sema, 1) + if root.nwait.Load() == 0 { + return + } } diff --git a/testdata/net.go b/testdata/net.go index 746becbce4..2a7908fa1c 100644 --- a/testdata/net.go +++ b/testdata/net.go @@ -1,25 +1,19 @@ package main import ( - "bytes" - "fmt" + "io" "net" "strconv" - "time" ) // Test golang network package integration for tinygo. // This test is not exhaustive and only tests the basic functionality of the package. -const ( - TEST_PORT = 9000 -) - var ( testsPassed uint + lnPort int err error - sendBuf = &bytes.Buffer{} - recvBuf = &bytes.Buffer{} + recvBuf []byte ) var ( @@ -28,29 +22,30 @@ var ( func TestDialListen() { // listen thread + listenReady := make(chan bool, 1) go func() { - ln, err := net.Listen("tcp", ":"+strconv.FormatInt(TEST_PORT, 10)) + ln, err := net.Listen("tcp4", ":0") if err != nil { - fmt.Printf("error listening: %v\n", err) + println("error listening: ", err) return } + lnPort = ln.Addr().(*net.TCPAddr).Port + listenReady <- true conn, err := ln.Accept() if err != nil { - fmt.Printf("error accepting: %v\n", err) + println("error accepting:", err) return } - recvBuf.Reset() - _, err = conn.Read(recvBuf.Bytes()) - if err != nil { - fmt.Printf("error reading: %v\n", err) + recvBuf = make([]byte, len(testDialListenData)) + if _, err := io.ReadFull(conn, recvBuf); err != nil { + println("error reading: ", err) return } - // TODO: this is racy - if recvBuf.String() != string(testDialListenData) { - fmt.Printf("error: received data does not match sent data: '%s' != '%s'\n", recvBuf.String(), string(testDialListenData)) + if string(recvBuf) != string(testDialListenData) { + println("error: received data does not match sent data", string(recvBuf), " != ", string(testDialListenData)) return } conn.Close() @@ -58,25 +53,21 @@ func TestDialListen() { return }() - // hacky way to wait for the listener to start - time.Sleep(1 * time.Second) - - sendBuf.Reset() - fmt.Fprint(sendBuf, testDialListenData) - conn, err := net.Dial("tcp4", "127.0.0.1:"+strconv.FormatInt(TEST_PORT, 10)) + <-listenReady + conn, err := net.Dial("tcp4", "127.0.0.1:"+strconv.FormatInt(int64(lnPort), 10)) if err != nil { - fmt.Printf("error dialing: %v\n", err) + println("error dialing: ", err) return } - if _, err = conn.Write(sendBuf.Bytes()); err != nil { - fmt.Printf("error writing: %v\n", err) + if _, err = conn.Write(testDialListenData); err != nil { + println("error writing: ", err) return } } func main() { - fmt.Printf("test: net start\n") + println("test: net start") TestDialListen() - fmt.Printf("test: net end\n") + println("test: net end") } From 2e04cc2f5ce6d042dafad4da551bc3a96fe591a9 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Mon, 11 Nov 2024 11:55:47 +0100 Subject: [PATCH 12/14] remove old forklock stub and replace it with sync.RWMutex Signed-off-by: leongross <leon.gross@9elements.com> --- src/syscall/forklock.go | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/src/syscall/forklock.go b/src/syscall/forklock.go index cab4721400..a158b24871 100644 --- a/src/syscall/forklock.go +++ b/src/syscall/forklock.go @@ -1,31 +1,12 @@ -//go:build tinygo && linux && !wasip1 && !wasip2 && !darwin && !baremetal +//go:build tinygo && linux && !wasip1 && !wasip2 && tinygo.wasm && !wasm_unknown && !darwin && !baremetal && !nintendoswitch package syscall -// This is the original ForkLock: -// -// var ForkLock sync.RWMutex -// -// This requires importing sync, but importing sync causes an import loop: -// -// package tinygo.org/x/drivers/examples/net/tcpclient -// imports bytes -// imports io -// imports sync -// imports internal/task -// imports runtime/interrupt -// imports device/arm -// imports syscall -// imports sync: import cycle not allowed -// -// So for now, make our own stubbed-out ForkLock that doesn't use sync.. +import ( + "sync" +) -type forklock struct{} - -func (f forklock) RLock() {} -func (f forklock) RUnlock() {} - -var ForkLock forklock +var ForkLock sync.RWMutex func CloseOnExec(fd int) { system.CloseOnExec(fd) @@ -34,5 +15,3 @@ func CloseOnExec(fd int) { func SetNonblock(fd int, nonblocking bool) (err error) { return system.SetNonblock(fd, nonblocking) } - -// type SysProcAttr struct{} From da0f694df53bf1a0c7e43c85de6724d75928d412 Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Sun, 17 Nov 2024 10:04:03 +0100 Subject: [PATCH 13/14] wip Signed-off-by: leongross <leon.gross@9elements.com> --- src/runtime/sync.go | 73 +++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/src/runtime/sync.go b/src/runtime/sync.go index 3d302ec7d2..83ddcab064 100644 --- a/src/runtime/sync.go +++ b/src/runtime/sync.go @@ -5,8 +5,9 @@ package runtime import ( + "internal/task" + "sync" "sync/atomic" - "unsafe" ) // This file contains stub implementations for internal/poll. @@ -24,22 +25,35 @@ import ( // This means we assume the following constant settings from the golang standard // library: lifo=false,profile=semaBlock,skipframe=0,reason=waitReasonSemaquire -type semaRoot struct { - nwait atomic.Uint32 +// The global state of the semaphore table. +// Semaphores are identified by their address. +// The table maps the address to the task that is currently holding the semaphore. +// The table is protected by a mutex. +// When a task acquires a semaphore, the mapping is added to the map. +// When a task releases a semaphore, the mapping is removed from the map. +// +// The table is used to implement the cansemacquire function. +// The cansemacquire function is called by the semacquire function. +// The cansemacquire function checks if the semaphore is available. +// If the semaphore is available, the function returns true. +// If the semaphore is not available, the function returns false. +type semTable struct { + table map[*uint32]*task.Task + lock sync.Mutex } var semtable semTable -// Prime to not correlate with any user patterns. -const semTabSize = 251 +func init() { + semtable.table = make(map[*uint32]*task.Task) +} -type semTable [semTabSize]struct { - root semaRoot - pad [64 - unsafe.Sizeof(semaRoot{})]byte // only 64 x86_64, make this variable +func (s *semTable) Lock() { + s.lock.Lock() } -func (t *semTable) rootFor(addr *uint32) *semaRoot { - return &t[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root +func (s *semTable) Unlock() { + s.lock.Unlock() } //go:linkname semacquire internal/poll.runtime_Semacquire @@ -51,22 +65,37 @@ func semacquire(sema *uint32) { // Copied from src/runtime/sema.go func cansemacquire(addr *uint32) bool { - for { - v := atomic.LoadUint32(addr) - if v == 0 { - return false - } - if atomic.CompareAndSwapUint32(addr, v, v-1) { - return true - } + // Busy Looping until a lookup to the global semaphore table can be made + semtable.Lock() + + if _, ok := semtable.table[addr]; !ok { + semtable.table[addr] = task.Current() + semtable.Unlock() + return true + } + + v := atomic.LoadUint32(addr) + if v == 0 { + semtable.Unlock() + return false + } + if atomic.CompareAndSwapUint32(addr, v, v-1) { + semtable.Unlock() + return true } + return true } //go:linkname semrelease internal/poll.runtime_Semrelease func semrelease(sema *uint32) { - root := semtable.rootFor(sema) - atomic.AddUint32(sema, 1) - if root.nwait.Load() == 0 { - return + // Check if the semaphore is in the table + semtable.Lock() + if _, ok := semtable.table[sema]; !ok { + panic("invalid semaphore") } + + atomic.AddUint32(sema, 1) + semtable.Unlock() + + Gosched() } From c16ff80f46f24e7545ecb8eb7a0a0c98670ed9ab Mon Sep 17 00:00:00 2001 From: leongross <leon.gross@9elements.com> Date: Fri, 29 Nov 2024 13:26:10 +0100 Subject: [PATCH 14/14] implement semrealse and semacquire using futex Signed-off-by: leongross <leon.gross@9elements.com> --- src/runtime/sync.go | 81 +++++++++------------------------------------ 1 file changed, 16 insertions(+), 65 deletions(-) diff --git a/src/runtime/sync.go b/src/runtime/sync.go index 83ddcab064..bf0499fe9e 100644 --- a/src/runtime/sync.go +++ b/src/runtime/sync.go @@ -5,9 +5,7 @@ package runtime import ( - "internal/task" - "sync" - "sync/atomic" + "internal/futex" ) // This file contains stub implementations for internal/poll. @@ -25,77 +23,30 @@ import ( // This means we assume the following constant settings from the golang standard // library: lifo=false,profile=semaBlock,skipframe=0,reason=waitReasonSemaquire -// The global state of the semaphore table. -// Semaphores are identified by their address. -// The table maps the address to the task that is currently holding the semaphore. -// The table is protected by a mutex. -// When a task acquires a semaphore, the mapping is added to the map. -// When a task releases a semaphore, the mapping is removed from the map. -// -// The table is used to implement the cansemacquire function. -// The cansemacquire function is called by the semacquire function. -// The cansemacquire function checks if the semaphore is available. -// If the semaphore is available, the function returns true. -// If the semaphore is not available, the function returns false. -type semTable struct { - table map[*uint32]*task.Task - lock sync.Mutex -} - -var semtable semTable - -func init() { - semtable.table = make(map[*uint32]*task.Task) -} - -func (s *semTable) Lock() { - s.lock.Lock() -} - -func (s *semTable) Unlock() { - s.lock.Unlock() -} - //go:linkname semacquire internal/poll.runtime_Semacquire func semacquire(sema *uint32) { - if cansemacquire(sema) { - return - } -} - -// Copied from src/runtime/sema.go -func cansemacquire(addr *uint32) bool { - // Busy Looping until a lookup to the global semaphore table can be made - semtable.Lock() + var semaBlock futex.Futex + semaBlock.Store(*sema) - if _, ok := semtable.table[addr]; !ok { - semtable.table[addr] = task.Current() - semtable.Unlock() - return true - } + // check if we can acquire the semaphore + semaBlock.Wait(1) - v := atomic.LoadUint32(addr) - if v == 0 { - semtable.Unlock() - return false - } - if atomic.CompareAndSwapUint32(addr, v, v-1) { - semtable.Unlock() - return true + // the semaphore is free to use so we can acquire it + if semaBlock.Swap(0) != 1 { + panic("semaphore is already acquired, racy") } - return true } //go:linkname semrelease internal/poll.runtime_Semrelease func semrelease(sema *uint32) { - // Check if the semaphore is in the table - semtable.Lock() - if _, ok := semtable.table[sema]; !ok { - panic("invalid semaphore") - } + var semaBlock futex.Futex + semaBlock.Store(*sema) - atomic.AddUint32(sema, 1) - semtable.Unlock() + // check if we can release the semaphore + if semaBlock.Swap(1) != 0 { + panic("semaphore is not acquired, racy") + } - Gosched() + // wake up the next waiter + semaBlock.Wake() }