Back to Blog
high SEVERITY8 min read

How Denial of Service via Infinite Loop happens in Go and how to fix it

CVE-2026-56852 is a high-severity Denial of Service vulnerability in the `golang.org/x/text` package where a `norm.Iter` iterator can enter an infinite loop when processing certain invalid UTF-8 input sequences. Applications using `golang.org/x/text` v0.37.0 or earlier that accept untrusted text input are at risk of complete service disruption. The fix is a one-line dependency bump in `go.mod` from v0.37.0 to v0.39.0.

O
By Orbis AppSec
•Published August 10, 2026•Reviewed August 10, 2026

Answer Summary

CVE-2026-56852 is a Denial of Service vulnerability (CWE-835: Loop with Unreachable Exit Condition) in the Go package `golang.org/x/text`. A `norm.Iter` iterator enters an infinite loop when it encounters specially crafted invalid UTF-8 byte sequences, allowing an attacker to permanently hang any goroutine processing that input. The fix is to upgrade `golang.org/x/text` from v0.37.0 to v0.39.0 in `go.mod` and `go.sum`, which corrects the iterator's handling of malformed Unicode data so it always terminates.

Vulnerability at a Glance

cweCWE-835 (Loop with Unreachable Exit Condition)
fixUpgrade golang.org/x/text from v0.37.0 to v0.39.0 in go.mod and go.sum
riskAttacker-controlled input can permanently hang a goroutine, exhausting server resources
languageGo
root causenorm.Iter in golang.org/x/text ≤0.37.0 fails to advance past certain invalid UTF-8 byte sequences, looping forever
vulnerabilityDenial of Service via Infinite Loop (norm.Iter on invalid UTF-8)

How Denial of Service via Infinite Loop Happens in Go and How to Fix It

The Vulnerability at a Glance

Field Detail
CVE CVE-2026-56852
Severity High
Package golang.org/x/text
Affected versions ≤ v0.37.0
Fixed version v0.39.0
CWE CWE-835: Loop with Unreachable Exit Condition
Impact Denial of Service (infinite CPU loop)

Introduction

The go.mod file in this repository declared a dependency on golang.org/x/text v0.37.0 — a widely used Go package for Unicode text processing, normalization, and encoding. Buried inside that package's normalization subsystem is a flaw in the norm.Iter iterator: when it encounters certain sequences of invalid UTF-8 bytes, it enters an infinite loop with no reachable exit condition. Any goroutine calling into that iterator with attacker-controlled input will spin forever at 100% CPU, never returning, and never releasing its resources.

For developers building APIs, CLIs, or data pipelines in Go that accept text from untrusted sources — user input, file uploads, network streams — this is a direct path to service unavailability. The Trivy scanner identified the vulnerable version pinned in go.mod and flagged it as likely exploitable.


The Vulnerability Explained

What is norm.Iter and why does it matter?

golang.org/x/text/unicode/norm provides Unicode normalization forms (NFC, NFD, NFKC, NFKD). The norm.Iter type is an iterator that walks through a byte slice, yielding normalized segments one at a time. It is used internally throughout the x/text ecosystem — in encoding transformers, language detection, collation, and anywhere the library needs to process text incrementally.

The iterator's core loop looks conceptually like this (simplified):

// Vulnerable pattern in norm.Iter (golang.org/x/text ≤ v0.37.0)
for !iter.Done() {
    segment := iter.Next() // <-- can return empty slice on invalid UTF-8
    process(segment)
}

The critical flaw: when iter.Next() encounters a specific class of invalid UTF-8 byte sequence, it returns an empty segment without advancing the internal byte-position cursor. The loop condition !iter.Done() remains true because the cursor hasn't moved past the bad bytes, and iter.Next() keeps returning empty without progressing. The loop has no reachable exit — it runs forever.

The vulnerable dependency declaration

The problem was pinned directly in go.mod:

// go.mod — BEFORE (vulnerable)
golang.org/x/text v0.37.0

And confirmed by the corresponding hash in go.sum:

// go.sum — BEFORE (vulnerable)
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=

How an attacker exploits this

Consider an HTTP API endpoint that accepts a JSON body containing a name or description field, and somewhere in the processing pipeline that string is passed through a Unicode normalization step — perhaps for case-folding, collation, or sanitization:

// Example vulnerable processing path
import "golang.org/x/text/unicode/norm"

func processUserInput(input []byte) string {
    var iter norm.Iter
    iter.Init(norm.NFC, input)  // input comes from HTTP request body

    var result []byte
    for !iter.Done() {
        result = append(result, iter.Next()...)  // infinite loop if input is malformed UTF-8
    }
    return string(result)
}

An attacker sends a POST request with a carefully crafted body containing the triggering invalid UTF-8 sequence. The goroutine handling that request enters the infinite loop. It never returns. The HTTP server's goroutine pool fills up with stuck handlers. New requests cannot be served. The service becomes completely unavailable — a full Denial of Service achieved with a single malformed request.

Because Go's HTTP server spawns a goroutine per request, an attacker doesn't even need high request volume. One request with the right malformed bytes is enough to permanently consume a goroutine; a handful of such requests can exhaust the pool entirely.

Real-world impact for this application

This repository uses golang.org/x/text alongside a MongoDB driver, Kubernetes client libraries (k8s.io/api, k8s.io/apimachinery), and cryptographic utilities. Any code path that normalizes, encodes, or collates text from external sources — database content, API responses, user-submitted data — could trigger this loop if the data contains invalid UTF-8. Given the breadth of the dependency graph, the attack surface is non-trivial.


The Fix

The fix is a targeted dependency upgrade in exactly two files: go.mod and go.sum.

go.mod — before and after

# go.mod
- golang.org/x/text v0.37.0
+ golang.org/x/text v0.39.0

This single line change tells the Go toolchain to resolve and link against v0.39.0 instead of v0.37.0. The v0.39.0 release patches the norm.Iter iterator so that it always advances its internal cursor past invalid UTF-8 bytes — even when it cannot produce a valid normalized segment — ensuring the loop's exit condition is always eventually reachable.

go.sum — cryptographic verification updated

# go.sum
- golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
- golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+ golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
+ golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=

The go.sum file stores cryptographic hashes of every dependency module and its go.mod. Updating these hashes is mandatory — the Go toolchain will refuse to build if the hashes in go.sum don't match the downloaded module. This change ensures that the build is reproducibly pinned to the patched version and that no tampered intermediate version can be silently substituted.

Why only two files?

The fix is deliberately minimal. No application code changes are required because the bug lived entirely within the library's internal iterator logic. The public API surface of norm.Iter — Init(), Next(), Done() — is unchanged. Valid UTF-8 input continues to be processed identically. Only the handling of malformed input is tightened, and that tightening happens inside the library itself.


Key Takeaways

  • golang.org/x/text is not part of the Go standard library — it must be explicitly upgraded to receive security patches, and v0.37.0 is vulnerable to this infinite loop.
  • A single malformed HTTP request is sufficient to permanently hang a goroutine via the norm.Iter loop, making this a low-effort, high-impact DoS vector.
  • The go.sum hash update is not optional — both go.mod and go.sum must be updated together for the Go toolchain to accept and reproducibly build the patched version.
  • Unicode normalization routines are a non-obvious attack surface — any code path that collates, encodes, or normalizes user-supplied text may route through norm.Iter even if you never call it directly.
  • Trivy's dependency scanning caught this without requiring code-level analysis — pinning vulnerable versions in go.mod is itself a detectable, fixable security issue.

How Orbis AppSec Detected This

  • Source: Untrusted text input (e.g., HTTP request body, user-supplied string fields) passed into any function that internally calls norm.Iter.Next() within golang.org/x/text.
  • Sink: The norm.Iter.Next() call inside the normalization iterator loop in golang.org/x/text ≤ v0.37.0, reachable via any consumer of the unicode/norm package.
  • Missing control: The iterator lacked a guard to advance its cursor past invalid UTF-8 byte sequences, leaving the loop's exit condition permanently unsatisfiable on malformed input.
  • CWE: CWE-835 — Loop with Unreachable Exit Condition.
  • Fix: Upgraded golang.org/x/text from v0.37.0 to v0.39.0 in go.mod and updated the corresponding cryptographic hashes in go.sum.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-56852 is a sharp reminder that Denial of Service vulnerabilities don't always require sophisticated exploits. A handful of malformed bytes — specifically crafted invalid UTF-8 sequences — is all it takes to permanently hang a goroutine processing text with golang.org/x/text ≤ v0.37.0. The norm.Iter iterator's failure to advance past bad input creates a loop that can never exit, turning a routine text-processing call into a resource-exhaustion attack.

The fix is as minimal as it gets: two lines changed in go.mod, two lines updated in go.sum, and the vulnerability is closed. But finding it requires knowing to look — which is exactly what automated dependency scanning tools like Trivy and govulncheck are built to do. Make them a standard part of your Go CI pipeline, keep your x/ packages current, and treat your go.mod file as the security-critical artifact it truly is.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2033

Related Articles

high

js-yaml 4.3.1 Denial of Service: Malformed Input Hangs YAML Parser

A denial of service vulnerability in js-yaml versions 4.3.1 and earlier allows attackers to hang the YAML parser indefinitely by providing specially crafted malformed input. The fix, released in versions 4.3.2 and 3.15.2, patches the parsing logic to prevent unbounded processing. Upgrading is recommended for all applications parsing untrusted YAML data.

high

Express `app.get('*')` Wildcard Handler Path Traversal in watch.js

A first-party Express server's wildcard route handler used `req.url.indexOf('font.woff2')` to gate access to a font file, allowing attackers to bypass the substring check with crafted paths. The fix replaces the catch-all handler with explicit route registration.

critical

Updater.parseUpdate() CWE-494: Unsigned Metadata Download

The parseUpdate function in the Updater component extracted download URLs from remote server responses without cryptographic verification, enabling supply chain attacks via compromised or spoofed update servers. The fix adds strict URL validation requiring HTTPS and a trusted hostname before accepting any update metadata.

high

brace-expansion DoS: Exponential Backtracking in Nested Brace Patterns

A critical vulnerability in brace-expansion allows attackers to cause denial of service by submitting specially crafted patterns with nested braces. The exponential-time complexity in pattern expansion creates a computationally expensive path that can freeze applications processing user-controlled input.

high

CVE-2026-67213: nanoid customAlphabet Infinite Loop Fix

nanoid, a widely-used ID generator pulled in transitively through postcss and vitepress, had an infinite-loop bug in its `customAlphabet` code path before version 5.1.6. This PR pins the entire dependency tree to nanoid 5.1.16 via a pnpm override so no transitive consumer can resolve back to the vulnerable 3.3.16 release.

high

KNX Project Extractor ZIP Bomb: Unbounded Decompression Before Size

The KNX project extractor used `@zip.js/zip.js` to decompress .knxproj files without enforcing maximum entry sizes, total archive sizes, or compression ratios. This allowed attackers to upload ZIP bombs that expanded exponentially—like the famous 42.zip producing 4.5PB from 42KB—consuming all available memory before the existing `Checked` validation could trigger. The fix introduces three hard limits: 512MB per entry, 1GB total per archive, and a 100:1 compression ratio ceiling.