servo / rust-url

URL parser for Rust
https://docs.rs/url/
Apache License 2.0
1.27k stars 317 forks source link

data-url: Fuzzed data URL causes high memory usage #941

Open correctmost opened 2 weeks ago

correctmost commented 2 weeks ago

Describe the bug

librsvg uses the data-url crate to parse data URLs. I recently added librsvg to OSS-Fuzz, which triggered a case where a fuzzed ~630KB data URL inside of an SVG caused data-url to allocate >2.5GB memory.

That seems like a lot of memory given the size of the input, but I'm not sure how pathological the fuzzed data is.

Version: 0.3.1

Sample code to reproduce the issue

use data_url::DataUrl;
use std::fs;

fn main() {
    if let Ok(data) = fs::read_to_string("fuzzed_data.txt") {
        let url = DataUrl::process(&data).unwrap();
        url.decode_to_vec().unwrap();
    }
}

fuzzed_data.txt

Massif data

--------------------------------------------------------------------------------
  n        time(B)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 45  2,833,383,176    2,833,379,096    2,833,374,944         4,152            0
 46  2,833,383,176    2,833,379,096    2,833,374,944         4,152            0
100.00% (2,833,374,944B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->99.98% (2,832,728,064B) 0x11ABF7: realloc (alloc.rs:138)
| ->99.98% (2,832,728,064B) 0x11ABF7: alloc::alloc::Global::grow_impl (alloc.rs:215)
|   ->99.98% (2,832,728,064B) 0x11AFEF: <alloc::alloc::Global as core::alloc::Allocator>::grow (alloc.rs:268)
|     ->99.98% (2,832,728,064B) 0x1143DA: alloc::raw_vec::finish_grow (raw_vec.rs:570)
|       ->99.98% (2,832,728,064B) 0x114CF9: alloc::raw_vec::RawVec<T,A>::grow_amortized (raw_vec.rs:485)
|         ->99.98% (2,832,728,064B) 0x115458: alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (raw_vec.rs:349)
|           ->99.98% (2,832,728,064B) 0x11D698: reserve<u8, alloc::alloc::Global> (raw_vec.rs:355)
|             ->99.98% (2,832,728,064B) 0x11D698: alloc::vec::Vec<T,A>::reserve (mod.rs:972)
|               ->99.98% (2,832,728,064B) 0x11D357: alloc::vec::Vec<T,A>::append_elements (mod.rs:2143)
|                 ->99.98% (2,832,728,064B) 0x11C428: <alloc::vec::Vec<T,A> as alloc::vec::spec_extend::SpecExtend<&T,core::slice::iter::Iter<T>>>::spec_extend (spec_extend.rs:55)
|                   ->99.98% (2,832,728,064B) 0x11D455: alloc::vec::Vec<T,A>::extend_from_slice (mod.rs:2589)
|                     ->99.98% (2,832,728,064B) 0x116FCA: data_url::DataUrl::decode_to_vec::{{closure}} (lib.rs:117)
|                       ->99.98% (2,832,728,064B) 0x119064: data_url::decode_without_base64 (lib.rs:300)
|                         ->99.98% (2,832,728,064B) 0x116DCF: data_url::DataUrl::decode (lib.rs:106)
|                           ->99.98% (2,832,728,064B) 0x116E62: data_url::DataUrl::decode_to_vec (lib.rs:116)
|                             ->99.98% (2,832,728,064B) 0x111F01: crash::main (main.rs:7)
|                               ->99.98% (2,832,728,064B) 0x11218A: core::ops::function::FnOnce::call_once (function.rs:250)
|                                 ->99.98% (2,832,728,064B) 0x1120ED: std::sys_common::backtrace::__rust_begin_short_backtrace (backtrace.rs:155)
|                                   ->99.98% (2,832,728,064B) 0x112950: std::rt::lang_start::{{closure}} (rt.rs:159)
|                                     ->99.98% (2,832,728,064B) 0x136875: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284)
|                                       ->99.98% (2,832,728,064B) 0x136875: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:552)
|                                         ->99.98% (2,832,728,064B) 0x136875: try<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:516)
|                                           ->99.98% (2,832,728,064B) 0x136875: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:149)
|                                             ->99.98% (2,832,728,064B) 0x136875: {closure
|                                               ->99.98% (2,832,728,064B) 0x136875: do_call<std::rt::lang_start_internal::{closure_env
|                                                 ->99.98% (2,832,728,064B) 0x136875: try<isize, std::rt::lang_start_internal::{closure_env
|                                                   ->99.98% (2,832,728,064B) 0x136875: catch_unwind<std::rt::lang_start_internal::{closure_env
|                                                     ->99.98% (2,832,728,064B) 0x136875: std::rt::lang_start_internal (rt.rs:141)
|                                                       ->99.98% (2,832,728,064B) 0x112929: std::rt::lang_start (rt.rs:158)
|                                                         ->99.98% (2,832,728,064B) 0x1120DD: main
|                                                           
->00.02% (646,880B) in 1+ places, all below ms_print's threshold (01.00%)