Safeguard
Vulnerability Analysis•

Go 1.27.2 and 1.26.9: 15 Standard Library Fixes, and How to Find Binaries That Still Need a Rebuild

Go 1.27.2 and 1.26.9 fix 15 CVEs, eight in net/http. Fixes only apply after a rebuild. What changed, the x/net v0.60.0 half, and a binary scanner.

Safeguard Security Team
Security Research
11 min read

On 8 October, Go 1.27.2 and 1.26.9 fixed 15 vulnerabilities in the standard library and the go command. Eight are in net/http, including two ways for a client to crash or exhaust an HTTP/2 server. Updating Go on your laptop fixes none of them in production. A Go binary carries the standard library it was built with, so every service, CLI and sidecar stays affected until someone rebuilds it with a fixed toolchain.

This guide is for teams that build or run Go software. It covers what the release fixes, which bugs matter for which programs, the five HTTP/2 bugs that also need a golang.org/x/net update, and a small scanner that reads build information out of binaries, so you can find what still needs a rebuild. We ran it on our own workstation: none of the 12 third-party Go tools installed there had been rebuilt.

The short answer

Go 1.27.2 and 1.26.9 (8 October 2026) fix 15 vulnerabilities: eight in net/http (five of them HTTP/2, plus HTTP/1 CONNECT handling and Range parsing), two in html/template, two checksum bypasses in the go command, and one each in net/textproto, crypto/tls and os on Windows. Standard library fixes only take effect when a binary is rebuilt, so rebuild every Go program with 1.27.2 or 1.26.9, starting with network servers. Programs that import golang.org/x/net/http2 directly also need golang.org/x/net v0.60.0. Go 1.25 and older get no fixes. Use go version -m or the scanner below to find binaries built with an older toolchain.

What the release fixes

AreaCVEWhat goes wrongReported by
HTTP/2 serverCVE-2026-97032A race in the HPACK encoder lets a client crash the server by changing the header table size while requests are in flightRyotaK (GMO Flatt Security)
HTTP/2 serverCVE-2026-78659A Trailer header declaring many fields makes the server allocate memory past MaxHeaderBytesRyotaK (GMO Flatt Security)
HTTP/2 serverCVE-2026-78669Repeated initial window size changes consume excessive CPUJakub Ciolek
HTTP/2 serverCVE-2026-78663Flow control credit refunded twice on server streamsAli Sherif
HTTP/2 clientCVE-2026-78660Transport accepts malformed framing-related headersTJ Barton
HTTP/1 serverCVE-2026-94439After a 2xx response to CONNECT, the server keeps reading HTTP from what should be a tunnel, which can enable request smugglingJakub Ciolek
HTTP/1 clientCVE-2026-56866A rejected CONNECT with a body leaves a pooled connection out of sync, so the next caller can read a response meant for an injected requestRajat Raghav
HTTPCVE-2026-78667Range headers with many small ranges burn CPU in FileServer, ServeContent and ServeFileJakub Ciolek
MIMECVE-2026-94440Multipart parsing can read an arbitrarily long line past its memory limitJakub Ciolek
TLSCVE-2026-97031Malformed ECH outer extension references cause memory amplification on serversNone listed
TemplatesCVE-2026-94448, CVE-2026-97030Escaping errors in JavaScript contexts: consecutive template literal expressions, and the yield keywordNone listed
go commandCVE-2026-94444, CVE-2026-94447A malicious project plus a malicious module proxy could bypass checksums for golang.org/fips140 and golang.org/toolchainNone listed
osCVE-2026-56857On Windows, Root.Mkdir and MkdirAll can follow a junction out of the rootDaniele Ballarini

The HTTP/2 server bugs carry the most risk for most teams, because any client that can open a connection can trigger them. The Go HTTP server enables HTTP/2 automatically on TLS, so a service using ListenAndServeTLS or a TLS listener is exposed without doing anything special. Cleartext HTTP/2 is only on if you've enabled it, through Server.Protocols or the h2c package.

The two go command bugs are different in kind. They affect developer machines and CI, not running programs, and they need you to build a hostile project while using a module proxy the attacker controls. Updating the toolchain in CI images closes them.

Why updating Go isn't enough

