fix(debuginfo): Add some dwarf debuginfo recursion limits#1015
4 issues
wrdn-dos-review: Found 4 issues (2 high, 2 medium)
High
parse_function / parse_function_children lack depth limit for nested DW_TAG_subprogram - `symbolic-debuginfo/src/dwarf.rs:1359`
While the PR caps inlined-subroutine recursion with max_inline_depth, nested DW_TAG_subprogram entries are still traversed via mutual recursion between parse_function and parse_function_children with no depth bound. A crafted DWARF file with a chain of deeply nested subprograms causes an unbounded stack growth and an uncatchable stack-overflow abort.
Also found at:
symbolic-debuginfo/src/dwarf.rs:1760symbolic-debuginfo/src/pe.rs:260symbolic-debuginfo/src/dwarf.rs:586symbolic-debuginfo/src/dwarf.rs:1893symbolic-debuginfo/src/elf.rs:568
ELF decompress_section defaults to unlimited size, allowing unbounded allocation - `symbolic-debuginfo/src/elf.rs:360`
max_decompressed_section_size defaults to None, which decompress_section unwraps to usize::MAX. An attacker-controlled compressed section header can declare any decompressed size, causing Vec::with_capacity(size) or zstd::bulk::decompress to attempt a multi-gigabyte allocation before any bytes are validated against real input.
Also found at:
symbolic-debuginfo/src/object.rs:152-153
Medium
Unbounded symbol table count passed to goblin without caller-side limit - `symbolic-debuginfo/src/elf.rs:360`
parse_with_opts derives count = shdr.sh_size / shdr.sh_entsize from attacker-controlled ELF section headers and passes it unbounded to elf::Symtab::parse. If sh_entsize is 1 and sh_size is huge, goblin may pre-allocate a Vec with up to usize::MAX entries before validating against the actual data length.
PeEmbeddedPortablePDB::decompress_to has no output-size cap on deflate decompression - `symbolic-debuginfo/src/pe.rs:264`
embedded_ppdb reads an attacker-declared uncompressed_size from the PE debug directory, and decompress_to feeds the compressed bytes into a flate2::write::DeflateDecoder without a max-output-size check; a compression bomb causes unbounded memory or disk growth.
⏱ 103m 4s · 18.2M in / 780.5k out · $6.01
Annotations
Check failure on line 1359 in symbolic-debuginfo/src/dwarf.rs
sentry-warden / warden: wrdn-dos-review
parse_function / parse_function_children lack depth limit for nested DW_TAG_subprogram
While the PR caps inlined-subroutine recursion with `max_inline_depth`, nested `DW_TAG_subprogram` entries are still traversed via mutual recursion between `parse_function` and `parse_function_children` with no depth bound. A crafted DWARF file with a chain of deeply nested subprograms causes an unbounded stack growth and an uncatchable stack-overflow abort.
Check failure on line 1760 in symbolic-debuginfo/src/dwarf.rs
sentry-warden / warden: wrdn-dos-review
[EVE-PMU] parse_function / parse_function_children lack depth limit for nested DW_TAG_subprogram (additional location)
While the PR caps inlined-subroutine recursion with `max_inline_depth`, nested `DW_TAG_subprogram` entries are still traversed via mutual recursion between `parse_function` and `parse_function_children` with no depth bound. A crafted DWARF file with a chain of deeply nested subprograms causes an unbounded stack growth and an uncatchable stack-overflow abort.
Check failure on line 260 in symbolic-debuginfo/src/pe.rs
sentry-warden / warden: wrdn-dos-review
[EVE-PMU] parse_function / parse_function_children lack depth limit for nested DW_TAG_subprogram (additional location)
While the PR caps inlined-subroutine recursion with `max_inline_depth`, nested `DW_TAG_subprogram` entries are still traversed via mutual recursion between `parse_function` and `parse_function_children` with no depth bound. A crafted DWARF file with a chain of deeply nested subprograms causes an unbounded stack growth and an uncatchable stack-overflow abort.
Check failure on line 586 in symbolic-debuginfo/src/dwarf.rs
sentry-warden / warden: wrdn-dos-review
[EVE-PMU] parse_function / parse_function_children lack depth limit for nested DW_TAG_subprogram (additional location)
While the PR caps inlined-subroutine recursion with `max_inline_depth`, nested `DW_TAG_subprogram` entries are still traversed via mutual recursion between `parse_function` and `parse_function_children` with no depth bound. A crafted DWARF file with a chain of deeply nested subprograms causes an unbounded stack growth and an uncatchable stack-overflow abort.
Check failure on line 1893 in symbolic-debuginfo/src/dwarf.rs
sentry-warden / warden: wrdn-dos-review
[EVE-PMU] parse_function / parse_function_children lack depth limit for nested DW_TAG_subprogram (additional location)
While the PR caps inlined-subroutine recursion with `max_inline_depth`, nested `DW_TAG_subprogram` entries are still traversed via mutual recursion between `parse_function` and `parse_function_children` with no depth bound. A crafted DWARF file with a chain of deeply nested subprograms causes an unbounded stack growth and an uncatchable stack-overflow abort.
Check failure on line 568 in symbolic-debuginfo/src/elf.rs
sentry-warden / warden: wrdn-dos-review
[EVE-PMU] parse_function / parse_function_children lack depth limit for nested DW_TAG_subprogram (additional location)
While the PR caps inlined-subroutine recursion with `max_inline_depth`, nested `DW_TAG_subprogram` entries are still traversed via mutual recursion between `parse_function` and `parse_function_children` with no depth bound. A crafted DWARF file with a chain of deeply nested subprograms causes an unbounded stack growth and an uncatchable stack-overflow abort.
Check failure on line 360 in symbolic-debuginfo/src/elf.rs
sentry-warden / warden: wrdn-dos-review
ELF decompress_section defaults to unlimited size, allowing unbounded allocation
`max_decompressed_section_size` defaults to `None`, which `decompress_section` unwraps to `usize::MAX`. An attacker-controlled compressed section header can declare any decompressed size, causing `Vec::with_capacity(size)` or `zstd::bulk::decompress` to attempt a multi-gigabyte allocation before any bytes are validated against real input.
Check failure on line 153 in symbolic-debuginfo/src/object.rs
sentry-warden / warden: wrdn-dos-review
[XWD-E2M] ELF decompress_section defaults to unlimited size, allowing unbounded allocation (additional location)
`max_decompressed_section_size` defaults to `None`, which `decompress_section` unwraps to `usize::MAX`. An attacker-controlled compressed section header can declare any decompressed size, causing `Vec::with_capacity(size)` or `zstd::bulk::decompress` to attempt a multi-gigabyte allocation before any bytes are validated against real input.
Check warning on line 360 in symbolic-debuginfo/src/elf.rs
sentry-warden / warden: wrdn-dos-review
Unbounded symbol table count passed to goblin without caller-side limit
`parse_with_opts` derives `count = shdr.sh_size / shdr.sh_entsize` from attacker-controlled ELF section headers and passes it unbounded to `elf::Symtab::parse`. If `sh_entsize` is 1 and `sh_size` is huge, goblin may pre-allocate a Vec with up to `usize::MAX` entries before validating against the actual data length.
Check warning on line 264 in symbolic-debuginfo/src/pe.rs
sentry-warden / warden: wrdn-dos-review
PeEmbeddedPortablePDB::decompress_to has no output-size cap on deflate decompression
`embedded_ppdb` reads an attacker-declared `uncompressed_size` from the PE debug directory, and `decompress_to` feeds the compressed bytes into a `flate2::write::DeflateDecoder` without a max-output-size check; a compression bomb causes unbounded memory or disk growth.