CVE-2026-53941: Uprobe gadgets Denial of Service Vulnerability
MODERATECVE-2026-53941 is a MODERATE denial of service vulnerability (CWE-400: Uncontrolled Resource Consumption) affecting Uprobe gadgets libraries in the Go ecosystem. The issue involves unprivileged container's ld.so.cache causes high cpu utilization and container startup dos. Remediation is available by upgrading affected packages to version 0.53.1.
CVE-2026-53941 at a Glance
How to Fix CVE-2026-53941
To remediate this vulnerability, upgrade all affected Uprobe gadgets dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade github.com/inspektor-gadget/inspektor-gadget to 0.53.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for github.com/inspektor-gadget/inspektor-gadget
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-53941, inspect your project dependencies for Uprobe gadgets:
inspect dependencies for github.com/inspektor-gadget/inspektor-gadget
Analyze the terminal output to locate any version of Uprobe gadgets in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.53.1), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-400: Uncontrolled Resource Consumption, where the product does not properly control the allocation and maintenance of a limited resource. In Uprobe gadgets, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-53941 Affected Uprobe gadgets Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
github.com/inspektor-gadget/inspektor-gadget
|
Go |
≥ 0.27.0
< 0.53.1
|
0.53.1
|
References
Frequently Asked Questions
What is CVE-2026-53941?
Which Uprobe gadgets versions are affected?
What version fixes CVE-2026-53941?
Is github.com/inspektor-gadget/inspektor-gadget affected?
What CWE is associated with CVE-2026-53941?
How can I check Go dependencies for CVE-2026-53941?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-vjhx-2cqw-3q6q",
"modified" : "2026-08-19T19:16:35Z",
"published" : "2026-08-19T19:16:35Z",
"aliases" : [ "CVE-2026-53941" ],
"summary" : "Uprobe gadgets: unprivileged container's ld.so.cache causes high CPU utilization and container startup DoS",
"details" : "## Summary\n\nAn unprivileged container can block all other containers from starting on the\nsame host by placing a crafted `/etc/ld.so.cache` file in its filesystem. When\nInspektor Gadget attaches any uprobe-based gadget, it parses this file in the\ncontainer startup path. A malicious cache causes ~53 seconds of CPU burn,\nduring which Docker cannot start any other container. No special capabilities\nare required.\n\n## Severity\n\nTo be assessed — Availability impact, no confidentiality or integrity impact.\n\n## Affected Versions\n\nAll versions of Inspektor Gadget that support uprobe-based gadgets (trace_malloc, trace_open, trace_ssl, trace_grpc, etc.).\n\n## Description\n\nWhen Inspektor Gadget attaches uprobe-based gadgets to containers, it resolves library paths by parsing the container's `/etc/ld.so.cache` file (`pkg/uprobetracer/ldcache_parser.go`). This file is fully controlled by the container.\n\nThe parser has three vulnerabilities:\n\n1. **Quadratic string building** (`pkg/uprobetracer/bytes.go:36-44`): The `readStringFromBytes` function concatenates one byte at a time (`res += string(data[i])`), which is O(n²) in Go due to string immutability. With a 16MB cache file containing large regions without null terminators, this causes massive CPU and memory churn.\n\n2. **Insufficient entry count validation** (`pkg/uprobetracer/ldcache_parser.go:120`): The `EntryCount` field is read directly from the untrusted file. While a per-entry bounds check prevents out-of-bounds access, the loop still iterates up to `(fileSize - headerSize) / entrySize ≈ 700,000` times, calling `readStringFromBytes` on each iteration.\n\n3. **Integer overflow in format detection** (`pkg/uprobetracer/ldcache_parser.go:174`): The `cache1Len` computation uses uint32 arithmetic (`ldCache1Size + cache1.EntryCount*ldCache1EntrySize`). With a crafted `EntryCount`, this overflows and produces a small value, causing the parser to misidentify the cache format.\n\nCombined, these cause ~53 seconds of CPU burn per container attachment when a crafted 16MB `/etc/ld.so.cache` is present.\n\n## Impact\n\n- **Container runtime DoS**: IG uses fanotify hooks (`pkg/container-hook`) to pause container startup until uprobe attachment completes. While IG is blocked processing the malicious cache, this pause is held, and Docker serializes container starts — meaning no other container can start on the host until IG finishes. This effectively causes a denial of service on the entire container runtime, not just on IG itself.\n- **Container startup delay**: When any uprobe-based gadget is running (trace_malloc, trace_ssl, etc.), starting a container with a crafted ld.so.cache delays startup by ~1 minute.\n- **Monitoring degradation**: The IG daemon is blocked processing the malicious cache, potentially missing events from other containers.\n- **Amplification**: Multiple containers with crafted caches can be started simultaneously to amplify the effect.\n- **No special privileges required**: Any container can include a crafted `/etc/ld.so.cache` in its image, mount one via a volume, or overwrite it at runtime before IG starts a uprobe gadget. In this last case, IG inspects all already-running containers when the gadget starts — this still burns CPU but does not block other containers from starting (since the fanotify pause only applies to new container starts).\n\n## Root Cause Analysis\n\nIn `pkg/uprobetracer/ldcache_parser.go`, the function `readCacheFormat2` is called with the full file content:\n\n```go\nfor i := uint32(0); i < ldCache.EntryCount; i++ {\n entryOffset := ldEntriesOffset + i*ldCache2EntrySize\n if uint32(len(data)) <= entryOffset+ldCache2EntrySize {\n return nil // bounds check stops iteration\n }\n // ... reads entry ...\n key := readStringFromBytes(data, keyOffset) // O(n²) per call\n value := readStringFromBytes(data, valueOffset) // O(n²) per call\n}\n```\n\nThe per-entry bounds check correctly prevents out-of-bounds access, but:\n- The loop iterates ~700K times (limited by file size, not EntryCount)\n- Each `readStringFromBytes` call uses quadratic string concatenation\n\nIn `pkg/uprobetracer/bytes.go`:\n\n```go\nfunc readStringFromBytes(data []byte, startPos uint32) string {\n res := \"\"\n for i := startPos; i < uint32(len(data)); i++ {\n if data[i] == 0 {\n return res\n }\n res += string(data[i]) // O(n²) — allocates new string each iteration\n }\n return \"\"\n}\n```\n\n## Note on Slice Bounds Checks\n\nThe code also performs slice accesses without proper bounds checks (e.g.,\n`data[:len(cache2Header)]` when `data` may be shorter than 20 bytes, and\n`ldCacheFile[:len(cache1Header)]` when the file may be shorter than 11 bytes).\n\nIn practice, a malicious container **cannot currently trigger a panic** from these\nmissing checks. This is because Go's `io.ReadAll` (used to read the file) always\nreturns slices with `cap >= 512` due to its initial buffer allocation\n(`make([]byte, 0, 512)` in Go's standard library). In Go, `s[:n]` only panics\nwhen `n > cap(s)`, not when `n > len(s)`. Since both header lengths (11 and 20)\nare well below 512, the slice expressions succeed — they simply read zero bytes\nbeyond `len`, which don't match any valid header magic.\n\nHowever, this relies on an **undocumented implementation detail** of `io.ReadAll`\nwhich could change in future Go versions. The bounds checks are still necessary\nfor correctness and defense in depth.",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Go",
"name" : "github.com/inspektor-gadget/inspektor-gadget"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0.27.0"
}, {
"fixed" : "0.53.1"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/inspektor-gadget/inspektor-gadget/security/advisories/GHSA-vjhx-2cqw-3q6q"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/inspektor-gadget/inspektor-gadget"
}, {
"type" : "WEB",
"url" : "https://github.com/inspektor-gadget/inspektor-gadget/releases/tag/v0.53.1"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-400", "CWE-770" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-08-19T19:16:35Z",
"nvd_published_at" : null
}
}