Go links the standard library into each binary. A service built in September with Go 1.27.1 contains the September net/http, and it keeps that code until it's rebuilt, whatever is installed on the machine that runs it. The same goes for container images, release binaries downloaded from GitHub, and tools installed through a package manager.

Two details make this easy to miss:

  • Dependency scanners don't see it. Lockfiles list modules, not the toolchain. A scanner reading go.sum can miss standard library CVEs entirely unless it also reads the go or toolchain line, and that line is a minimum, not what built the binary.
  • The go directive isn't the build version. go 1.27 in go.mod says what the code needs. Which release built the binary depends on the build machine or image, which is often pinned and forgotten.

Go writes the toolchain version into every binary it builds, though, and go version -m ./binary prints it along with the module list. That's what the scanner below automates.

The x/net half of the HTTP/2 fixes

Five of the HTTP/2 bugs (CVE-2026-97032, 78659, 78669, 78663 and 78660) also exist in golang.org/x/net/http2, the standalone package that the standard library's copy comes from. The fixed version is golang.org/x/net v0.60.0, published the same day. If your code imports golang.org/x/net/http2 or h2c directly, as gRPC-Go and many proxies do, you need both the new toolchain and the module update. If you only use net/http, the toolchain is enough.

Find binaries that need a rebuild

This program walks directories, reads the build information Go embeds in each binary, and checks the toolchain against the fixed releases. It also looks for function names that show whether HTTP server, HTTP/2, TLS server or template code is linked in. Go keeps function names in the binary even when the symbol table is stripped, so stripped builds work too. It flags golang.org/x/net below v0.60.0 when x/net/http2 is linked. It uses only the standard library:

// gobinscan finds Go binaries that need a rebuild for the 8 October 2026 Go
// security release (go1.27.2 / go1.26.9) or a golang.org/x/net bump (v0.60.0).
//
// Usage: go run . <dir-or-file>...   (a directory is walked recursively)
package main

import (
	"bytes"
	"debug/buildinfo"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

// Function names stay in the binary's pclntab even when the symbol table is stripped.
var markers = map[string][]string{
	"http-server": {"net/http.(*conn).serve"},
	"http2":       {"net/http/internal/http2.", "net/http.(*http2serverConn)", "golang.org/x/net/http2."},
	"x/net-http2": {"golang.org/x/net/http2."},
	"template":    {"html/template.(*Template).Execute"},
	"tls":         {"crypto/tls.(*Conn).serverHandshake"},
}

// stdlibStatus compares a toolchain version like "go1.27.1" with the fixed releases.
func stdlibStatus(v string) string {
	v = strings.Fields(v)[0]
	parts := strings.Split(strings.TrimPrefix(v, "go"), ".")
	if len(parts) < 2 {
		return "unknown toolchain " + v
	}
	minor, _ := strconv.Atoi(parts[1])
	patch := 0
	if len(parts) > 2 {
		patch, _ = strconv.Atoi(strings.SplitN(parts[2], "-", 2)[0])
	}
	switch {
	case minor > 27:
		return "ok"
	case minor == 27 && patch >= 2, minor == 26 && patch >= 9:
		return "ok"
	case minor == 27, minor == 26:
		return "REBUILD with go1.27.2 or go1.26.9"
	default:
		return "REBUILD: go1." + parts[1] + " gets no security fixes"
	}
}

// older reports whether a v0.x.y module version is below v0.<major>.<minor>.0.
func older(v string, major, minor int) bool {
	p := strings.Split(strings.TrimPrefix(v, "v"), ".")
	if len(p) < 2 {
		return false
	}
	a, _ := strconv.Atoi(p[0])
	b, _ := strconv.Atoi(p[1])
	return a < major || (a == major && b < minor)
}

func has(data []byte, keys []string) bool {
	for _, k := range keys {
		if bytes.Contains(data, []byte(k)) {
			return true
		}
	}
	return false
}

func scan(path string) {
	info, err := buildinfo.ReadFile(path)
	if err != nil {
		return // not a Go binary
	}
	data, err := os.ReadFile(path)
	if err != nil {
		return
	}
	var uses []string
	for _, k := range []string{"http-server", "http2", "template", "tls"} {
		if has(data, markers[k]) {
			uses = append(uses, k)
		}
	}
	status := stdlibStatus(info.GoVersion)
	if strings.HasPrefix(status, "REBUILD") && len(uses) == 0 {
		status += " (low priority: no HTTP, TLS server or template code found)"
	}
	fmt.Printf("%s\n  %s, main %s, code present: %s\n  stdlib: %s\n", path, info.GoVersion,
		info.Main.Path, strings.Join(uses, " "), status)
	for _, d := range info.Deps {
		if d.Replace != nil {
			d = d.Replace
		}
		if d.Path == "golang.org/x/net" && older(d.Version, 0, 60) && has(data, markers["x/net-http2"]) {
			fmt.Printf("  x/net: %s imports x/net/http2 directly, UPDATE to v0.60.0\n", d.Version)
		}
	}
}

func main() {
	for _, root := range os.Args[1:] {
		filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
			if err == nil && d.Type().IsRegular() {
				scan(p)
			}
			return nil
		})
	}
}

