Skip to content
This repository has been archived by the owner on Oct 23, 2022. It is now read-only.

first attempt at fuzzer fixes #511

Merged
merged 1 commit into from
Sep 22, 2022
Merged

Conversation

evanrichter
Copy link
Contributor

This PR fixes two crashes that the #510 fuzzer found.

I am not sure if 64k is a reasonable limitation for bytes and list items, but please let me know what they should be!

Copy link
Collaborator

@koivunej koivunej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I think the 64 kB might be too small in both cases. Reading values in 64kB chunks might be a good idea, which should exhaust any malformed input faster than any noticeable large allocation gets to happen, but that can be done afterwards. This would come at the cost of few reallocs.

I'll approve and try to merge this soon-ish.

@evanrichter
Copy link
Contributor Author

ok, sounds good-- I trust you have a much better sense for a reasonable limit here!

@koivunej
Copy link
Collaborator

To explain my thinking a bit further:

Within the context of IPFS there's a maximum block size "constant", which already narrows this down. I haven't checked how has the constant been doing in the last years, but assuming there's one limit X opens the door to either early exit when a field would be longer than what can remain in a document. This might not be applicable in all contexts (unsure if ipfs supports any block compression transparently through the paths yet for example).

The chunking idea would be similar to:

pub fn read_bytes<R: Read>(r: &mut R, len: usize) -> CborResult<Vec<u8>> {
  // some arbitrary limit for safer reallocs
  let limit = 64 * 1024;
  let mut out = Vec::new();
  let mut remaining = len;
  let mut read = 0;

  while remaining > 0 {
    let extended = std::cmp::min(remaining, limit);

    // this could also be:
    // out.reserve(extended);
    // access the memory through out.spare_capacity_mut() but
    // unsure if Read yet accepts &mut [MaybeUninit<u8>]
    // and finally an `unsafe { out.set_len(read); } `
    out.extend(std::iter::repeat(0u8).take(extended));
  
    r.read_exact(&mut out[read..][..extended])?;

    read += extended;
    remaining = remaining.checked_sub(extended).unwrap();
  }

  Ok(out)
}

(Above most likely has bugs, only wrote it, didn't test it :)

But this would of course be a slower implementation due to reallocs and possibly suboptimal performance where R would happen to be a BufRead and have to read twice for each read assuming using 64kB buffer and starting the read from the middle. I think it would always defeat the fuzzer at least for this method, meaning all of those payloads which cause huge allocations in small amount of input.

Could be that there's also now a limit on how long can a raw bytes field be in CBOR, I haven't had the time to check.

However this is good enough for now :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants