A subtle parsing bug in Go’s standard library — specifically in the math/big package’s handling of rational numbers — could be weaponized to crash processes and deny service: inputs with excessively large exponents passed to (big.Rat).SetString or (big.Rat).UnmarshalText may trigger a panic or unrecoverable fatal error in affected Go releases before 1.15.13 and 1.16.x before 1.16.5.
The vulnerability, tracked as CVE-2021-33198 and cataloged in ecosystem trackers as GO-2021-0242, was discovered by the OSS‑Fuzz project and reported to the Go team by Emmanuel Odeke. It affects the standard-library routines that parse textual rational numbers into math/big.Rat values. Because math/big implements arbitrary-precision arithmetic, parsing textual numeric representations must carefully bound intermediate size computations; this issue failed to do so for extreme exponent values and allowed internal calculations to overflow or request uncontrolled allocations.
Go maintainers shipped the fix as part of their routine minor releases: Go 1.15.13 and Go 1.16.5 include the targeted change to prevent excessive exponent handling in (*Rat).SetString. The project’s release announcement explicitly calls out the issue and credits OSS‑Fuzz and the reporter. (groups.google.com)
Vendor and distributor advisories uniformly treated the impact as primarily an availability (DoS) risk: an unauthenticated remote attacker who can supply crafted numeric strings to a vulnerable parsing call can repeatedly crash or exhaust a service. Typical scoring in vulnerability trackers was high for availability (for example CVSS-like assessments centered on AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).
The Go patch introduces bounds checks and explicit validation: before proceeding with allocations it validates intermediate values, refuses to accept sizes that would wrap, and returns errors for inputs that would require impractically large resources. This targeted fix prevents the specific overflow/unchecked allocation path without changing the public API semantics for normal inputs. The patch and commit metadata are public in the Go repository. (go.googlesource.com)
However, two practical constraints moderate real‑world exploitation likelihood:
Source: MSRC Security Update Guide - Microsoft Security Response Center
Background / Overview
The vulnerability, tracked as CVE-2021-33198 and cataloged in ecosystem trackers as GO-2021-0242, was discovered by the OSS‑Fuzz project and reported to the Go team by Emmanuel Odeke. It affects the standard-library routines that parse textual rational numbers into math/big.Rat values. Because math/big implements arbitrary-precision arithmetic, parsing textual numeric representations must carefully bound intermediate size computations; this issue failed to do so for extreme exponent values and allowed internal calculations to overflow or request uncontrolled allocations.Go maintainers shipped the fix as part of their routine minor releases: Go 1.15.13 and Go 1.16.5 include the targeted change to prevent excessive exponent handling in (*Rat).SetString. The project’s release announcement explicitly calls out the issue and credits OSS‑Fuzz and the reporter. (groups.google.com)
Why this matters: availability, parsing, and the surprise of big numbers
Modern services routinely accept and manipulate numeric input — APIs, telemetry, serialization formats, configuration parsers, and data ingestion pipelines all parse numbers from text. Arbitrary-precision types like big.Int and big.Rat are convenient for correctness, but they shift the safety boundary: unbounded inputs can require unbounded allocations unless the parsing code defends against worst-case sizes. The SetString/UnmarshalText bug exposed exactly this risk: a crafted numeric text with a very large exponent can force the library down a path where intermediate size estimates overflow or escalate, producing attempts to allocate extremely large internal buffers and ultimately causing a panic or fatal error that takes a process offline.Vendor and distributor advisories uniformly treated the impact as primarily an availability (DoS) risk: an unauthenticated remote attacker who can supply crafted numeric strings to a vulnerable parsing call can repeatedly crash or exhaust a service. Typical scoring in vulnerability trackers was high for availability (for example CVSS-like assessments centered on AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).
Technical anatomy — what went wrong inside math/big
At a high level, parsing a textual integer or rational requires converting digit counts into machine-word allocations (limbs) that hold the multi-precision representation. The vulnerable implementation computed required sizes from digit counts and then performed arithmetic shifts and multiplications to turn digit estimates into limb counts. Under crafted inputs that push digit counts and exponent ranges to extremes, those intermediate arithmetic operations could overflow the integer type used to hold a size estimate. The truncated or wrapped result would then be used downstream to allocate buffers or index into memory, producing either oversized allocation attempts or out-of-bounds behavior that ends with a panic or abort.The Go patch introduces bounds checks and explicit validation: before proceeding with allocations it validates intermediate values, refuses to accept sizes that would wrap, and returns errors for inputs that would require impractically large resources. This targeted fix prevents the specific overflow/unchecked allocation path without changing the public API semantics for normal inputs. The patch and commit metadata are public in the Go repository. (go.googlesource.com)
Discovery and disclosure: fuzzing + responsible reporting
OSS‑Fuzz — Google’s continuous fuzzing service — discovered the faulty behavior, and the issue was responsibly reported and triaged by the Go team and community. The quick identification and focused remediation illustrate two important lessons: fuzzing is highly effective at finding edge-case parsing bugs in widely used runtimes, and responsible disclosure with coordinated fixes reduces the window of exposure. The Go project credited OSS‑Fuzz and Emmanuel Odeke in the release notes and in the change record. (go.googlesource.com)Who and what is affected
- Go toolchains and runtimes: Any installation of Go in the affected release ranges (pre-1.15.13 for the 1.15 line, and 1.16.x prior to 1.16.5) is vulnerable when parsing untrusted numeric text using math/big.Rat SetString or UnmarshalText.
- Compiled binaries: Because Go binaries are frequently statically linked and include the standard library, a binary built with a vulnerable toolchain can harbor the vulnerable code even when deployed on a host with a patched Go package. Operators must therefore treat build-time toolchain versions as first-class security artifacts.
- Downstream products and containers: Many third‑party utilities, telemetry agents, or container images include Go components; vendor advisories and OS package trackers published backports or upgraded packages. Distributions such as Debian, Ubuntu, Red Hat, and others tracked the CVE and released patches.
Exploitability: practical attack paths and constraints
The core exploit is straightforward where the attack surface exists: an attacker sends a textual numeric value (for example, a rational with a very large exponent) to an endpoint that eventually calls (*big.Rat).SetString or UnmarshalText on attacker‑controlled input. The malformed input causes unchecked intermediate calculations to overflow or request massive memory, triggering a process panic or fatal error. Because the attack requires only network access and no privileges, its attack complexity is low when the vulnerable call is reachable.However, two practical constraints moderate real‑world exploitation likelihood:
- The vulnerable call must be reachable from an untrusted input surface; not all applications expose such parsing directly. Many safe usages are internal and bounded.
- Although the conceptual exploit is simple, crafting an input that reliably triggers excessive allocation depends on the exact parsing paths and string forms; operators should not assume the absence of published PoCs equals safety. Several vendor advisories noted no mass-exploitation telemetry but still treated the issue as operationally urgent.
Short-term mitigations (what to do immediately if you cannot upgrade)
- Inventory and triage: identify systems that run Go toolchains in the affected ranges and binaries that were built with those toolchains. Search for embedded version strings (for example using go version -m on executables built with module support) and scan container images. Treat build hosts, CI runners, and deployed static binaries as equally important.
- Block or sanitize inputs: where possible, add input validation at the edge. Reject numeric strings with extremely large exponents or unusually long numerator/denominator fields before they reach math/big parsing calls. Implement sanity checks on digit counts and exponent magnitude. This is a pragmatic stopgap but not a substitute for updating the runtime.
- Memory/process limits and sandboxing: enforce per-request memory caps using container limits, ulimit, cgroups, or job supervisors, so a single parse cannot exhaust host RAM. Consider running parsing in a supervised subprocess or sandboxed helper with strict resource quotas and restart handling to contain crashes.
- Rate‑limit and circuit-break: if the parsing endpoint is externally reachable, apply rate-limiting and circuit-breakers to reduce the impact of repeated attempts to trigger the failure. Monitor for anomalous allocation patterns and set alerts.
The correct fix: upgrade and rebuild
- Upgrade the Go toolchain on developer and build hosts to at least Go 1.15.13 for the 1.15 line or Go 1.16.5 (and later) for the 1.16 line. The Go project’s minor releases carry the math/big patch and are documented in the announce post. (groups.google.com)
- Rebuild any static binaries that were compiled with a vulnerable toolchain. Because the Go standard library is embedded into many statically linked executables, simply patching the host’s golang package is insufficient for artifacts already built and deployed. Confirm fixed status by inspecting binary build metadata or by rebuilding and redeploying.
- Apply distribution vendor updates for packaged golang where applicable. Operating systems and packaging vendors produced backports and advisories; use your platform’s package manager to install vendor-supplied fixes.
- Steps to remediate:
- Inventory build hosts and deployed binaries to find occurrences of vulnerable toolchains.
- Upgrade toolchains and CI images to patched releases. (groups.google.com)
- Rebuild and redeploy applications that embed the standard library.
- Validate with regression tests, including extreme numeric cases to ensure the fix prevents large allocations.
Detection and post‑remediation verification
- Dynamic checks: run fuzz-based or high-boundary parsing tests that feed extremely large exponent strings to your application in staging. Confirm memory usage remains bounded and the process does not panic. This mirrors how OSS‑Fuzz discovered the flaw and offers practical assurance. (go.googlesource.com)
- Static and package scans: use SCA and OS vulnerability scanners to detect vulnerable Go packages in images, and search codebases for direct calls to (*big.Rat).SetString or UnmarshalText. Add detection rules in CI to prevent pushing artifacts with vulnerable build metadata.
- Audit logs and observability: instrument memory and allocation metrics, set alerts for rapid memory growth, and track process restarts that may indicate exploitation attempts. Add logging around parsing endpoints to detect suspiciously large numeric tokens.
Supply-chain and long-term hardening: what organizations must change
This CVE underscores a recurring truth in modern software security: the build toolchain is part of your attack surface. Organizations should:- Treat the Go toolchain and CI environment as critical production dependencies. Pin toolchain versions, record SBOM-style metadata for built artifacts, and require rebuilds when the toolchain is upgraded for security reasons.
- Add fuzz testing, boundary testing, and static analysis to CI for parsing code, especially for types that can grow without bound such as big.Int and big.Rat. Continuous fuzzing would likely have caught similar parsing issues earlier in many codebases. (go.googlesource.com)
- Limit direct exposure of arbitrary-precision parsing APIs to untrusted inputs. Prefer explicit, limited-range parsing routines for network- and API-facing endpoints. Where arbitrary precision is necessary, apply strict upper bounds on digits and exponent ranges.
- Build a rebuild-and-rollback plan that includes detecting binaries compiled with vulnerable toolchains, scheduling rebuilds, and redeploying in a controlled manner to close windows where patched packages alone cannot help.
Vendor response and timeline (concise)
- Discovery: OSS‑Fuzz flagged the issue; the report was credited to Emmanuel Odeke. (go.googlesource.com)
- Fix: The Go change list and commit to math/big prevent excessive exponents and validate intermediate sizes; the commit is public in the Go repo. (go.googlesource.com)
- Releases: The Go team issued minor releases Go 1.15.13 and Go 1.16.5 that include the fix; these releases were announced in the official golang-announce posts. (groups.google.com)
- Downstream: OS and distro maintainers published advisories and patches; operators should rely on vendor update channels or rebuild static binaries as needed.
Practical checklist for operators and developers (action-oriented)
- Inventory:
- List build hosts and CI images; note Go versions used.
- Scan deployed binaries for embedded Go version strings and module metadata.
- Immediate fixes:
- If you run affected toolchain versions, upgrade to Go 1.15.13 / 1.16.5 or later. (groups.google.com)
- Rebuild static binaries compiled with vulnerable toolchains.
- Apply vendor-supplied OS package updates for packaged golang.
- Short-term mitigations while rebuilding:
- Add edge input validation to reject extreme numeric strings.
- Enforce memory limits and run risky parsers in sandboxed subprocesses.
- Rate-limit parsing endpoints and set allocation-growth alerts.
- Verification:
- Run regression and fuzz tests covering large exponents; confirm no panic or runaway allocation. (go.googlesource.com)
- Re-scan images and packages with vulnerability scanners to confirm the CVE is resolved.
Risks that remain and caveats
- Static binaries are the most persistent vector. A patched host OS package alone does not neutralize binaries already built with a vulnerable toolchain; rebuilds are essential.
- Not every service that uses math/big is exposed. Triage must confirm whether a vulnerable call is reachable from untrusted inputs. Blind patching without inventory is insufficient.
- Public exploit telemetry was limited at disclosure time, but the conceptual exploit is trivial to construct where the call path exists; do not assume safety due to lack of PoCs.
- The underlying class of bug — failing to bound intermediate arithmetic used for allocation sizing — can appear in other parsers and libraries; broad code review for similar patterns is prudent.
Conclusion
CVE‑2021‑33198 is a textbook example of how correctness features (arbitrary-precision numeric types) plus insufficient defensive coding can become an availability risk. The vulnerability was found by automated fuzzing, patched quickly by the Go project, and distributed via point releases; nevertheless, the operational work of inventorying affected toolchains, rebuilding static artifacts, and applying vendor updates falls to operators and developers. Immediate action is clear: inventory, patch or rebuild, and add input validation and resource limits where necessary. In the longer term, treat build toolchains as part of your security posture, integrate fuzzing and boundary tests into CI, and avoid exposing unbounded parsing to untrusted inputs. The fix exists; the remaining job is operational follow-through. (go.googlesource.com)Source: MSRC Security Update Guide - Microsoft Security Response Center