We built four test binaries with Go 1.27.1, which is affected, and with GOTOOLCHAIN=local so nothing else was downloaded: a web server using net/http and html/template, the same server built with -ldflags="-s -w", a small CLI, and an h2c server using golang.org/x/net v0.59.0. The folder also held a non-Go binary, which the scanner skips:

$ go run . ../fx/bin
../fx/bin/cli
  go1.27.1, main example.com/cli, code present: 
  stdlib: REBUILD with go1.27.2 or go1.26.9 (low priority: no HTTP, TLS server or template code found)
../fx/bin/h2srv
  go1.27.1, main example.com/h2srv, code present: http-server http2
  stdlib: REBUILD with go1.27.2 or go1.26.9
  x/net: v0.59.0 imports x/net/http2 directly, UPDATE to v0.60.0
../fx/bin/websrv
  go1.27.1, main example.com/websrv, code present: http-server http2 template tls
  stdlib: REBUILD with go1.27.2 or go1.26.9
../fx/bin/websrv-stripped
  go1.27.1, main example.com/websrv, code present: http-server http2 template tls
  stdlib: REBUILD with go1.27.2 or go1.26.9

"Code present" means the code is linked in, not that it's reachable. The web server contains HTTP/2 server code but serves plain HTTP/1, so the HTTP/2 bugs can't be triggered against it as written. It still needs a rebuild, and the scanner can't know how you'll deploy it. For a precise answer per binary, govulncheck -mode=binary checks which vulnerable functions are actually called.

Then we ran it, read-only, over the Homebrew and /usr/local/bin directories on one of our workstations. Leaving out the Go toolchain itself, it found 12 Go binaries from 12 different packages: five built with Go 1.27.1, six with 1.26.5 or 1.26.6, and one with Go 1.25.10, which no longer gets security fixes. None had been built with 1.27.2 or 1.26.9. Eight contained HTTP server code and nine linked x/net/http2 below v0.60.0. Most are command-line tools where that code serves a local callback or is never used, so the risk on a laptop is low. But the same pattern on a server image is not.

What to do

  1. Update build images and CI to Go 1.27.2 or 1.26.9. This also fixes the two go command bugs on build machines.
  2. Rebuild and redeploy Go services, starting with anything that terminates HTTP/2 or TLS from untrusted clients.
  3. Bump golang.org/x/net to v0.60.0 in modules that import x/net/http2 or h2c directly.
  4. Move off Go 1.25 and older. Only the two newest major releases get security fixes.
  5. Scan what you ship, not just what you build: container images, downloaded release binaries and vendor tools. Run the scanner over an extracted image filesystem, or use go version -m.
  6. Ask vendors who ship Go binaries when they'll rebuild, and check the answer with the same tool.

For the long view, our Go standard library CVE trend report covers how often these releases come, and rebuilding Docker images covers making rebuilds routine rather than an event.

The fair view

The Go team's process is a model here. Fixes ship to both supported releases on the same day, each advisory goes into the Go vulnerability database with the affected packages and versions, and the HTTP/2 fixes land in x/net simultaneously. The build information that makes this audit possible is there because Go chose to embed it in every binary by default.

The cost is the other side of static linking. One go.mod change fixes a dependency for everyone who updates, but a standard library fix only reaches users through a rebuild of every program, by every publisher. Go releases these security updates roughly monthly. Teams that rebuild on a schedule absorb them without noticing. Teams that rebuild only when code changes can run a September net/http for a year.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.