mapeditor / rs-tiled

Reads files from the Tiled editor into Rust
https://crates.io/crates/tiled
MIT License
268 stars 102 forks source link

Add cargo fuzz support #287

Closed smackysnacks closed 6 months ago

smackysnacks commented 6 months ago

Addresses https://github.com/mapeditor/rs-tiled/issues/209

This PR adds initial cargo fuzz integration. Just run cargo +nightly fuzz run tiled. On my machine it found a panic pretty quickly. You can use cargo +nightly fuzz tmin tiled artifacts/tiled/crash-<ID> to help minimize the input. Here's the minimized input it found on my machine:

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.4" tiledversion="1.4.0" orientation="orthogonal" renderorder="right-down" width="100" height="100" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#ff00ff" nextlayerid="3" nextobjectid="5">
 <tileset firstgid="1" name="tilesheet" tilewidth="32" tileheight="32" tilecount="84" columns="14">="192"/>
  <tile id="1">
   <properties>
    <property name="a tile property" value="123"/>
   </properties>
  </tile>
 </tileset>
 <layer id="1" name="Tile Layer 1" width="100" height="100">
  <properties>
   <property name="prop1" value="12"/>
   <property name="prop2" value="some text"/>
    </properties>
  <data encoding="csv">
35,35,350,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,object id="4" x=,0 139,128 -55,64 -37,-49 159,object>
 </objectgroup>
</map>

And the associated backtrace:

target/x86_64-unknown-linux-gnu/release/tiled: Running 1 inputs 1 time(s) each.
Running: artifacts/tiled/minimized-from-24190e64bc998016ec1dca07850c40fdb51c7182
thread '<unnamed>' panicked at /mnt/e/code/rs-tiled/src/layers/tile/util.rs:75:47:
called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }
stack backtrace:
   0: rust_begin_unwind
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/std/src/panicking.rs:652:5
   1: core::panicking::panic_fmt
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/result.rs:1658:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/result.rs:1081:23
   4: tiled::layers::tile::util::decode_csv::{{closure}}
             at /mnt/e/code/rs-tiled/src/layers/tile/util.rs:75:30
   5: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &mut F>::call_once
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/ops/function.rs:305:13
   6: core::option::Option<T>::map
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/option.rs:1072:29
   7: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::next
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/iter/adapters/map.rs:108:26
   8: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::next
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/iter/adapters/map.rs:108:9
   9: alloc::vec::Vec<T,A>::extend_desugared
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/alloc/src/vec/mod.rs:3066:35
  10: <alloc::vec::Vec<T,A> as alloc::vec::spec_extend::SpecExtend<T,I>>::spec_extend
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/alloc/src/vec/spec_extend.rs:17:9
  11: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter_nested::SpecFromIterNested<T,I>>::from_iter
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/alloc/src/vec/spec_from_iter_nested.rs:43:9
  12: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/alloc/src/vec/spec_from_iter.rs:33:9
  13: <alloc::vec::Vec<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/alloc/src/vec/mod.rs:2972:9
  14: core::iter::traits::iterator::Iterator::collect
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/core/src/iter/traits/iterator.rs:2005:9
  15: tiled::layers::tile::util::decode_csv
             at /mnt/e/code/rs-tiled/src/layers/tile/util.rs:77:22
  16: tiled::layers::tile::util::parse_data_line
             at /mnt/e/code/rs-tiled/src/layers/tile/util.rs:15:32
  17: tiled::layers::tile::finite::FiniteTileLayerData::new
             at /mnt/e/code/rs-tiled/src/layers/tile/finite.rs:56:21
  18: tiled::layers::tile::TileLayerData::new::{{closure}}
             at /mnt/e/code/rs-tiled/src/layers/tile/mod.rs:117:43
  19: tiled::layers::tile::TileLayerData::new
             at /mnt/e/code/rs-tiled/src/util.rs:189:60
  20: tiled::layers::LayerData::new
             at /mnt/e/code/rs-tiled/src/layers/mod.rs:112:40
  21: tiled::map::Map::parse_xml::{{closure}}
             at /mnt/e/code/rs-tiled/src/map.rs:190:29
  22: tiled::map::Map::parse_xml
             at /mnt/e/code/rs-tiled/src/util.rs:189:60
  23: tiled::parse::xml::map::parse_map
             at /mnt/e/code/rs-tiled/src/parse/xml/map.rs:27:28
  24: tiled::loader::Loader<Cache,Reader>::load_tmx_map
             at /mnt/e/code/rs-tiled/src/loader.rs:169:9
  25: tiled::_::__libfuzzer_sys_run
             at ./fuzz_targets/tiled.rs:31:13
  26: rust_fuzzer_test_input
             at /home/smacks/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.7/src/lib.rs:224:17
  27: libfuzzer_sys::test_input_wrap::{{closure}}
             at /home/smacks/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.7/src/lib.rs:61:9
  28: std::panicking::try::do_call
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/std/src/panicking.rs:559:40
  29: __rust_try
  30: std::panicking::try
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/std/src/panicking.rs:523:19
  31: std::panic::catch_unwind
             at /rustc/36153f1a4e3162f0a143c7b3e468ecb3beb0008e/library/std/src/panic.rs:149:14
  32: LLVMFuzzerTestOneInput
             at /home/smacks/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.7/src/lib.rs:59:22
  33: _ZN6fuzzer6Fuzzer15ExecuteCallbackEPKhm
             at /home/smacks/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.7/libfuzzer/FuzzerLoop.cpp:612:15
  34: _ZN6fuzzer10RunOneTestEPNS_6FuzzerEPKcm
             at /home/smacks/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.7/libfuzzer/FuzzerDriver.cpp:324:21
  35: _ZN6fuzzer12FuzzerDriverEPiPPPcPFiPKhmE
             at /home/smacks/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.7/libfuzzer/FuzzerDriver.cpp:860:19
  36: main
             at /home/smacks/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libfuzzer-sys-0.4.7/libfuzzer/FuzzerMain.cpp:20:30
  37: <unknown>
  38: __libc_start_main
  39: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

The majority of additions here are sample tmx files used to seed the corpus. The actual test harness is https://github.com/mapeditor/rs-tiled/pull/287/files#diff-033f3dc25a239c9a63b10aa400c2d43accb526ed8e9ce918cf4c3d7d6807a54b.

aleokdev commented 6 months ago

Thank you! One question: Are the corpus files the same ones as in examples/? If so, could we (or is it advisable to) reuse them somehow?

smackysnacks commented 6 months ago

Thank you! One question: Are the corpus files the same ones as in examples/? If so, could we (or is it advisable to) reuse them somehow?

They are from the assets/ dir that the examples use. We may be able to avoid duplicating them by writing a fuzz/build.rs that performs the copy at build-time. Let me see if I can get something like that working.