servo / servo/rust-url

data-url: Fuzzed data URL causes high memory usage

Open
#941 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
1.6k
Forks
406
PR merge metrics
No merged PRs in 30d

Description

  • Note that this crate implements the URL Standard not RFC 1738 or RFC 3986

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%)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with lib.rs:106-117 and decode_without_base64 at lib.rs:300, then reproduce the allocation using the linked fuzzed_data.txt and sample program. Trace how the decoded output grows for non-base64 data and add a regression test. Done means the sample no longer causes disproportionate memory use while valid data URLs still decode correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.