Skip to content

TLS 1.3 panic with OpenSSL >= 3.5.6: OSSL_PARAM_BLD_push_octet_string rejects NULL buf #2221

@Daniel-Wachter

Description

@Daniel-Wachter

Summary

OpenSSL 3.5.6 (released April 7, 2026) added NULL checks to OSSL_PARAM_BLD_push_octet_string (openssl/openssl@d5ad0b8). This causes a panic in TLS 1.3 handshakes when using GOEXPERIMENT=systemcrypto, because addOctetString in the vendored golang-fips/openssl/v2/params.go passes a NULL pointer for empty/nil Go slices (e.g. the empty context in TLS 1.3 ExpandLabel).

The upstream golang-fips/openssl v2 branch already has a fix for this — replacing nil slices with []byte{} before calling OSSL_PARAM_BLD_push_octet_string. However, this fix has not been picked up in the microsoft/go vendored copy yet.

Reproduction

Build any Go FIPS binary on a system with OpenSSL >= 3.5.6 (or 3.6.2):

GOEXPERIMENT=systemcrypto CGO_ENABLED=1 go run main.go

Where main.go makes any HTTPS request:

package main

import (
    "log"
    "net/http"
)

func main() {
    resp, err := http.Get("https://www.google.com")
    if err != nil {
        panic(err)
    }
    log.Printf("Status: %d\n", resp.StatusCode)
}

Panic

panic: failed to add parameter data: OSSL_PARAM_BLD_push_octet_string
    openssl error(s):
    40D77BADA5710000:error:078C0102:common libcrypto routines:OSSL_PARAM_BLD_push_octet_string:passed a null parameter:crypto/param_build.c:369:

goroutine 55 [running]:
crypto/tls/internal/tls13.ExpandLabel[...](... {0x0, 0x0, 0x0}, 0x10)
    /usr/lib/go/src/crypto/tls/internal/tls13/tls13.go:40 +0x3e5
crypto/tls.(*cipherSuiteTLS13).trafficKey(...)
    /usr/lib/go/src/crypto/tls/key_schedule.go:29 +0x8e
...

The {0x0, 0x0, 0x0} is the nil context slice passed to ExpandLabel during TLS 1.3 key derivation. The vendored pbase() converts this to a NULL unsafe.Pointer, which OpenSSL 3.5.6 now rejects.

Root cause

In src/vendor/github.com/golang-fips/openssl/v2/params.go:

func (b *paramBuilder) addOctetString(name cString, value []byte) {
    if !b.check() {
        return
    }
    if len(value) != 0 {
        b.pinner.Pin(&value[0])
    }
    // pbase(value) returns NULL when len(value) == 0
    if _, err := ossl.OSSL_PARAM_BLD_push_octet_string(b.bld, name.ptr(), pbase(value), len(value)); err != nil {
        b.err = addParamError{name.str(), err}
    }
}

Fix

The upstream golang-fips/openssl v2 branch already has the fix — passing a non-nil empty slice instead of nil:

https://github.com/golang-fips/openssl/blob/v2/params.go

This needs to be synced into the vendored copy in microsoft/go.

Affected versions

  • microsoft/go v1.26.2-1 (confirmed)
  • microsoft/go v1.25.8-1 (same vulnerable code path)
  • Any microsoft/go binary running against OpenSSL >= 3.5.6

Related

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions