From ab9a2485f73bb00fee6d268662ad530fb1cdbeb7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 21 Mar 2021 22:56:28 +0100 Subject: [PATCH 001/104] llmp cleanups --- libafl/src/bolts/llmp.rs | 72 ++++++++++++++++++++++++--------------- libafl/src/bolts/shmem.rs | 4 +-- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index bb71eeb574..054bc2c3c9 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -966,7 +966,7 @@ where unsafe fn recv(&mut self) -> Result, Error> { /* DBG("recv %p %p\n", page, last_msg); */ compiler_fence(Ordering::SeqCst); - let page = self.current_recv_map.page_mut(); + let mut page = self.current_recv_map.page_mut(); let last_msg = self.last_msg_recvd; let current_msg_id = ptr::read_volatile(&(*page).current_msg_id); @@ -1004,7 +1004,7 @@ where } LLMP_TAG_END_OF_PAGE => { #[cfg(feature = "std")] - dbg!("Got end of page, allocing next"); + println!("Received end of page, allocating next"); // Handle end of page if (*msg).buf_len < size_of::() as u64 { panic!( @@ -1015,18 +1015,19 @@ where } let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - /* We can reuse the map mem space, no need to free and calloc. - However, the pageinfo points to the map we're about to unmap. - Clone the contents first to be safe (probably fine in rust eitner way). */ + /* The pageinfo points to the map we're about to unmap. + Copy the contents first to be safe (probably fine in rust either way). */ let pageinfo_cpy = *pageinfo; // Mark the old page save to unmap, in case we didn't so earlier. ptr::write_volatile(&mut (*page).save_to_unmap, 1); + // Map the new page. The old one should be unmapped by Drop self.current_recv_map = LlmpSharedMap::existing(SH::existing_from_shm_slice( &pageinfo_cpy.shm_str, pageinfo_cpy.map_size, )?); + page = self.current_recv_map.page_mut(); // Mark the new page save to unmap also (it's mapped by us, the broker now) ptr::write_volatile(&mut (*page).save_to_unmap, 1); @@ -1434,7 +1435,9 @@ where // to read from the initial map id. let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; - let broadcast_str_initial = *client_out_map_mem.shm_slice(); + let broadcast_map_description = postcard::to_allocvec(&client_out_map_mem.description())?; + + let mut incoming_map_description_serialized = vec![0u8; broadcast_map_description.len()]; let llmp_tcp_id = self.llmp_clients.len() as u32; @@ -1462,33 +1465,37 @@ where match listener.accept() { ListenerStream::Tcp(mut stream, addr) => { dbg!("New connection", addr, stream.peer_addr().unwrap()); - match stream.write(&broadcast_str_initial) { + match stream.write(&broadcast_map_description) { Ok(_) => {} // fire & forget Err(e) => { dbg!("Could not send to shmap to client", e); continue; } }; - let mut new_client_map_str: [u8; 20] = Default::default(); - match stream.read_exact(&mut new_client_map_str) { + match stream.read_exact(&mut incoming_map_description_serialized) { Ok(()) => (), Err(e) => { dbg!("Ignoring failed read from client", e); continue; } }; - unsafe { - let msg = new_client_sender - .alloc_next(size_of::()) - .expect("Could not allocate a new message in shared map."); - (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; - let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*pageinfo).shm_str = new_client_map_str; - (*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE; - match new_client_sender.send(msg) { - Ok(()) => (), - Err(e) => println!("Error forwarding client on map: {:?}", e), - }; + if let Ok(incoming_map_description) = postcard::from_bytes::( + &incoming_map_description_serialized, + ) { + unsafe { + let msg = new_client_sender + .alloc_next(size_of::()) + .expect("Could not allocate a new message in shared map."); + (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; + let pageinfo = + (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; + (*pageinfo).shm_str = incoming_map_description.str_bytes; + (*pageinfo).map_size = incoming_map_description.size; + match new_client_sender.send(msg) { + Ok(()) => (), + Err(e) => println!("Error forwarding client on map: {:?}", e), + }; + } } } #[cfg(unix)] @@ -1496,14 +1503,14 @@ where dbg!("New connection", addr); let broadcast_fd_initial: i32 = - CStr::from_ptr(broadcast_str_initial.as_ptr() as *const c_char) + CStr::from_ptr(broadcast_map_description.as_ptr() as *const c_char) .to_string_lossy() .into_owned() .parse() .unwrap_or_else(|_| { panic!( "ShmId is not a valid int file descriptor: {:?}", - broadcast_str_initial + broadcast_map_description ) }); @@ -1856,15 +1863,24 @@ where let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))?; println!("Connected to port {}", port); - let mut new_broker_map_str: [u8; 20] = Default::default(); + // First, get the serialized description size by serializing a dummy. + let dummy_description = ShMemDescription { + size: 0, + str_bytes: Default::default(), + }; + let mut new_broker_map_str = postcard::to_allocvec(&dummy_description)?; + stream.read_exact(&mut new_broker_map_str)?; - let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice( - &new_broker_map_str, - LLMP_CFG_INITIAL_MAP_SIZE, + let broker_map_description: ShMemDescription = postcard::from_bytes(&new_broker_map_str)?; + + let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_description( + &broker_map_description, )?))?; - stream.write_all(ret.sender.out_maps.first().unwrap().shmem.shm_slice())?; + let own_map_description_bytes = + postcard::to_allocvec(&ret.sender.out_maps.first().unwrap().shmem.description())?; + stream.write_all(&own_map_description_bytes)?; Ok(ret) } } diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 2d832da7e9..999e21591b 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -20,9 +20,9 @@ use crate::Error; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct ShMemDescription { /// Size of this map - size: usize, + pub size: usize, /// of name of this map, as fixed 20 bytes c-string - str_bytes: [u8; 20], + pub str_bytes: [u8; 20], } /// A Shared map From 245379c0205e6e41bd5243446c46051f7ba073d7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 21 Mar 2021 23:38:06 +0100 Subject: [PATCH 002/104] added fuzz_loop_for --- libafl/src/fuzzer.rs | 101 +++++++++++++++++++++++++++++++++---------- libafl/src/utils.rs | 20 ++------- 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index b65587d205..d1c34324c1 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -6,10 +6,15 @@ use crate::{ observers::ObserversTuple, stages::StagesTuple, state::HasExecutions, - utils::{current_milliseconds, current_time}, + utils::current_time, Error, }; -use core::marker::PhantomData; + +use core::{marker::PhantomData, time::Duration}; +use std::ops::Sub; + +/// Send a stats update all 6 (or more) seconds +const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(6 * 1000); /// Holds a set of stages pub trait HasStages @@ -38,9 +43,55 @@ where /// The main fuzzer trait. pub trait Fuzzer { + /// Fuzz for a single iteration + /// Returns the index of the last fuzzed corpus item fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result; - fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result; + /// Fuzz forever (or until stopped) + fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<(), Error> { + let mut last = current_time(); + let stats_timeout = STATS_TIMEOUT_DEFAULT; + loop { + self.fuzz_one(state, executor, manager)?; + last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; + } + } + + /// Fuzz for n iterations + /// Returns the index of the last fuzzed corpus item + fn fuzz_loop_for( + &self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + iters: u64, + ) -> Result { + if iters == 0 { + return Err(Error::IllegalArgument( + "Cannot fuzz for 0 iterations!".to_string(), + )); + } + + let mut ret = 0; + let mut last = current_time(); + let stats_timeout = STATS_TIMEOUT_DEFAULT; + + for _ in 0..iters { + ret = self.fuzz_one(state, executor, manager)?; + last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; + } + Ok(ret) + } + + /// Given the last time, if stats_timeout seconds passed, send off an info/stats/heartbeat message to the broker. + /// Returns the new `last` time (so the old one, unless `stats_timeout` time has passed and stats have been sent) + /// Will return an Error, if the stats could not be sent. + fn maybe_report_stats( + state: &mut S, + manager: &mut EM, + last: Duration, + stats_timeout: Duration, + ) -> Result; } /// Your default fuzzer instance, for everyday use. @@ -102,6 +153,31 @@ where OT: ObserversTuple, I: Input, { + #[inline] + fn maybe_report_stats( + state: &mut S, + manager: &mut EM, + last: Duration, + stats_timeout: Duration, + ) -> Result { + let cur = current_time(); + if cur.sub(last) > stats_timeout { + //println!("Fire {:?} {:?} {:?}", cur, last, stats_timeout); + manager.fire( + state, + Event::UpdateStats { + executions: *state.executions(), + time: cur, + phantom: PhantomData, + }, + )?; + Ok(cur) + } else { + if cur.as_millis() % 1000 == 0 {} + Ok(last) + } + } + fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result { let idx = self.scheduler().next(state)?; @@ -111,25 +187,6 @@ where manager.process(state, executor, self.scheduler())?; Ok(idx) } - - fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result { - let mut last = current_milliseconds(); - loop { - self.fuzz_one(state, executor, manager)?; - let cur = current_milliseconds(); - if cur - last > 60 * 100 { - last = cur; - manager.fire( - state, - Event::UpdateStats { - executions: *state.executions(), - time: current_time(), - phantom: PhantomData, - }, - )? - } - } - } } impl StdFuzzer diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 27fcb93115..74734333d1 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -160,28 +160,16 @@ pub fn current_time() -> time::Duration { time::Duration::from_millis(1) } -#[cfg(feature = "std")] -#[inline] /// Gets current nanoseconds since UNIX_EPOCH +#[inline] pub fn current_nanos() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos() as u64 + current_time().as_nanos() as u64 } -#[cfg(feature = "std")] /// Gets current milliseconds since UNIX_EPOCH +#[inline] pub fn current_milliseconds() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as u64 -} - -#[cfg(not(feature = "std"))] -pub fn current_milliseconds() -> u64 { - 1000 + current_time().as_millis() as u64 } /// XXH3 Based, hopefully speedy, rnd implementation From a02cc39bba26a7f58562ad4ea71ef0ced1b9b327 Mon Sep 17 00:00:00 2001 From: s1341 Date: Mon, 22 Mar 2021 13:45:38 +0200 Subject: [PATCH 003/104] Frida Executor Example (#27) * inprocess: Allow InProcessExecutor to take a function pointer or a closure * frida: initial working (but slow + buggy) frida helper Issues: - it's slow as **** - there is an Llmp exception after the 227th corpus entry is found - Cargo.toml lines currently import from a local ../frida-rust dir, as frida-rust is still under development * inprocess: let the InProcessExecutor take a closure or a function pointer * frida: working FridaHelper with InProcessExecutor * frida: Apply suggestions; Move to RefCell; Cleanup warnings * frida: link libstdc++_static.a on android * take an FnMut in InProcessExecutor * adapt libfuzzer_libpng to FnMut in InProcessExecutor * create FridaInProcessExecutor and FridaEdgeCoverageHelper * fix frida build.rs * frida: move gum to main, get rid of lazy_static; use PageProtection enum * stalker exclude * frida: implement inline map-update for x86_64 * inprocess: add harness/harness_mut accessors * format * remove get_module_size from FridaEdgeCoverageHelper * frida: implement aarch64 inline map update * frida: add missing IndexMode * add timeouts for executors * move timeouts to observer * add with_timeout constructor for Observer * cast to i64 later in pre_exec * add cfg(unix) guards * add TimeoutExecutor * add TimeoutFeedback and send ExitKind::Timeout from the handler * pass Duration and move timeout stuff to post_exec * format * add timeouts to libpng_libfuzzer * 10 sec timeout * timeout executor file * fix timeout executor no_std * format * todos * Win32ShMem * win32 exceptions * fixes * fix win32 build.rs * fix win32 build.rs * fixes fro win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * inprocess::windows_exception_handler * inprocess::windows_exception_handler fixes * windows_exception_handler in InProcessExecutor * inprocess::windows_exception_handler fix * fix windows exceptions mapping * format * format * inprocess: Allow InProcessExecutor to take a function pointer or a closure * frida: initial working (but slow + buggy) frida helper Issues: - it's slow as **** - there is an Llmp exception after the 227th corpus entry is found - Cargo.toml lines currently import from a local ../frida-rust dir, as frida-rust is still under development * inprocess: let the InProcessExecutor take a closure or a function pointer * frida: Apply suggestions; Move to RefCell; Cleanup warnings * take an FnMut in InProcessExecutor * adapt libfuzzer_libpng to FnMut in InProcessExecutor * reenabled ci for prs * frida: update to frida-rust 0.3.2 * frida: fix buid errors * frida: fix build_and_test.yml * frida: uses crates.io for frida-gum and frida-gum-sys * fix merge errors * fix typo * frida: x86_64 now working Co-authored-by: Andrea Fioraldi Co-authored-by: toka Co-authored-by: Dominik Maier --- Cargo.toml | 1 + fuzzers/frida_libpng/.gitignore | 1 + fuzzers/frida_libpng/Cargo.toml | 39 ++ fuzzers/frida_libpng/README.md | 25 + fuzzers/frida_libpng/build.rs | 114 ++++ fuzzers/frida_libpng/harness.cc | 201 +++++++ fuzzers/frida_libpng/src/fuzzer.rs | 524 ++++++++++++++++++ fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs | 22 +- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 25 +- .../libfuzzer_libpng_cmpalloc/src/fuzzer.rs | 25 +- libafl/src/executors/inprocess.rs | 48 +- libafl/src/lib.rs | 11 +- libafl/src/observers/map.rs | 10 + 13 files changed, 971 insertions(+), 75 deletions(-) create mode 100644 fuzzers/frida_libpng/.gitignore create mode 100644 fuzzers/frida_libpng/Cargo.toml create mode 100644 fuzzers/frida_libpng/README.md create mode 100644 fuzzers/frida_libpng/build.rs create mode 100644 fuzzers/frida_libpng/harness.cc create mode 100644 fuzzers/frida_libpng/src/fuzzer.rs diff --git a/Cargo.toml b/Cargo.toml index 77ae433219..98347cc3da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ #example fuzzers "fuzzers/libfuzzer_libpng", + "fuzzers/frida_libpng", "fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libpng_cmpalloc", ] diff --git a/fuzzers/frida_libpng/.gitignore b/fuzzers/frida_libpng/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/frida_libpng/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml new file mode 100644 index 0000000000..347724ca56 --- /dev/null +++ b/fuzzers/frida_libpng/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "frida_libpng" +version = "0.1.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["std", "frida"] +std = [] +frida = ["frida-gum", "frida-gum-sys"] + +#[profile.release] +#lto = true +#codegen-units = 1 +#opt-level = 3 +#debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +num_cpus = "1.0" + +[dependencies] +libafl = { path = "../../libafl/" } +#frida-gum = { path = "../../../frida-rust/frida-gum", version = "0.2.3", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +#frida-gum-sys = { path = "../../../frida-rust/frida-gum-sys", version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +frida-gum = { version = "0.3.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +frida-gum-sys = { version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +lazy_static = "1.4.0" +libc = "0.2" +libloading = "0.7.0" + +[[example]] +name = "frida_libpng" +path = "./src/fuzzer.rs" +test = false +bench = false diff --git a/fuzzers/frida_libpng/README.md b/fuzzers/frida_libpng/README.md new file mode 100644 index 0000000000..f56138c2b5 --- /dev/null +++ b/fuzzers/frida_libpng/README.md @@ -0,0 +1,25 @@ +# Libfuzzer for libpng + +This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. +To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. +It has been tested on Linux. + +## Build + +To build this example, run `cargo build --example libfuzzer_libpng --release`. +This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. +Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. +Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. + +## Run + +The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. + +Each following execution will run a fuzzer client. +As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). +This means each client will start itself again to listen for crashes and timeouts. +By restarting the actual fuzzer, it can recover from these exit conditions. + +In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). + +For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs new file mode 100644 index 0000000000..94e44cd8f4 --- /dev/null +++ b/fuzzers/frida_libpng/build.rs @@ -0,0 +1,114 @@ +// build.rs + +use std::{ + env, + path::Path, + process::{exit, Command}, +}; + +const LIBPNG_URL: &str = + "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; + +fn main() { + if cfg!(windows) { + println!("cargo:warning=Skipping libpng example on Windows"); + exit(0); + } + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + let out_dir = out_dir.to_string_lossy().to_string(); + let out_dir_path = Path::new(&out_dir); + std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir)); + + println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); + println!("cargo:rerun-if-changed=harness.cc"); + + let libpng = format!("{}/libpng-1.6.37", &out_dir); + let libpng_path = Path::new(&libpng); + let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); + + // Enforce clang for its -fsanitize-coverage support. + std::env::set_var("CC", "clang"); + std::env::set_var("CXX", "clang++"); + let ldflags = match env::var("LDFLAGS") { + Ok(val) => val, + Err(_) => "".to_string(), + }; + + // println!("cargo:warning=output path is {}", libpng); + if !libpng_path.is_dir() { + if !Path::new(&libpng_tar).is_file() { + println!("cargo:warning=Libpng not found, downloading..."); + // Download libpng + Command::new("wget") + .arg("-c") + .arg(LIBPNG_URL) + .arg("-O") + .arg(&libpng_tar) + .status() + .unwrap(); + } + Command::new("tar") + .current_dir(&out_dir_path) + .arg("xvf") + .arg(&libpng_tar) + .status() + .unwrap(); + Command::new(format!("{}/configure", &libpng)) + .current_dir(&libpng_path) + .args(&[ + "--disable-shared", + &format!("--host={}", env::var("TARGET").unwrap())[..], + ]) + .env("CC", "clang") + .env("CXX", "clang++") + .env( + "CFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "CXXFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "LDFLAGS", + //format!("-g -fPIE -fsanitize=address {}", ldflags), + format!("-g -fPIE {}", ldflags), + ) + .status() + .unwrap(); + Command::new("make") + .current_dir(&libpng_path) + .status() + .unwrap(); + } + + let status = cc::Build::new() + .cpp(true) + .get_compiler() + .to_command() + .current_dir(&cwd) + .arg("-I") + .arg(format!("{}", &libpng)) + //.arg("-D") + //.arg("HAS_DUMMY_CRASH=1") + .arg("-fPIC") + .arg("-shared") + .arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" { + "-static-libstdc++" + } else { + "" + }) + .arg("-o") + .arg(format!("{}/libpng-harness.so", &out_dir)) + .arg("./harness.cc") + .arg(format!("{}/.libs/libpng16.a", &libpng)) + .arg("-l") + .arg("z") + .status() + .unwrap(); + assert!(status.success()); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/fuzzers/frida_libpng/harness.cc b/fuzzers/frida_libpng/harness.cc new file mode 100644 index 0000000000..eaf9aa27ba --- /dev/null +++ b/fuzzers/frida_libpng/harness.cc @@ -0,0 +1,201 @@ +// libpng_read_fuzzer.cc +// Copyright 2017-2018 Glenn Randers-Pehrson +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that may +// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE + +// Last changed in libpng 1.6.35 [July 15, 2018] + +// The modifications in 2017 by Glenn Randers-Pehrson include +// 1. addition of a PNG_CLEANUP macro, +// 2. setting the option to ignore ADLER32 checksums, +// 3. adding "#include " which is needed on some platforms +// to provide memcpy(). +// 4. adding read_end_info() and creating an end_info structure. +// 5. adding calls to png_set_*() transforms commonly used by browsers. + +#include +#include +#include + +#include + +#define PNG_INTERNAL +#include "png.h" + +#define PNG_CLEANUP \ + if(png_handler.png_ptr) \ + { \ + if (png_handler.row_ptr) \ + png_free(png_handler.png_ptr, png_handler.row_ptr); \ + if (png_handler.end_info_ptr) \ + png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ + &png_handler.end_info_ptr); \ + else if (png_handler.info_ptr) \ + png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ + nullptr); \ + else \ + png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ + png_handler.png_ptr = nullptr; \ + png_handler.row_ptr = nullptr; \ + png_handler.info_ptr = nullptr; \ + png_handler.end_info_ptr = nullptr; \ + } + +struct BufState { + const uint8_t* data; + size_t bytes_left; +}; + +struct PngObjectHandler { + png_infop info_ptr = nullptr; + png_structp png_ptr = nullptr; + png_infop end_info_ptr = nullptr; + png_voidp row_ptr = nullptr; + BufState* buf_state = nullptr; + + ~PngObjectHandler() { + if (row_ptr) + png_free(png_ptr, row_ptr); + if (end_info_ptr) + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); + else if (info_ptr) + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + else + png_destroy_read_struct(&png_ptr, nullptr, nullptr); + delete buf_state; + } +}; + +void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { + BufState* buf_state = static_cast(png_get_io_ptr(png_ptr)); + if (length > buf_state->bytes_left) { + png_error(png_ptr, "read error"); + } + memcpy(data, buf_state->data, length); + buf_state->bytes_left -= length; + buf_state->data += length; +} + +static const int kPngHeaderSize = 8; + +extern "C" int afl_libfuzzer_init() { + return 0; +} + +// Entry point for LibFuzzer. +// Roughly follows the libpng book example: +// http://www.libpng.org/pub/png/book/chapter13.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < kPngHeaderSize) { + return 0; + } + + std::vector v(data, data + size); + if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { + // not a PNG. + return 0; + } + + PngObjectHandler png_handler; + png_handler.png_ptr = nullptr; + png_handler.row_ptr = nullptr; + png_handler.info_ptr = nullptr; + png_handler.end_info_ptr = nullptr; + + png_handler.png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_handler.png_ptr) { + return 0; + } + + png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); + if (!png_handler.info_ptr) { + PNG_CLEANUP + return 0; + } + + png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); + if (!png_handler.end_info_ptr) { + PNG_CLEANUP + return 0; + } + + png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); +#ifdef PNG_IGNORE_ADLER32 + png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); +#endif + + // Setting up reading from buffer. + png_handler.buf_state = new BufState(); + png_handler.buf_state->data = data + kPngHeaderSize; + png_handler.buf_state->bytes_left = size - kPngHeaderSize; + png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); + png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); + + if (setjmp(png_jmpbuf(png_handler.png_ptr))) { + PNG_CLEANUP + return 0; + } + + // Reading. + png_read_info(png_handler.png_ptr, png_handler.info_ptr); + + // reset error handler to put png_deleter into scope. + if (setjmp(png_jmpbuf(png_handler.png_ptr))) { + PNG_CLEANUP + return 0; + } + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, compression_type; + int filter_type; + + if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, + &height, &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_type)) { + PNG_CLEANUP + return 0; + } + + // This is going to be too slow. + if (width && height > 100000000 / width) { + PNG_CLEANUP +#ifdef HAS_DUMMY_CRASH + #ifdef __aarch64__ + asm volatile (".word 0xf7f0a000\n"); + #else + asm("ud2"); + #endif +#endif + return 0; + } + + // Set several transforms that browsers typically use: + png_set_gray_to_rgb(png_handler.png_ptr); + png_set_expand(png_handler.png_ptr); + png_set_packing(png_handler.png_ptr); + png_set_scale_16(png_handler.png_ptr); + png_set_tRNS_to_alpha(png_handler.png_ptr); + + int passes = png_set_interlace_handling(png_handler.png_ptr); + + png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); + + png_handler.row_ptr = png_malloc( + png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, + png_handler.info_ptr)); + + for (int pass = 0; pass < passes; ++pass) { + for (png_uint_32 y = 0; y < height; ++y) { + png_read_row(png_handler.png_ptr, + static_cast(png_handler.row_ptr), nullptr); + } + } + + png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); + + PNG_CLEANUP + return 0; +} + diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs new file mode 100644 index 0000000000..dac2564c4e --- /dev/null +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -0,0 +1,524 @@ +//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts +//! The example harness is built for libpng. + +use libafl::{ + bolts::{ + shmem::UnixShMem, + tuples::{tuple_list, Named}, + }, + corpus::{ + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + QueueCorpusScheduler, + }, + events::{setup_restarting_mgr, EventManager}, + executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, + feedbacks::{CrashFeedback, MaxMapFeedback}, + fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, + inputs::{HasTargetBytes, Input}, + mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, HasMetadata, State}, + stats::SimpleStats, + utils::{current_nanos, StdRand}, + Error, +}; + +#[cfg(target_arch = "x86_64")] +use frida_gum::instruction_writer::X86Register; +#[cfg(target_arch = "aarch64")] +use frida_gum::instruction_writer::{Aarch64Register, IndexMode}; +use frida_gum::{ + instruction_writer::InstructionWriter, + stalker::{NoneEventSink, Stalker, Transformer}, +}; +use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection}; + +use libloading; + +use std::{cell::RefCell, env, ffi::c_void, path::PathBuf}; + +/// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation +pub trait FridaHelper<'a> { + fn transformer(&self) -> &Transformer<'a>; +} + +const MAP_SIZE: usize = 64 * 1024; + +/// An helper that feeds FridaInProcessExecutor with edge-coverage instrumentation +struct FridaEdgeCoverageHelper<'a> { + map: [u8; MAP_SIZE], + previous_pc: RefCell, + base_address: u64, + size: usize, + current_log_impl: u64, + /// Transformer that has to be passed to FridaInProcessExecutor + transformer: Option>, +} + +impl<'a> FridaHelper<'a> for FridaEdgeCoverageHelper<'a> { + fn transformer(&self) -> &Transformer<'a> { + self.transformer.as_ref().unwrap() + } +} + +/// Helper function to get the size of a module's CODE section from frida +pub fn get_module_size(module_name: &str) -> usize { + let mut code_size = 0; + let code_size_ref = &mut code_size; + Module::enumerate_ranges(module_name, PageProtection::ReadExecute, move |details| { + *code_size_ref = details.memory_range().size() as usize; + true + }); + + code_size +} + +/// A minimal maybe_log implementation. We insert this into the transformed instruction stream +/// every time we need a copy that is within a direct branch of the start of the transformed basic +/// block. +#[cfg(target_arch = "x86_64")] +const MAYBE_LOG_CODE: [u8; 69] = [ + 0x9c, // pushfq + 0x50, // push rax + 0x51, // push rcx + 0x52, // push rdx + 0x56, // push rsi + 0x89, 0xf8, // mov eax, edi + 0xc1, 0xe0, 0x08, // shl eax, 8 + 0xc1, 0xef, 0x04, // shr edi, 4 + 0x31, 0xc7, // xor edi, eax + 0x0f, 0xb7, 0xc7, // movzx eax, di + 0x48, 0x8d, 0x0d, 0x34, 0x00, 0x00, 0x00, // lea rcx, sym._afl_area_ptr_ptr + 0x48, 0x8b, 0x09, // mov rcx, qword [rcx] + 0x48, 0x8d, 0x15, 0x22, 0x00, 0x00, 0x00, // lea rdx, sym._afl_prev_loc_ptr + 0x48, 0x8b, 0x32, // mov rsi, qword [rdx] + 0x48, 0x8b, 0x36, // mov rsi, qword [rsi] + 0x48, 0x31, 0xc6, // xor rsi, rax + 0x48, 0x81, 0xe6, 0xff, 0x1f, 0x00, + 0x00, // and rsi, 0x1fff (8 * 1024 - 1) TODO: make this variable + 0xfe, 0x04, 0x31, // inc byte [rcx + rsi] + 0x48, 0xd1, 0xe8, // shr rax, 1 + 0x48, 0x8b, 0x0a, // mov rcx, qword [rdx] + 0x48, 0x89, 0x01, // mov qword [rcx], rax + 0x5e, // pop rsi + 0x5a, // pop rdx + 0x59, // pop rcx + 0x58, // pop rax + 0x9d, // popfq + 0xc3, // ret + // Read-only data goes here: + // uint64_t* afl_prev_loc_ptr + // uint8_t** afl_area_ptr_ptr + // unsigned int afl_instr_rms +]; + +#[cfg(target_arch = "aarch64")] +const MAYBE_LOG_CODE: [u8; 104] = [ + 0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]! + 0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]! + 0xE1, 0x03, 0x00, 0xAA, // mov x1, x0 + 0x00, 0xDC, 0x78, 0xD3, // lsl x0, x0, #8 + 0x21, 0xFC, 0x44, 0xD3, // lsr x1, x1, #4 + 0x00, 0x00, 0x01, 0xCA, // eor x0, x0, x1 + 0x00, 0x3C, 0x00, 0x53, // uxth w0, w0 + 0xa1, 0x02, 0x00, 0x58, // ldr x1, =area_ptr + 0x42, 0x02, 0x00, 0x58, // ldr x2, =pc_ptr + 0x43, 0x00, 0x40, 0xF9, // ldr x3, [x2] + 0x63, 0x00, 0x00, 0xCA, // eor x3, x3, x0 + 0x63, 0x40, 0x40, 0x92, // and x3, x3, #0x1ffff + 0x21, 0x00, 0x03, 0x8B, // add x1, x1, x3 + 0x24, 0x00, 0x40, 0x39, // ldrb w4, [x1, #0 + 0x84, 0x04, 0x00, 0x91, // add x4, x4, #1 + 0x24, 0x00, 0x00, 0x39, // strb w4, [x1, #0] + 0x00, 0xFC, 0x41, 0xD3, // lsr x0, x0, #1 + 0x40, 0x00, 0x00, 0xF9, // str x0, [x2] + 0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10 + 0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10 + 0xC0, 0x03, 0x5F, 0xD6, // ret + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop +]; + +/// The implementation of the FridaEdgeCoverageHelper +impl<'a> FridaEdgeCoverageHelper<'a> { + /// Constructor function to create a new FridaEdgeCoverageHelper, given a module_name. + pub fn new(gum: &'a Gum, module_name: &str) -> Self { + let mut helper = Self { + map: [0u8; MAP_SIZE], + previous_pc: RefCell::new(0x0), + base_address: Module::find_base_address(module_name).0 as u64, + size: get_module_size(module_name), + current_log_impl: 0, + transformer: None, + }; + + let transformer = Transformer::from_callback(gum, |basic_block, _output| { + let mut first = true; + for instruction in basic_block { + if first { + first = false; + let address = unsafe { (*instruction.instr()).address }; + if address >= helper.base_address + && address <= helper.base_address + helper.size as u64 + { + let writer = _output.writer(); + if helper.current_log_impl == 0 + || !writer.can_branch_directly_to(helper.current_log_impl) + || !writer.can_branch_directly_between( + writer.pc() + 128, + helper.current_log_impl, + ) + { + let after_log_impl = writer.code_offset() + 1; + + #[cfg(target_arch = "x86_64")] + writer.put_jmp_near_label(after_log_impl); + #[cfg(target_arch = "aarch64")] + writer.put_b_label(after_log_impl); + + helper.current_log_impl = writer.pc(); + writer.put_bytes(&MAYBE_LOG_CODE); + let prev_loc_pointer = helper.previous_pc.as_ptr() as *mut _ as usize; + let map_pointer = helper.map.as_ptr() as usize; + + writer.put_bytes(&prev_loc_pointer.to_ne_bytes()); + writer.put_bytes(&map_pointer.to_ne_bytes()); + + writer.put_label(after_log_impl); + } + #[cfg(target_arch = "x86_64")] + { + println!("here"); + writer.put_lea_reg_reg_offset( + X86Register::Rsp, + X86Register::Rsp, + -(frida_gum_sys::GUM_RED_ZONE_SIZE as i32), + ); + writer.put_push_reg(X86Register::Rdi); + writer.put_mov_reg_address(X86Register::Rdi, address); + writer.put_call_address(helper.current_log_impl); + writer.put_pop_reg(X86Register::Rdi); + writer.put_lea_reg_reg_offset( + X86Register::Rsp, + X86Register::Rsp, + frida_gum_sys::GUM_RED_ZONE_SIZE as i32, + ); + } + #[cfg(target_arch = "aarch64")] + { + writer.put_stp_reg_reg_reg_offset( + Aarch64Register::Lr, + Aarch64Register::X0, + Aarch64Register::Sp, + -(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64, + IndexMode::PreAdjust, + ); + writer.put_ldr_reg_u64(Aarch64Register::X0, address); + writer.put_bl_imm(helper.current_log_impl); + writer.put_ldp_reg_reg_reg_offset( + Aarch64Register::Lr, + Aarch64Register::X0, + Aarch64Register::Sp, + 16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i64, + IndexMode::PostAdjust, + ); + } + } + } + instruction.keep() + } + }); + + helper.transformer = Some(transformer); + helper + } +} + +struct FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + base: InProcessExecutor<'a, H, I, OT>, + /// Frida's dynamic rewriting engine + stalker: Stalker<'a>, + /// User provided callback for instrumentation + helper: &'a FH, + followed: bool, +} + +impl<'a, FH, H, I, OT> Executor for FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + /// Called right before exexution starts + #[inline] + fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> + where + EM: EventManager, + { + if !self.followed { + self.followed = true; + self.stalker + .follow_me::(self.helper.transformer(), None); + } else { + self.stalker.activate(NativePointer( + self.base.harness_mut() as *mut _ as *mut c_void + )) + } + self.base.pre_exec(state, event_mgr, input) + } + + /// Instruct the target about the input and run + #[inline] + fn run_target(&mut self, input: &I) -> Result { + self.base.run_target(input) + } + + /// Called right after execution finished. + #[inline] + fn post_exec( + &mut self, + state: &mut S, + event_mgr: &mut EM, + input: &I, + ) -> Result<(), Error> + where + EM: EventManager, + { + self.stalker.deactivate(); + self.base.post_exec(state, event_mgr, input) + } +} + +impl<'a, FH, H, I, OT> HasObservers for FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + #[inline] + fn observers(&self) -> &OT { + self.base.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.base.observers_mut() + } +} + +impl<'a, FH, H, I, OT> Named for FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + fn name(&self) -> &str { + self.base.name() + } +} + +impl<'a, FH, H, I, OT> FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT>, helper: &'a FH) -> Self { + let mut stalker = Stalker::new(gum); + + // Let's exclude the main module and libc.so at least: + stalker.exclude(&MemoryRange::new( + Module::find_base_address(&env::args().next().unwrap()), + get_module_size(&env::args().next().unwrap()), + )); + stalker.exclude(&MemoryRange::new( + Module::find_base_address("libc.so"), + get_module_size("libc.so"), + )); + + Self { + base: base, + stalker: stalker, + helper: helper, + followed: false, + } + } +} + +/// The main fn, usually parsing parameters, and starting the fuzzer +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + unsafe { + fuzz( + &env::args().nth(1).expect("no module specified"), + &env::args().nth(2).expect("no symbol specified"), + vec![PathBuf::from("./corpus")], + PathBuf::from("./crashes"), + 1337, + ) + .expect("An error occurred while fuzzing"); + } +} + +/// Not supported on windows right now +#[cfg(windows)] +fn fuzz( + _module_name: &str, + _symbol_name: &str, + _corpus_dirs: Vec, + _objective_dir: PathBuf, + _broker_port: u16, +) -> Result<(), ()> { + todo!("Example not supported on Windows"); +} + +/// The actual fuzzer +#[cfg(unix)] +unsafe fn fuzz( + module_name: &str, + symbol_name: &str, + corpus_dirs: Vec, + objective_dir: PathBuf, + broker_port: u16, +) -> Result<(), Error> { + // 'While the stats are state, they are usually used in the broker - which is likely never restarted + let stats = SimpleStats::new(|s| println!("{}", s)); + + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + let (state, mut restarting_mgr) = + match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { + Ok(res) => res, + Err(err) => match err { + Error::ShuttingDown => { + return Ok(()); + } + _ => { + panic!("Failed to setup the restarter: {}", err); + } + }, + }; + + let gum = Gum::obtain(); + let lib = libloading::Library::new(module_name).unwrap(); + let target_func: libloading::Symbol i32> = + lib.get(symbol_name.as_bytes()).unwrap(); + let mut frida_helper = FridaEdgeCoverageHelper::new(&gum, module_name); + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr( + "edges", + frida_helper.map.as_mut_ptr(), + MAP_SIZE, + )); + + let mut frida_harness = move |buf: &[u8]| { + (target_func)(buf.as_ptr(), buf.len()); + ExitKind::Ok + }; + + // If not restarting, create a State from scratch + let mut state = state.unwrap_or_else(|| { + State::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Feedbacks to rate the interestingness of an input + tuple_list!(MaxMapFeedback::new_with_observer_track( + &edges_observer, + true, + false + )), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(objective_dir).unwrap(), + // Feedbacks to recognize an input as solution + tuple_list!(CrashFeedback::new()), + ) + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().is_none() { + state.add_metadata(Tokens::new(vec![ + vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header + "IHDR".as_bytes().to_vec(), + "IDAT".as_bytes().to_vec(), + "PLTE".as_bytes().to_vec(), + "IEND".as_bytes().to_vec(), + ])); + } + + // Setup a basic mutator with a mutational stage + let mutator = HavocBytesMutator::default(); + let stage = StdMutationalStage::new(mutator); + + // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + + // Create the executor for an in-process function with just one observer for edge coverage + let mut executor = FridaInProcessExecutor::new( + &gum, + InProcessExecutor::new( + "in-process(edges)", + &mut frida_harness, + tuple_list!(edges_observer), + &mut state, + &mut restarting_mgr, + )?, + &frida_helper, + ); + // Let's exclude the main module and libc.so at least: + executor.stalker.exclude(&MemoryRange::new( + Module::find_base_address(&env::args().next().unwrap()), + get_module_size(&env::args().next().unwrap()), + )); + executor.stalker.exclude(&MemoryRange::new( + Module::find_base_address("libc.so"), + get_module_size("libc.so"), + )); + + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + state + .load_initial_inputs( + &mut executor, + &mut restarting_mgr, + fuzzer.scheduler(), + &corpus_dirs, + ) + .expect(&format!( + "Failed to load initial corpus at {:?}", + &corpus_dirs + )); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + + // Never reached + Ok(()) +} diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs index ffbbcdfa78..cafe05bc1b 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs @@ -36,20 +36,6 @@ extern "C" { static __lafl_max_edges_size: u32; } -/// The wrapped harness function, calling out to the LLVM-style harness -#[cfg(unix)] -fn harness(_executor: &E, buf: &[u8]) -> ExitKind -where - E: Executor, - I: Input, -{ - // println!("{:?}", buf); - unsafe { - LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); - } - ExitKind::Ok -} - /// The main fn, usually parsing parameters, and starting the fuzzer pub fn main() { // Registry the metadata types used in this fuzzer @@ -121,10 +107,16 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // A fuzzer with just one stage and a random policy to get testcasess from the corpus let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage)); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( "in-process(edges)", - harness, + &mut harness, tuple_list!(edges_observer), &mut state, &mut restarting_mgr, diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 9bd2febdc3..32531944b5 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -12,10 +12,9 @@ use libafl::{ QueueCorpusScheduler, }, events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, Executor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - inputs::Input, mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, @@ -39,20 +38,6 @@ extern "C" { static __lafl_max_edges_size: u32; } -/// The wrapped harness function, calling out to the LLVM-style harness -#[cfg(unix)] -fn harness(_executor: &E, buf: &[u8]) -> ExitKind -where - E: Executor, - I: Input, -{ - // println!("{:?}", buf); - unsafe { - LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); - } - ExitKind::Ok -} - /// The main fn, usually parsing parameters, and starting the fuzzer pub fn main() { // Registry the metadata types used in this fuzzer @@ -143,11 +128,17 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + // Create the executor for an in-process function with just one observer for edge coverage let mut executor = TimeoutExecutor::new( InProcessExecutor::new( "in-process(edges)", - harness, + &mut harness, tuple_list!(edges_observer, TimeObserver::new("time")), &mut state, &mut restarting_mgr, diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs index 58215afc63..c4f26cabb4 100644 --- a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs @@ -11,10 +11,9 @@ use libafl::{ QueueCorpusScheduler, }, events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, Executor, ExitKind}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - inputs::Input, mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, @@ -41,20 +40,6 @@ extern "C" { static __lafl_max_edges_size: u32; } -/// The wrapped harness function, calling out to the LLVM-style harness -#[cfg(unix)] -fn harness(_executor: &E, buf: &[u8]) -> ExitKind -where - E: Executor, - I: Input, -{ - // println!("{:?}", buf); - unsafe { - LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); - } - ExitKind::Ok -} - /// The main fn, usually parsing parameters, and starting the fuzzer pub fn main() { // Registry the metadata types used in this fuzzer @@ -154,10 +139,16 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( "in-process(edges,cmps,allocs)", - harness, + &mut harness, tuple_list!( edges_observer, cmps_observer, diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index fa42f0da9a..0071f194a0 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -25,26 +25,25 @@ use crate::{ Error, }; -/// The inmem executor harness -type HarnessFunction = fn(&E, &[u8]) -> ExitKind; - /// The inmem executor simply calls a target function, then returns afterwards. -pub struct InProcessExecutor +pub struct InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { /// The name of this executor instance, to address it from other components name: &'static str, /// The harness function, being executed for each fuzzing loop execution - harness_fn: HarnessFunction, + harness_fn: &'a mut H, /// The observers, observing each run observers: OT, phantom: PhantomData, } -impl Executor for InProcessExecutor +impl<'a, H, I, OT> Executor for InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -95,7 +94,7 @@ where #[inline] fn run_target(&mut self, input: &I) -> Result { let bytes = input.target_bytes(); - let ret = (self.harness_fn)(self, bytes.as_slice()); + let ret = (self.harness_fn)(bytes.as_slice()); Ok(ret) } @@ -126,8 +125,9 @@ where } } -impl Named for InProcessExecutor +impl<'a, H, I, OT> Named for InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -136,8 +136,9 @@ where } } -impl HasObservers for InProcessExecutor +impl<'a, H, I, OT> HasObservers for InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -152,8 +153,9 @@ where } } -impl InProcessExecutor +impl<'a, H, I, OT> InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -166,7 +168,7 @@ where /// This may return an error on unix, if signal handler setup fails pub fn new( name: &'static str, - harness_fn: HarnessFunction, + harness_fn: &'a mut H, observers: OT, _state: &mut S, _event_mgr: &mut EM, @@ -215,6 +217,18 @@ where phantom: PhantomData, }) } + + /// Retrieve the harness function. + #[inline] + pub fn harness(&self) -> &H { + self.harness_fn + } + + /// Retrieve the harness function for a mutable reference. + #[inline] + pub fn harness_mut(&mut self) -> &mut H { + self.harness_fn + } } #[cfg(unix)] @@ -627,19 +641,15 @@ mod tests { use crate::{ bolts::tuples::tuple_list, executors::{Executor, ExitKind, InProcessExecutor}, - inputs::Input, + inputs::NopInput, }; - fn test_harness_fn_nop, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind { - ExitKind::Ok - } - #[test] fn test_inmem_exec() { - use crate::inputs::NopInput; + let mut harness = |_buf: &[u8]| ExitKind::Ok; - let mut in_process_executor = InProcessExecutor:: { - harness_fn: test_harness_fn_nop, + let mut in_process_executor = InProcessExecutor::<_, NopInput, ()> { + harness_fn: &mut harness, observers: tuple_list!(), name: "main", phantom: PhantomData, diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 55170850ad..c58307a045 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -137,8 +137,8 @@ mod tests { use crate::{ bolts::tuples::tuple_list, corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase}, - executors::{Executor, ExitKind, InProcessExecutor}, - inputs::{BytesInput, Input}, + executors::{ExitKind, InProcessExecutor}, + inputs::BytesInput, mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator}, stages::StdMutationalStage, state::{HasCorpus, State}, @@ -150,10 +150,6 @@ mod tests { #[cfg(feature = "std")] use crate::events::SimpleEventManager; - fn harness, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind { - ExitKind::Ok - } - #[test] fn test_fuzzer() { let rand = StdRand::with_seed(0); @@ -175,9 +171,10 @@ mod tests { }); let mut event_manager = SimpleEventManager::new(stats); + let mut harness = |_buf: &[u8]| ExitKind::Ok; let mut executor = InProcessExecutor::new( "main", - harness, + &mut harness, tuple_list!(), //Box::new(|_, _, _, _, _| ()), &mut state, diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 141f75f02d..825a62b8b9 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -126,6 +126,16 @@ where } } + /// Creates a new MapObserver with an owned map + pub fn new_owned(name: &'static str, map: Vec) -> Self { + let initial = if map.is_empty() { T::default() } else { map[0] }; + Self { + map: ArrayMut::Owned(map), + name: name.to_string(), + initial, + } + } + /// Creates a new MapObserver from a raw pointer /// # Safety /// Will dereference the map_ptr with up to len elements. From 013dc85328e11fa0836d653ed8741c84bf1d843f Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 14:04:34 +0100 Subject: [PATCH 004/104] download to out_dir --- fuzzers/libfuzzer_libmozjpeg/build.rs | 4 ++-- fuzzers/libfuzzer_libpng_cmpalloc/build.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fuzzers/libfuzzer_libmozjpeg/build.rs b/fuzzers/libfuzzer_libmozjpeg/build.rs index 96f09a9db0..a3ced5b2bb 100644 --- a/fuzzers/libfuzzer_libmozjpeg/build.rs +++ b/fuzzers/libfuzzer_libmozjpeg/build.rs @@ -15,7 +15,7 @@ fn main() { } let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + //let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string(); let out_dir_path = Path::new(&out_dir); @@ -24,7 +24,7 @@ fn main() { let libmozjpeg = format!("{}/mozjpeg-4.0.3", &out_dir); let libmozjpeg_path = Path::new(&libmozjpeg); - let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &cwd); + let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &out_dir); // Enforce clang for its -fsanitize-coverage support. std::env::set_var("CC", "clang"); diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/build.rs b/fuzzers/libfuzzer_libpng_cmpalloc/build.rs index 49f3cfba94..45dcf2ce58 100644 --- a/fuzzers/libfuzzer_libpng_cmpalloc/build.rs +++ b/fuzzers/libfuzzer_libpng_cmpalloc/build.rs @@ -16,7 +16,6 @@ fn main() { } let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string(); let out_dir_path = Path::new(&out_dir); @@ -25,7 +24,7 @@ fn main() { let libpng = format!("{}/libpng-1.6.37", &out_dir); let libpng_path = Path::new(&libpng); - let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); + let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &out_dir); // Enforce clang for its -fsanitize-coverage support. std::env::set_var("CC", "clang"); From 11686b16674184af8d8eacdaa71c9fe04dd48b8e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 15:04:56 +0100 Subject: [PATCH 005/104] fixed libfuzzer_libmozjpeg build --- fuzzers/libfuzzer_libmozjpeg/build.rs | 23 +++++++++++----------- fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs | 3 +-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fuzzers/libfuzzer_libmozjpeg/build.rs b/fuzzers/libfuzzer_libmozjpeg/build.rs index a3ced5b2bb..e71dafcc01 100644 --- a/fuzzers/libfuzzer_libmozjpeg/build.rs +++ b/fuzzers/libfuzzer_libmozjpeg/build.rs @@ -48,16 +48,13 @@ fn main() { .arg(&libmozjpeg_tar) .status() .unwrap(); - Command::new(format!("{}/cmake", &libmozjpeg)) - .current_dir(&out_dir_path) - .args(&[ - "-G\"Unix Makefiles\"", - "--disable-shared", - &libmozjpeg, - "CC=clang", - "CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - "LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard", - ]) + //println!("cargo:warning=Running cmake on {}", &libmozjpeg); + + Command::new("cmake") + .current_dir(&libmozjpeg_path) + .args(&["-G", "Unix Makefiles", "--disable-shared"]) + .arg(&libmozjpeg) + .env("OPT_LEVEL", "3") .env("CC", "clang") .env("CXX", "clang++") .env( @@ -68,13 +65,17 @@ fn main() { "CXXFLAGS", "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", ) - .env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard"); + .env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard") + .status() + .unwrap(); + Command::new("make") .current_dir(&libmozjpeg_path) //.arg(&format!("-j{}", num_cpus::get())) .args(&[ "CC=clang", "CXX=clang++", + "OPT_LEVEL=3", "CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", "LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard", "CXXFLAGS=-D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs index cafe05bc1b..4634c7fd31 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs @@ -8,10 +8,9 @@ use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, Executor, ExitKind}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - inputs::Input, mutators::scheduled::HavocBytesMutator, mutators::token_mutations::Tokens, observers::StdMapObserver, From 33654dbf0493badf4f3e398a331ca1eaa91dfb45 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 15:09:47 +0100 Subject: [PATCH 006/104] fixed no_std --- libafl/src/bolts/llmp.rs | 4 ++-- libafl/src/fuzzer.rs | 4 ++-- libafl/src/mutators/token_mutations.rs | 1 + libafl/src/observers/map.rs | 5 ++++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 054bc2c3c9..f49f78a635 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -89,10 +89,10 @@ use std::{ }, }; -#[cfg(all(feature = "std", unix))] +#[cfg(all(unix, feature = "std"))] use libc::c_char; -#[cfg(unix)] +#[cfg(all(unix, feature = "std"))] use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; #[cfg(unix)] diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index d1c34324c1..2bddff0b5a 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -10,8 +10,8 @@ use crate::{ Error, }; +use alloc::string::ToString; use core::{marker::PhantomData, time::Duration}; -use std::ops::Sub; /// Send a stats update all 6 (or more) seconds const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(6 * 1000); @@ -161,7 +161,7 @@ where stats_timeout: Duration, ) -> Result { let cur = current_time(); - if cur.sub(last) > stats_timeout { + if cur - last > stats_timeout { //println!("Fire {:?} {:?} {:?}", cur, last, stats_timeout); manager.fire( state, diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 90741b2f97..715bac7f35 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -206,6 +206,7 @@ mod tests { #[cfg(feature = "std")] use std::fs; + #[cfg(feature = "std")] use super::Tokens; #[cfg(feature = "std")] diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 825a62b8b9..85d5d2d6be 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -1,4 +1,7 @@ -use alloc::string::{String, ToString}; +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; use serde::{Deserialize, Serialize}; use crate::{ From c328a27ebd30faffa31957d57c64a751ef5fde2b Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 16:16:18 +0100 Subject: [PATCH 007/104] ci on dev --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index a6fd669bf6..ab248a70ac 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -2,7 +2,7 @@ name: Build and Test on: push: - branches: [ main ] + branches: [ main, dev ] pull_request: branches: [ main, dev ] From 556141e9a3fe27f533e82dede9c6fb4bd1ac7cd6 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 16:54:31 +0100 Subject: [PATCH 008/104] ignored frida on windows --- fuzzers/frida_libpng/Cargo.toml | 4 ++-- fuzzers/frida_libpng/build.rs | 2 +- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 1 + fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs | 1 + libafl/src/bolts/os/windows_exceptions.rs | 2 +- libafl/src/executors/mod.rs | 1 + libafl/src/executors/timeout.rs | 7 +++++++ 7 files changed, 14 insertions(+), 4 deletions(-) diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 347724ca56..2fd07e7c74 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -22,7 +22,7 @@ frida = ["frida-gum", "frida-gum-sys"] cc = { version = "1.0", features = ["parallel"] } num_cpus = "1.0" -[dependencies] +[target.'cfg(unix)'.dependencies] libafl = { path = "../../libafl/" } #frida-gum = { path = "../../../frida-rust/frida-gum", version = "0.2.3", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } #frida-gum-sys = { path = "../../../frida-rust/frida-gum-sys", version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } @@ -34,6 +34,6 @@ libloading = "0.7.0" [[example]] name = "frida_libpng" -path = "./src/fuzzer.rs" +path = "./src/main.rs" test = false bench = false diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs index 94e44cd8f4..007a54139d 100644 --- a/fuzzers/frida_libpng/build.rs +++ b/fuzzers/frida_libpng/build.rs @@ -11,7 +11,7 @@ const LIBPNG_URL: &str = fn main() { if cfg!(windows) { - println!("cargo:warning=Skipping libpng example on Windows"); + println!("cargo:warning=Skipping libpng frida example on Windows"); exit(0); } diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 32531944b5..163a4c43ed 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -1,6 +1,7 @@ //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! The example harness is built for libpng. +#[cfg(unix)] use core::time::Duration; use std::{env, path::PathBuf}; diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs index c4f26cabb4..c78a6d88d4 100644 --- a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs @@ -23,6 +23,7 @@ use libafl::{ Error, }; +#[cfg(unix)] const MAP_SIZE: usize = 16 * 1024; /// We will interact with a C++ target, so use external c functionality diff --git a/libafl/src/bolts/os/windows_exceptions.rs b/libafl/src/bolts/os/windows_exceptions.rs index ed43a04c00..d4796f0397 100644 --- a/libafl/src/bolts/os/windows_exceptions.rs +++ b/libafl/src/bolts/os/windows_exceptions.rs @@ -14,7 +14,7 @@ use std::os::raw::{c_long, c_void}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; +//const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; //const EXCEPTION_CONTINUE_SEARCH: c_long = 0; const EXCEPTION_EXECUTE_HANDLER: c_long = 1; diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index a5ee692469..705b317d33 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -3,6 +3,7 @@ pub mod inprocess; pub use inprocess::InProcessExecutor; pub mod timeout; +#[cfg(unix)] pub use timeout::TimeoutExecutor; #[cfg(feature = "runtime")] pub mod runtime; diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index 6acb753032..dd88613455 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -1,7 +1,9 @@ //! A TimeoutExecutor set a timeout before each target run +#[cfg(unix)] use core::{marker::PhantomData, time::Duration}; +#[cfg(unix)] use crate::{ bolts::tuples::Named, events::EventManager, @@ -39,6 +41,7 @@ extern "C" { const ITIMER_REAL: c_int = 0; /// The timeout excutor is a wrapper that set a timeout before each run +#[cfg(unix)] pub struct TimeoutExecutor where E: Executor + HasObservers, @@ -50,6 +53,7 @@ where phantom: PhantomData<(I, OT)>, } +#[cfg(unix)] impl Named for TimeoutExecutor where E: Executor + HasObservers, @@ -61,6 +65,7 @@ where } } +#[cfg(unix)] impl HasObservers for TimeoutExecutor where E: Executor + HasObservers, @@ -78,6 +83,7 @@ where } } +#[cfg(unix)] impl TimeoutExecutor where E: Executor + HasObservers, @@ -93,6 +99,7 @@ where } } +#[cfg(unix)] impl Executor for TimeoutExecutor where E: Executor + HasObservers, From 92dd0bfcb2fc7bec5a843093715c1a1724788f1d Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 16:58:53 +0100 Subject: [PATCH 009/104] added main.rs wrapper for frida --- fuzzers/frida_libpng/src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 fuzzers/frida_libpng/src/main.rs diff --git a/fuzzers/frida_libpng/src/main.rs b/fuzzers/frida_libpng/src/main.rs new file mode 100644 index 0000000000..54fa1969bc --- /dev/null +++ b/fuzzers/frida_libpng/src/main.rs @@ -0,0 +1,6 @@ +#[cfg(unix)] +pub use fuzzer::main; +#[cfg(not(unix))] +pub fn main() { + todo!("Frida not yet supported on this OS."); +} From 5f74a08316b8db88202047849001c7c6fc3433c8 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 17:02:14 +0100 Subject: [PATCH 010/104] fix frida wrapper --- fuzzers/frida_libpng/src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fuzzers/frida_libpng/src/main.rs b/fuzzers/frida_libpng/src/main.rs index 54fa1969bc..ae199821eb 100644 --- a/fuzzers/frida_libpng/src/main.rs +++ b/fuzzers/frida_libpng/src/main.rs @@ -1,5 +1,9 @@ #[cfg(unix)] -pub use fuzzer::main; +mod fuzzer; +#[cfg(unix)] +pub fn main() { + fuzzer::main(); +} #[cfg(not(unix))] pub fn main() { todo!("Frida not yet supported on this OS."); From 61a89f4aa6ea4a1d3bc64874c59760b6efc644bd Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 22 Mar 2021 18:29:46 +0100 Subject: [PATCH 011/104] skeleton for libafl_cc --- Cargo.toml | 1 + libafl_cc/Cargo.toml | 9 +++++++++ libafl_cc/src/bin/libafl-cc.rs | 3 +++ libafl_cc/src/lib.rs | 9 +++++++++ libafl_cc/src/runtime.rs | 23 +++++++++++++++++++++++ 5 files changed, 45 insertions(+) create mode 100644 libafl_cc/Cargo.toml create mode 100644 libafl_cc/src/bin/libafl-cc.rs create mode 100644 libafl_cc/src/lib.rs create mode 100644 libafl_cc/src/runtime.rs diff --git a/Cargo.toml b/Cargo.toml index 98347cc3da..d1037a8bc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ debug = true members = [ "libafl", "libafl_derive", + "libafl_cc", #example fuzzers "fuzzers/libfuzzer_libpng", diff --git a/libafl_cc/Cargo.toml b/libafl_cc/Cargo.toml new file mode 100644 index 0000000000..237f040e2c --- /dev/null +++ b/libafl_cc/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "libafl_cc" +version = "0.1.0" +authors = ["Andrea Fioraldi "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/libafl_cc/src/bin/libafl-cc.rs b/libafl_cc/src/bin/libafl-cc.rs new file mode 100644 index 0000000000..f3e95c9cc1 --- /dev/null +++ b/libafl_cc/src/bin/libafl-cc.rs @@ -0,0 +1,3 @@ +fn main() { + todo!("libafl-cc"); +} diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs new file mode 100644 index 0000000000..005323f7bb --- /dev/null +++ b/libafl_cc/src/lib.rs @@ -0,0 +1,9 @@ +pub mod runtime; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/libafl_cc/src/runtime.rs b/libafl_cc/src/runtime.rs new file mode 100644 index 0000000000..9a2cc92586 --- /dev/null +++ b/libafl_cc/src/runtime.rs @@ -0,0 +1,23 @@ +pub const MAP_SIZE: usize = 65536; + +pub static mut EDGES_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut MAX_EDGES_NUM: usize = 0; + +#[no_mangle] +pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { + let pos = *guard as usize; + let val = (EDGES_MAP[pos] as u8).wrapping_add(1); + EDGES_MAP[pos] = val; +} + +#[no_mangle] +pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { + if start == stop || *start != 0 { return } + + while start < stop { + MAX_EDGES_NUM += 1; + *start = (MAX_EDGES_NUM & (MAP_SIZE -1)) as u32; + start = start.offset(1); + } +} From 009c35dbf916b18b93ec883ae82c81ba45c1ea90 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 22 Mar 2021 20:22:42 +0100 Subject: [PATCH 012/104] docs folder --- docs/.gitignore | 1 + docs/README.md | 13 +++++++++++++ docs/book.toml | 6 ++++++ docs/src/SUMMARY.md | 3 +++ docs/src/chapter_1.md | 1 + 5 files changed, 24 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/README.md create mode 100644 docs/book.toml create mode 100644 docs/src/SUMMARY.md create mode 100644 docs/src/chapter_1.md diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000000..7585238efe --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..8e379b7b28 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,13 @@ +# LibAFL Documentation + +This project contains the out-of-source LibAFL documentation. + +Here you can find tutorials, examples and detailed explanations. + +For the API documentation instead, run `cargo doc` in the LibAFl root folder. + +## Usage + +To build this book, you need [mdBook](https://github.com/rust-lang/mdBook). + +`mdbook build` to build, `mdbook serve` to serve the book locally. diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000000..68f4a19788 --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["Andrea Fioraldi"] +language = "en" +multilingual = false +src = "src" +title = "LibAFL Documentation" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000000..7390c82896 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +- [Chapter 1](./chapter_1.md) diff --git a/docs/src/chapter_1.md b/docs/src/chapter_1.md new file mode 100644 index 0000000000..b743fda354 --- /dev/null +++ b/docs/src/chapter_1.md @@ -0,0 +1 @@ +# Chapter 1 From 19133f5827288a56ec23bed3e3f074aa8a5bdd0b Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 23 Mar 2021 12:17:28 +0100 Subject: [PATCH 013/104] clang wrapper skeleton --- libafl_cc/src/lib.rs | 130 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 005323f7bb..7c92da60ee 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -1,9 +1,133 @@ -pub mod runtime; +use std::{string::String, vec::Vec}; + +#[derive(Debug)] +pub enum Error { + InvalidArguments(String), + Unknown(String), +} + +/// Wrap a compiler hijacking its arguments +pub trait CompilerWrapper { + /// Set the wrapper arguments parsing a command line set of arguments + fn from_args<'a>(&'a mut self, args: Vec) -> Result<&'a mut Self, Error>; + + /// Add a compiler argument + fn add_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error>; + + /// Run the compiler + fn compile(&mut self) -> Result<(), Error>; +} + +/// Wrap Clang +pub struct ClangWrapper { + optimize: bool, + wrapped_cc: String, + wrapped_cxx: String, + + name: String, + is_cpp: bool, + linking: bool, + x_set: bool, + bit_mode: u32, + + args: Vec, +} + +impl CompilerWrapper for ClangWrapper { + fn from_args<'a>(&'a mut self, args: Vec) -> Result<&'a mut Self, Error> { + let mut new_args = vec![]; + if args.len() < 1 { + return Err(Error::InvalidArguments( + "The number of arguments cannot be 0".into(), + )); + } + + self.name = args[0].clone(); + // Detect C++ compiler looking at the wrapper name + self.is_cpp = self.name.ends_with("++"); + if self.is_cpp { + new_args.push(self.wrapped_cxx.clone()); + } else { + new_args.push(self.wrapped_cc.clone()); + } + + // Sancov flag + // new_args.push("-fsanitize-coverage=trace-pc-guard".into()); + + let mut linking = true; + // Detect stray -v calls from ./configure scripts. + if args.len() == 1 && args[1] == "-v" { + linking = false; + } + + for arg in &args[1..] { + match arg.as_str() { + "-x" => self.x_set = true, + "-m32" => self.bit_mode = 32, + "-m64" => self.bit_mode = 64, + "-c" | "-S" | "-E" => linking = false, + "-shared" => linking = false, // TODO dynamic list? + "-Wl,-z,defs" | "-Wl,--no-undefined" => continue, + _ => (), + }; + new_args.push(arg.clone()); + } + self.linking = linking; + + if self.optimize { + new_args.push("-g".into()); + new_args.push("-O3".into()); + new_args.push("-funroll-loops".into()); + } + + // Fuzzing define common among tools + new_args.push("-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1".into()); + + self.args = new_args; + Ok(self) + } + + fn add_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error> { + self.args.push(arg); + Ok(self) + } + + fn compile(&mut self) -> Result<(), Error> { + if self.linking { + if self.x_set { + self.args.push("-x".into()); + self.args.push("none".into()); + } + } + + Ok(()) + } +} + +impl ClangWrapper { + pub fn new(wrapped_cc: &str, wrapped_cxx: &str) -> Self { + Self { + optimize: true, + wrapped_cc: wrapped_cc.into(), + wrapped_cxx: wrapped_cxx.into(), + name: "".into(), + is_cpp: false, + linking: false, + x_set: false, + bit_mode: 0, + args: vec![], + } + } +} #[cfg(test)] mod tests { #[test] - fn it_works() { - assert_eq!(2 + 2, 4); + fn test_clang_version() { + ClangWrapper::new("clang", "clang++") + .from_args(vec!["my-clang", "-v"]) + .unwrap() + .compile() + .unwrap(); } } From 0a3b9f1a96f39e2d5c1f63d3e89cec79f54a83c8 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 23 Mar 2021 12:20:36 +0100 Subject: [PATCH 014/104] fix libafl-cc test --- libafl_cc/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 7c92da60ee..7c58bcbea9 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -100,6 +100,8 @@ impl CompilerWrapper for ClangWrapper { } } + println!("{:?}", self.args); + Ok(()) } } @@ -122,10 +124,12 @@ impl ClangWrapper { #[cfg(test)] mod tests { + use crate::{ClangWrapper, CompilerWrapper}; + #[test] fn test_clang_version() { ClangWrapper::new("clang", "clang++") - .from_args(vec!["my-clang", "-v"]) + .from_args(vec!["my-clang".into(), "-v".into()]) .unwrap() .compile() .unwrap(); From 7f72d709779e13d76ea5050a05449c7407f14299 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 23 Mar 2021 13:50:22 +0100 Subject: [PATCH 015/104] Windows Fuzzing Example (#41) * windows fuzzer target and minor changes - breaks android support and maybe linux build * adapted windows example * removed warnings from build.rs * fixed build on unix * fixed no_std * build fix, silenced warnings * no_std warning silenced * clippy fixes * fmt * windows fuzzer target and minor changes - breaks android support and maybe linux build * adapted windows example * removed warnings from build.rs * fixed build on unix * fixed no_std * build fix, silenced warnings * no_std warning silenced * clippy fixes * fmt * clippy * trying to add clang support * debugging win build * debugging win build more * debuggin.. * debuggin.... * more debugging * giving up Co-authored-by: richinseattle --- .github/workflows/build_and_test.yml | 140 +- Cargo.toml | 1 + fuzzers/frida_libpng/build.rs | 228 +- fuzzers/frida_libpng/src/fuzzer.rs | 16 +- fuzzers/libfuzzer_libpng/build.rs | 222 +- fuzzers/libfuzzer_runtime/rt.c | 4 + fuzzers/libfuzzer_windows/.gitignore | 1 + fuzzers/libfuzzer_windows/Cargo.toml | 31 + fuzzers/libfuzzer_windows/README.md | 25 + fuzzers/libfuzzer_windows/build.rs | 58 + .../libfuzzer_windows/corpus/not_kitty.png | Bin 0 -> 218 bytes .../corpus/not_kitty_alpha.png | Bin 0 -> 376 bytes .../corpus/not_kitty_gamma.png | Bin 0 -> 228 bytes .../corpus/not_kitty_icc.png | Bin 0 -> 427 bytes fuzzers/libfuzzer_windows/harness.cc | 231 + fuzzers/libfuzzer_windows/src/fuzzer.rs | 180 + fuzzers/libfuzzer_windows/stb_image.h | 7762 +++++++++++++++++ fuzzers/libfuzzer_windows/test.bat | 20 + libafl/src/bolts/llmp.rs | 3 + libafl/src/bolts/os/windows_exceptions.rs | 666 +- libafl/src/bolts/shmem.rs | 15 +- libafl/src/events/llmp.rs | 9 +- libafl_cc/src/runtime.rs | 8 +- 23 files changed, 8976 insertions(+), 644 deletions(-) create mode 100644 fuzzers/libfuzzer_windows/.gitignore create mode 100644 fuzzers/libfuzzer_windows/Cargo.toml create mode 100644 fuzzers/libfuzzer_windows/README.md create mode 100644 fuzzers/libfuzzer_windows/build.rs create mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty.png create mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty_alpha.png create mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty_gamma.png create mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty_icc.png create mode 100644 fuzzers/libfuzzer_windows/harness.cc create mode 100644 fuzzers/libfuzzer_windows/src/fuzzer.rs create mode 100644 fuzzers/libfuzzer_windows/stb_image.h create mode 100644 fuzzers/libfuzzer_windows/test.bat diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index dafd5dc04e..1a6d6dc4d4 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1,67 +1,73 @@ -name: Build and Test - -on: - push: - branches: [ main, dev ] - pull_request: - branches: [ main, dev ] - -env: - CARGO_TERM_COLOR: always - -jobs: - lint: - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 - - name: Cache cargo registry - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }} - - name: Add clippy - run: rustup component add clippy - - name: Run clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all - ubuntu: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Default Build - run: cargo build --verbose - - name: Default Test - run: cargo test --verbose - - name: Build all features - run: cd libafl && cargo build --all-features --verbose - - name: Test all features - run: cd libafl && cargo test --all-features --verbose - - name: Build no_std - run: cd libafl && cargo build --no-default-features --verbose - - name: Test no_std - run: cd libafl && cargo test --no-default-features --verbose - - name: Build examples - run: cargo build --examples --verbose - - uses: actions/checkout@v2 - - name: Format - run: cargo fmt -- --check - - uses: actions/checkout@v2 - - name: Build Docs - run: cargo doc - - name: Test Docs - run: cargo test --doc - windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - name: Windows Build - run: cargo build --verbose - - name: Windows Test - run: cargo test --verbose +name: Build and Test + +on: + push: + branches: [ main, dev ] + pull_request: + branches: [ main, dev ] + +env: + CARGO_TERM_COLOR: always + +jobs: + lint: + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }} + - name: Add clippy + run: rustup component add clippy + - name: Run clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all + ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Default Build + run: cargo build --verbose + - name: Default Test + run: cargo test --verbose + - name: Build all features + run: cd libafl && cargo build --all-features --verbose + - name: Test all features + run: cd libafl && cargo test --all-features --verbose + - name: Build no_std + run: cd libafl && cargo build --no-default-features --verbose + - name: Test no_std + run: cd libafl && cargo test --no-default-features --verbose + - name: Build examples + run: cargo build --examples --verbose + - uses: actions/checkout@v2 + - name: Format + run: cargo fmt -- --check + - uses: actions/checkout@v2 + - name: Build Docs + run: cargo doc + - name: Test Docs + run: cargo test --doc + windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Windows Build + run: cargo build --verbose + # TODO: Figure out how to properly build stuff with clang + #- name: Add clang path to $PATH env + # if: runner.os == 'Windows' + # run: echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 + #- name: Try if clang works + # run: clang -v + #- name: Windows Test + # run: C:\Rust\.cargo\bin\cargo.exe test --verbose diff --git a/Cargo.toml b/Cargo.toml index d1037a8bc0..bc3ba2904a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,5 @@ members = [ "fuzzers/frida_libpng", "fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libpng_cmpalloc", + "fuzzers/libfuzzer_windows", ] diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs index 007a54139d..cfe52fdf7a 100644 --- a/fuzzers/frida_libpng/build.rs +++ b/fuzzers/frida_libpng/build.rs @@ -1,114 +1,114 @@ -// build.rs - -use std::{ - env, - path::Path, - process::{exit, Command}, -}; - -const LIBPNG_URL: &str = - "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; - -fn main() { - if cfg!(windows) { - println!("cargo:warning=Skipping libpng frida example on Windows"); - exit(0); - } - - let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); - let out_dir = out_dir.to_string_lossy().to_string(); - let out_dir_path = Path::new(&out_dir); - std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir)); - - println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); - println!("cargo:rerun-if-changed=harness.cc"); - - let libpng = format!("{}/libpng-1.6.37", &out_dir); - let libpng_path = Path::new(&libpng); - let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); - - // Enforce clang for its -fsanitize-coverage support. - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); - let ldflags = match env::var("LDFLAGS") { - Ok(val) => val, - Err(_) => "".to_string(), - }; - - // println!("cargo:warning=output path is {}", libpng); - if !libpng_path.is_dir() { - if !Path::new(&libpng_tar).is_file() { - println!("cargo:warning=Libpng not found, downloading..."); - // Download libpng - Command::new("wget") - .arg("-c") - .arg(LIBPNG_URL) - .arg("-O") - .arg(&libpng_tar) - .status() - .unwrap(); - } - Command::new("tar") - .current_dir(&out_dir_path) - .arg("xvf") - .arg(&libpng_tar) - .status() - .unwrap(); - Command::new(format!("{}/configure", &libpng)) - .current_dir(&libpng_path) - .args(&[ - "--disable-shared", - &format!("--host={}", env::var("TARGET").unwrap())[..], - ]) - .env("CC", "clang") - .env("CXX", "clang++") - .env( - "CFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", - ) - .env( - "CXXFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", - ) - .env( - "LDFLAGS", - //format!("-g -fPIE -fsanitize=address {}", ldflags), - format!("-g -fPIE {}", ldflags), - ) - .status() - .unwrap(); - Command::new("make") - .current_dir(&libpng_path) - .status() - .unwrap(); - } - - let status = cc::Build::new() - .cpp(true) - .get_compiler() - .to_command() - .current_dir(&cwd) - .arg("-I") - .arg(format!("{}", &libpng)) - //.arg("-D") - //.arg("HAS_DUMMY_CRASH=1") - .arg("-fPIC") - .arg("-shared") - .arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" { - "-static-libstdc++" - } else { - "" - }) - .arg("-o") - .arg(format!("{}/libpng-harness.so", &out_dir)) - .arg("./harness.cc") - .arg(format!("{}/.libs/libpng16.a", &libpng)) - .arg("-l") - .arg("z") - .status() - .unwrap(); - assert!(status.success()); - - println!("cargo:rerun-if-changed=build.rs"); -} +// build.rs + +use std::{ + env, + path::Path, + process::{exit, Command}, +}; + +const LIBPNG_URL: &str = + "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; + +fn main() { + if cfg!(windows) { + println!("cargo:warning=Skipping libpng frida example on Windows"); + exit(0); + } + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + let out_dir = out_dir.to_string_lossy().to_string(); + let out_dir_path = Path::new(&out_dir); + std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir)); + + println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); + println!("cargo:rerun-if-changed=harness.cc"); + + let libpng = format!("{}/libpng-1.6.37", &out_dir); + let libpng_path = Path::new(&libpng); + let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); + + // Enforce clang for its -fsanitize-coverage support. + std::env::set_var("CC", "clang"); + std::env::set_var("CXX", "clang++"); + let ldflags = match env::var("LDFLAGS") { + Ok(val) => val, + Err(_) => "".to_string(), + }; + + // println!("cargo:warning=output path is {}", libpng); + if !libpng_path.is_dir() { + if !Path::new(&libpng_tar).is_file() { + println!("cargo:warning=Libpng not found, downloading..."); + // Download libpng + Command::new("wget") + .arg("-c") + .arg(LIBPNG_URL) + .arg("-O") + .arg(&libpng_tar) + .status() + .unwrap(); + } + Command::new("tar") + .current_dir(&out_dir_path) + .arg("xvf") + .arg(&libpng_tar) + .status() + .unwrap(); + Command::new(format!("{}/configure", &libpng)) + .current_dir(&libpng_path) + .args(&[ + "--disable-shared", + &format!("--host={}", env::var("TARGET").unwrap())[..], + ]) + .env("CC", "clang") + .env("CXX", "clang++") + .env( + "CFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "CXXFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "LDFLAGS", + //format!("-g -fPIE -fsanitize=address {}", ldflags), + format!("-g -fPIE {}", ldflags), + ) + .status() + .unwrap(); + Command::new("make") + .current_dir(&libpng_path) + .status() + .unwrap(); + } + + let status = cc::Build::new() + .cpp(true) + .get_compiler() + .to_command() + .current_dir(&cwd) + .arg("-I") + .arg(&libpng) + //.arg("-D") + //.arg("HAS_DUMMY_CRASH=1") + .arg("-fPIC") + .arg("-shared") + .arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" { + "-static-libstdc++" + } else { + "" + }) + .arg("-o") + .arg(format!("{}/libpng-harness.so", &out_dir)) + .arg("./harness.cc") + .arg(format!("{}/.libs/libpng16.a", &libpng)) + .arg("-l") + .arg("z") + .status() + .unwrap(); + assert!(status.success()); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index dac2564c4e..51950b31ef 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -34,8 +34,6 @@ use frida_gum::{ }; use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection}; -use libloading; - use std::{cell::RefCell, env, ffi::c_void, path::PathBuf}; /// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation @@ -351,9 +349,9 @@ where )); Self { - base: base, - stalker: stalker, - helper: helper, + base, + stalker, + helper, followed: false, } } @@ -464,10 +462,10 @@ unsafe fn fuzz( if state.metadata().get::().is_none() { state.add_metadata(Tokens::new(vec![ vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header - "IHDR".as_bytes().to_vec(), - "IDAT".as_bytes().to_vec(), - "PLTE".as_bytes().to_vec(), - "IEND".as_bytes().to_vec(), + b"IHDR".to_vec(), + b"IDAT".to_vec(), + b"PLTE".to_vec(), + b"IEND".to_vec(), ])); } diff --git a/fuzzers/libfuzzer_libpng/build.rs b/fuzzers/libfuzzer_libpng/build.rs index 49f3cfba94..5e68b3bc46 100644 --- a/fuzzers/libfuzzer_libpng/build.rs +++ b/fuzzers/libfuzzer_libpng/build.rs @@ -1,109 +1,113 @@ -// build.rs - -use std::{ - env, - path::Path, - process::{exit, Command}, -}; - -const LIBPNG_URL: &str = - "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; - -fn main() { - if cfg!(windows) { - println!("cargo:warning=Skipping libpng example on Windows"); - exit(0); - } - - let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); - let out_dir = out_dir.to_string_lossy().to_string(); - let out_dir_path = Path::new(&out_dir); - - println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); - println!("cargo:rerun-if-changed=harness.cc"); - - let libpng = format!("{}/libpng-1.6.37", &out_dir); - let libpng_path = Path::new(&libpng); - let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); - - // Enforce clang for its -fsanitize-coverage support. - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); - let ldflags = match env::var("LDFLAGS") { - Ok(val) => val, - Err(_) => "".to_string(), - }; - - if !libpng_path.is_dir() { - if !Path::new(&libpng_tar).is_file() { - println!("cargo:warning=Libpng not found, downloading..."); - // Download libpng - Command::new("wget") - .arg("-c") - .arg(LIBPNG_URL) - .arg("-O") - .arg(&libpng_tar) - .status() - .unwrap(); - } - Command::new("tar") - .current_dir(&out_dir_path) - .arg("-xvf") - .arg(&libpng_tar) - .status() - .unwrap(); - Command::new(format!("{}/configure", &libpng)) - .current_dir(&libpng_path) - .args(&[ - "--disable-shared", - &format!("--host={}", env::var("TARGET").unwrap())[..], - ]) - .env("CC", "clang") - .env("CXX", "clang++") - .env( - "CFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "CXXFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "LDFLAGS", - format!("-g -fPIE -fsanitize-coverage=trace-pc-guard {}", ldflags), - ) - .status() - .unwrap(); - Command::new("make") - .current_dir(&libpng_path) - .status() - .unwrap(); - } - - cc::Build::new() - .file("../libfuzzer_runtime/rt.c") - .compile("libfuzzer-sys"); - - cc::Build::new() - .include(&libpng_path) - .cpp(true) - .flag("-fsanitize-coverage=trace-pc-guard") - // .define("HAS_DUMMY_CRASH", "1") - .file("./harness.cc") - .compile("libfuzzer-harness"); - - println!("cargo:rustc-link-search=native={}", &out_dir); - println!("cargo:rustc-link-search=native={}/.libs", &libpng); - println!("cargo:rustc-link-lib=static=png16"); - - //Deps for libpng: -pthread -lz -lm - println!("cargo:rustc-link-lib=dylib=m"); - println!("cargo:rustc-link-lib=dylib=z"); - - //For the C++ harness - //must by dylib for android - println!("cargo:rustc-link-lib=dylib=stdc++"); - - println!("cargo:rerun-if-changed=build.rs"); -} +// build.rs + +use std::{ + env, + path::Path, + process::{exit, Command}, +}; + +const LIBPNG_URL: &str = + "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; + +fn main() { + if cfg!(windows) { + println!("cargo:warning=Skipping libpng example on Windows"); + exit(0); + } + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + let out_dir = out_dir.to_string_lossy().to_string(); + let out_dir_path = Path::new(&out_dir); + + println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); + println!("cargo:rerun-if-changed=harness.cc"); + + let libpng = format!("{}/libpng-1.6.37", &out_dir); + let libpng_path = Path::new(&libpng); + let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); + + // Enforce clang for its -fsanitize-coverage support. + std::env::set_var("CC", "clang"); + std::env::set_var("CXX", "clang++"); + let ldflags = match env::var("LDFLAGS") { + Ok(val) => val, + Err(_) => "".to_string(), + }; + + if !libpng_path.is_dir() { + if !Path::new(&libpng_tar).is_file() { + println!("cargo:warning=Libpng not found, downloading..."); + // Download libpng + Command::new("wget") + .arg("-c") + .arg(LIBPNG_URL) + .arg("-O") + .arg(&libpng_tar) + .status() + .unwrap(); + } + Command::new("tar") + .current_dir(&out_dir_path) + .arg("-xvf") + .arg(&libpng_tar) + .status() + .unwrap(); + Command::new(format!("{}/configure", &libpng)) + .current_dir(&libpng_path) + .args(&[ + "--disable-shared", + &format!("--host={}", env::var("TARGET").unwrap())[..], + ]) + .env("CC", "clang") + .env("CXX", "clang++") + .env( + "CFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", + ) + .env( + "CXXFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", + ) + .env( + "LDFLAGS", + format!("-g -fPIE -fsanitize-coverage=trace-pc-guard {}", ldflags), + ) + .status() + .unwrap(); + Command::new("make") + .current_dir(&libpng_path) + .status() + .unwrap(); + } + + cc::Build::new() + .file("../libfuzzer_runtime/rt.c") + .compile("libfuzzer-sys"); + + cc::Build::new() + .include(&libpng_path) + .cpp(true) + .flag("-fsanitize-coverage=trace-pc-guard") + .flag("-Wno-void-pointer-to-int-cast") + .flag("-Wno-int-to-pointer-cast") + .flag("-Wno-sign-compare") + .flag("-Wno-format") + // .define("HAS_DUMMY_CRASH", "1") + .file("./harness.cc") + .compile("libfuzzer-harness"); + + println!("cargo:rustc-link-search=native={}", &out_dir); + println!("cargo:rustc-link-search=native={}/.libs", &libpng); + println!("cargo:rustc-link-lib=static=png16"); + + //Deps for libpng: -pthread -lz -lm + println!("cargo:rustc-link-lib=dylib=m"); + println!("cargo:rustc-link-lib=dylib=z"); + + //For the C++ harness + //must by dylib for android + println!("cargo:rustc-link-lib=dylib=stdc++"); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/fuzzers/libfuzzer_runtime/rt.c b/fuzzers/libfuzzer_runtime/rt.c index 8379132732..eed26db24f 100644 --- a/fuzzers/libfuzzer_runtime/rt.c +++ b/fuzzers/libfuzzer_runtime/rt.c @@ -131,6 +131,10 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { } +#ifdef _WIN32 +#define posix_memalign(p, a, s) (((*(p)) = _aligned_malloc((s), (a))), *(p) ?0 :errno) +#endif + void *malloc(size_t size) { uintptr_t k = (uintptr_t)__builtin_return_address(0); diff --git a/fuzzers/libfuzzer_windows/.gitignore b/fuzzers/libfuzzer_windows/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/libfuzzer_windows/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/libfuzzer_windows/Cargo.toml b/fuzzers/libfuzzer_windows/Cargo.toml new file mode 100644 index 0000000000..2f212a1e6a --- /dev/null +++ b/fuzzers/libfuzzer_windows/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "libfuzzer_windows" +version = "0.1.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["std"] +std = [] + +#[profile.release] +#lto = true +#codegen-units = 1 +#opt-level = 3 +#debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +num_cpus = "1.0" + +[dependencies] +libafl = { path = "../../libafl/" } + +[[example]] +name = "libfuzzer_windows" +path = "./src/fuzzer.rs" +test = false +bench = false diff --git a/fuzzers/libfuzzer_windows/README.md b/fuzzers/libfuzzer_windows/README.md new file mode 100644 index 0000000000..f56138c2b5 --- /dev/null +++ b/fuzzers/libfuzzer_windows/README.md @@ -0,0 +1,25 @@ +# Libfuzzer for libpng + +This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. +To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. +It has been tested on Linux. + +## Build + +To build this example, run `cargo build --example libfuzzer_libpng --release`. +This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. +Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. +Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. + +## Run + +The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. + +Each following execution will run a fuzzer client. +As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). +This means each client will start itself again to listen for crashes and timeouts. +By restarting the actual fuzzer, it can recover from these exit conditions. + +In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). + +For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/libfuzzer_windows/build.rs b/fuzzers/libfuzzer_windows/build.rs new file mode 100644 index 0000000000..bb08b22443 --- /dev/null +++ b/fuzzers/libfuzzer_windows/build.rs @@ -0,0 +1,58 @@ +// build.rs + +#[cfg(windows)] +use std::env; + +#[cfg(not(windows))] +fn main() { + println!("cargo:warning=Skipping libpng windows example on non-Windows"); + return; +} + +#[cfg(windows)] +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = out_dir.to_string_lossy().to_string(); + + println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); + println!("cargo:rerun-if-changed=harness.cc"); + + // Enforce clang for its -fsanitize-coverage support. + std::env::set_var("CC", "clang"); + std::env::set_var("CXX", "clang++"); + /*let ldflags = match env::var("LDFLAGS") { + Ok(val) => val, + Err(_) => "".to_string(), + };*/ + + cc::Build::new() + .file("../libfuzzer_runtime/rt.c") + .compile("libfuzzer-sys"); + + cc::Build::new() + .cpp(true) + .flag("-fsanitize-coverage=trace-pc-guard") + // .define("HAS_DUMMY_CRASH", "1") + .flag("-Wno-void-pointer-to-int-cast") + .flag("-Wno-pointer-to-int-cast") + .flag("-Wno-int-to-pointer-cast") + .flag("-Wno-sign-compare") + .flag("-Wno-format") + .flag("-Wno-unused-variable") + .file("./harness.cc") + .compile("windows-harness"); + + println!("cargo:rustc-link-search=native={}", &out_dir); + //println!("cargo:rustc-link-search=native={}/.libs", &libpng); + //println!("cargo:rustc-link-lib=static=png16"); + + //Deps for libpng: -pthread -lz -lm + //println!("cargo:rustc-link-lib=dylib=m"); + //println!("cargo:rustc-link-lib=dylib=z"); + + //For the C++ harness + //must by dylib for android + //println!("cargo:rustc-link-lib=dylib=stdc++"); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/fuzzers/libfuzzer_windows/corpus/not_kitty.png b/fuzzers/libfuzzer_windows/corpus/not_kitty.png new file mode 100644 index 0000000000000000000000000000000000000000..eff7c1707b936a8f8df725814f604d454b78b5c3 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X_yc@GT+_~+`TzevkY_wIZRYx+5&y#hyq+?%!C8<`)MX5lF!N|bSRM)^r*U&J;z}U*bz{;0L z1Vuw`eoAIqC5i?kD`P_|6GMoGiCWXn12ss3YzWRzD=AMbN@Z|N$xljE@XSq2PYp^< WOsOn9nQ8-6#Ng@b=d#Wzp$PyV*n0l} literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_windows/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_windows/corpus/not_kitty_gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..939d9d29a9b9f95bac5e9a72854361ee85469921 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmTQ929t;oCfmw1AIbU z)6Sgv|NlRbXFM})=KnKxKI=t+9LW;bh?3y^w370~qErUQl>DSr1<%~X^wgl##FWay zlc_d9MbVxvjv*GO?@o5)YH;9THa`3B|5>?^8?LvjJ}xLe>!7e@k)r^sLedir0mCVe z=5sMjEm$*~tHD+}{NS_$nMdb|ABqg-@UGMMsZ=uY-X%Cq@&3vmZ%&@H{P?6&+U!yq VvuXWlo?M_c44$rjF6*2UngF4cP+$N6 literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_windows/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_windows/corpus/not_kitty_icc.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c7804d99829cc6307c1c6ae9915cf42d555414 GIT binary patch literal 427 zcmV;c0aX5pP)9xSWu9|B*4Isn^#g47m^r~thH)GiR<@yX0fO)OF<2Kt#qCldyUF#H?{4jV?XGw9)psxE&K1B1m^ z1_tH{2(hG@3=G>_85ksPA;eS`Ffj19FfeR8pIlm01~rBeWCZ{dbvfq;rA3DT000kA zOjJc?%*_A){{R30GnreSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM003J_L_t(I%iWVf3V=Wi12fJ3|IHp$*hSlV@t||fKp?cDK@bHXV&o_g zF_hw;3ILUGteXmeJsVfSmcVJno)^MdQwU3bFHCtNG)uY>mLcD%`0UBaIq~Fq8#dBr V12uok3~c}a002ovPDHLkV1nKBo!S5Z literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_windows/harness.cc b/fuzzers/libfuzzer_windows/harness.cc new file mode 100644 index 0000000000..2c256bcc71 --- /dev/null +++ b/fuzzers/libfuzzer_windows/harness.cc @@ -0,0 +1,231 @@ +#include +#include +#include + +#define STBI_ASSERT(x) +#define STBI_NO_SIMD +#define STBI_NO_LINEAR +#define STBI_NO_STDIO +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +int target_func(const uint8_t *buf, size_t size) { + + /*printf("BUF (%ld): ", size); + for (int i = 0; i < size; i++) { + printf("%02X", buf[i]); + } + printf("\n");*/ + + if (size == 0) return 0; + + switch (buf[0]) { + + case 1: + if (buf[1] == 0x44) { + //__builtin_trap(); + return 8; + } + + break; + case 0xff: + if (buf[2] == 0xff) { + if (buf[1] == 0x44) { + //*(char *)(0xdeadbeef) = 1; + return 9; + } + } + + break; + default: + break; + + } + + return 1; + +} + +int parse_pe(const uint8_t *data, int size) +{ + HANDLE file = NULL; + DWORD fileSize = NULL; + DWORD bytesRead = NULL; + LPVOID fileData = NULL; + PIMAGE_DOS_HEADER dosHeader = {}; + PIMAGE_NT_HEADERS imageNTHeaders = {}; + PIMAGE_SECTION_HEADER sectionHeader = {}; + PIMAGE_SECTION_HEADER importSection = {}; + IMAGE_IMPORT_DESCRIPTOR* importDescriptor = {}; + PIMAGE_THUNK_DATA thunkData = {}; + DWORD thunk = NULL; + DWORD rawOffset = NULL; + + // allocate heap + fileSize = size; + fileData = (void *)data; + + // IMAGE_DOS_HEADER + dosHeader = (PIMAGE_DOS_HEADER)fileData; + + printf("******* DOS HEADER *******\n"); + printf("\t0x%x\t\tMagic number\n", dosHeader->e_magic); + /* + printf("\t0x%x\t\tBytes on last page of file\n", dosHeader->e_cblp); + printf("\t0x%x\t\tPages in file\n", dosHeader->e_cp); + printf("\t0x%x\t\tRelocations\n", dosHeader->e_crlc); + printf("\t0x%x\t\tSize of header in paragraphs\n", dosHeader->e_cparhdr); + printf("\t0x%x\t\tMinimum extra paragraphs needed\n", dosHeader->e_minalloc); + printf("\t0x%x\t\tMaximum extra paragraphs needed\n", dosHeader->e_maxalloc); + printf("\t0x%x\t\tInitial (relative) SS value\n", dosHeader->e_ss); + printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp); + printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp); + printf("\t0x%x\t\tChecksum\n", dosHeader->e_csum); + printf("\t0x%x\t\tInitial IP value\n", dosHeader->e_ip); + printf("\t0x%x\t\tInitial (relative) CS value\n", dosHeader->e_cs); + printf("\t0x%x\t\tFile address of relocation table\n", dosHeader->e_lfarlc); + printf("\t0x%x\t\tOverlay number\n", dosHeader->e_ovno); + printf("\t0x%x\t\tOEM identifier (for e_oeminfo)\n", dosHeader->e_oemid); + printf("\t0x%x\t\tOEM information; e_oemid specific\n", dosHeader->e_oeminfo); + printf("\t0x%x\t\tFile address of new exe header\n", dosHeader->e_lfanew); +*/ + // IMAGE_NT_HEADERS + imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)fileData + dosHeader->e_lfanew); + /* + printf("\n******* NT HEADERS *******\n"); + printf("\t%x\t\tSignature\n", imageNTHeaders->Signature); + + // FILE_HEADER + printf("\n******* FILE HEADER *******\n"); + printf("\t0x%x\t\tMachine\n", imageNTHeaders->FileHeader.Machine); + printf("\t0x%x\t\tNumber of Sections\n", imageNTHeaders->FileHeader.NumberOfSections); + printf("\t0x%x\tTime Stamp\n", imageNTHeaders->FileHeader.TimeDateStamp); + printf("\t0x%x\t\tPointer to Symbol Table\n", imageNTHeaders->FileHeader.PointerToSymbolTable); + printf("\t0x%x\t\tNumber of Symbols\n", imageNTHeaders->FileHeader.NumberOfSymbols); + printf("\t0x%x\t\tSize of Optional Header\n", imageNTHeaders->FileHeader.SizeOfOptionalHeader); + printf("\t0x%x\t\tCharacteristics\n", imageNTHeaders->FileHeader.Characteristics); + + // OPTIONAL_HEADER + printf("\n******* OPTIONAL HEADER *******\n"); + printf("\t0x%x\t\tMagic\n", imageNTHeaders->OptionalHeader.Magic); + printf("\t0x%x\t\tMajor Linker Version\n", imageNTHeaders->OptionalHeader.MajorLinkerVersion); + printf("\t0x%x\t\tMinor Linker Version\n", imageNTHeaders->OptionalHeader.MinorLinkerVersion); + printf("\t0x%x\t\tSize Of Code\n", imageNTHeaders->OptionalHeader.SizeOfCode); + printf("\t0x%x\t\tSize Of Initialized Data\n", imageNTHeaders->OptionalHeader.SizeOfInitializedData); + printf("\t0x%x\t\tSize Of UnInitialized Data\n", imageNTHeaders->OptionalHeader.SizeOfUninitializedData); + printf("\t0x%x\t\tAddress Of Entry Point (.text)\n", imageNTHeaders->OptionalHeader.AddressOfEntryPoint); + printf("\t0x%x\t\tBase Of Code\n", imageNTHeaders->OptionalHeader.BaseOfCode); + //printf("\t0x%x\t\tBase Of Data\n", imageNTHeaders->OptionalHeader.BaseOfData); + printf("\t0x%x\t\tImage Base\n", imageNTHeaders->OptionalHeader.ImageBase); + printf("\t0x%x\t\tSection Alignment\n", imageNTHeaders->OptionalHeader.SectionAlignment); + printf("\t0x%x\t\tFile Alignment\n", imageNTHeaders->OptionalHeader.FileAlignment); + printf("\t0x%x\t\tMajor Operating System Version\n", imageNTHeaders->OptionalHeader.MajorOperatingSystemVersion); + printf("\t0x%x\t\tMinor Operating System Version\n", imageNTHeaders->OptionalHeader.MinorOperatingSystemVersion); + printf("\t0x%x\t\tMajor Image Version\n", imageNTHeaders->OptionalHeader.MajorImageVersion); + printf("\t0x%x\t\tMinor Image Version\n", imageNTHeaders->OptionalHeader.MinorImageVersion); + printf("\t0x%x\t\tMajor Subsystem Version\n", imageNTHeaders->OptionalHeader.MajorSubsystemVersion); + printf("\t0x%x\t\tMinor Subsystem Version\n", imageNTHeaders->OptionalHeader.MinorSubsystemVersion); + printf("\t0x%x\t\tWin32 Version Value\n", imageNTHeaders->OptionalHeader.Win32VersionValue); + printf("\t0x%x\t\tSize Of Image\n", imageNTHeaders->OptionalHeader.SizeOfImage); + printf("\t0x%x\t\tSize Of Headers\n", imageNTHeaders->OptionalHeader.SizeOfHeaders); + printf("\t0x%x\t\tCheckSum\n", imageNTHeaders->OptionalHeader.CheckSum); + printf("\t0x%x\t\tSubsystem\n", imageNTHeaders->OptionalHeader.Subsystem); + printf("\t0x%x\t\tDllCharacteristics\n", imageNTHeaders->OptionalHeader.DllCharacteristics); + printf("\t0x%x\t\tSize Of Stack Reserve\n", imageNTHeaders->OptionalHeader.SizeOfStackReserve); + printf("\t0x%x\t\tSize Of Stack Commit\n", imageNTHeaders->OptionalHeader.SizeOfStackCommit); + printf("\t0x%x\t\tSize Of Heap Reserve\n", imageNTHeaders->OptionalHeader.SizeOfHeapReserve); + printf("\t0x%x\t\tSize Of Heap Commit\n", imageNTHeaders->OptionalHeader.SizeOfHeapCommit); + printf("\t0x%x\t\tLoader Flags\n", imageNTHeaders->OptionalHeader.LoaderFlags); + printf("\t0x%x\t\tNumber Of Rva And Sizes\n", imageNTHeaders->OptionalHeader.NumberOfRvaAndSizes); + + // DATA_DIRECTORIES + printf("\n******* DATA DIRECTORIES *******\n"); + printf("\tExport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[0].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[0].Size); + printf("\tImport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[1].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[1].Size); + */ +return 0; + // SECTION_HEADERS + printf("\n******* SECTION HEADERS *******\n"); + // get offset to first section headeer + DWORD sectionLocation = (DWORD)imageNTHeaders + sizeof(DWORD) + (DWORD)(sizeof(IMAGE_FILE_HEADER)) + (DWORD)imageNTHeaders->FileHeader.SizeOfOptionalHeader; + DWORD sectionSize = (DWORD)sizeof(IMAGE_SECTION_HEADER); + + // get offset to the import directory RVA + DWORD importDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + // print section data + for (int i = 0; i < imageNTHeaders->FileHeader.NumberOfSections; i++) { + sectionHeader = (PIMAGE_SECTION_HEADER)sectionLocation; + printf("\t%s\n", sectionHeader->Name); + printf("\t\t0x%x\t\tVirtual Size\n", sectionHeader->Misc.VirtualSize); + printf("\t\t0x%x\t\tVirtual Address\n", sectionHeader->VirtualAddress); + /* + printf("\t\t0x%x\t\tSize Of Raw Data\n", sectionHeader->SizeOfRawData); + printf("\t\t0x%x\t\tPointer To Raw Data\n", sectionHeader->PointerToRawData); + printf("\t\t0x%x\t\tPointer To Relocations\n", sectionHeader->PointerToRelocations); + printf("\t\t0x%x\t\tPointer To Line Numbers\n", sectionHeader->PointerToLinenumbers); + printf("\t\t0x%x\t\tNumber Of Relocations\n", sectionHeader->NumberOfRelocations); + printf("\t\t0x%x\t\tNumber Of Line Numbers\n", sectionHeader->NumberOfLinenumbers); + printf("\t\t0x%x\tCharacteristics\n", sectionHeader->Characteristics); + */ + // save section that contains import directory table + if (importDirectoryRVA >= sectionHeader->VirtualAddress && importDirectoryRVA < sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize) { + importSection = sectionHeader; + } + sectionLocation += sectionSize; + } + + // get file offset to import table + rawOffset = (DWORD)fileData + importSection->PointerToRawData; + + // get pointer to import descriptor's file offset. Note that the formula for calculating file offset is: imageBaseAddress + pointerToRawDataOfTheSectionContainingRVAofInterest + (RVAofInterest - SectionContainingRVAofInterest.VirtualAddress) + importDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)(rawOffset + (imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress - importSection->VirtualAddress)); + + printf("\n******* DLL IMPORTS *******\n"); + for (; importDescriptor->Name != 0; importDescriptor++) { + // imported dll modules + printf("\t%s\n", rawOffset + (importDescriptor->Name - importSection->VirtualAddress)); + thunk = importDescriptor->OriginalFirstThunk == 0 ? importDescriptor->FirstThunk : importDescriptor->OriginalFirstThunk; + thunkData = (PIMAGE_THUNK_DATA)(rawOffset + (thunk - importSection->VirtualAddress)); + + // dll exported functions + for (; thunkData->u1.AddressOfData != 0; thunkData++) { + //a cheap and probably non-reliable way of checking if the function is imported via its ordinal number ¯\_(ツ)_/¯ + if (thunkData->u1.AddressOfData > 0x80000000) { + //show lower bits of the value to get the ordinal ¯\_(ツ)_/¯ + printf("\t\tOrdinal: %x\n", (WORD)thunkData->u1.AddressOfData); + } else { + printf("\t\t%s\n", (rawOffset + (thunkData->u1.AddressOfData - importSection->VirtualAddress + 2))); + } + } + } + + return 0; +} + + + +int load_stbi(const uint8_t *data, int size) +{ + int w; + int h; + int channels; + + const unsigned char * img = stbi_load_from_memory(data, size, &w, &h, &channels, 0); + if (img) { stbi_image_free((void *)img); } + // STBI_FREE((void *)img); } + + return 0; +} + +extern "C" +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + //return target_func(Data, Size); + + if(Size > 0x4000) return 0; + + int size = Size; + const unsigned char * data = Data; + //return load_stbi(data, size); + return parse_pe(data, size); +} + diff --git a/fuzzers/libfuzzer_windows/src/fuzzer.rs b/fuzzers/libfuzzer_windows/src/fuzzer.rs new file mode 100644 index 0000000000..be97929037 --- /dev/null +++ b/fuzzers/libfuzzer_windows/src/fuzzer.rs @@ -0,0 +1,180 @@ +//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts +//! The example harness is built for libpng. + +#[cfg(windows)] +use std::{env, path::PathBuf}; + +#[cfg(windows)] +use libafl::{ + bolts::{shmem::Win32ShMem, tuples::tuple_list}, + corpus::{ + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + QueueCorpusScheduler, + }, + events::setup_restarting_mgr, + executors::{inprocess::InProcessExecutor, ExitKind}, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, + mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, HasMetadata, State}, + stats::SimpleStats, + utils::{current_nanos, StdRand}, + Error, +}; + +/// We will interact with a C++ target, so use external c functionality +#[cfg(windows)] +extern "C" { + /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) + fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; + + // afl_libfuzzer_init calls LLVMFUzzerInitialize() + fn afl_libfuzzer_init() -> i32; + + static __lafl_edges_map: *mut u8; + static __lafl_cmp_map: *mut u8; + static __lafl_max_edges_size: u32; +} + +/// The main fn, usually parsing parameters, and starting the fuzzer +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + #[cfg(windows)] + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + + #[cfg(not(windows))] + todo!("Example currently only supports Windows."); + + #[cfg(windows)] + fuzz( + vec![PathBuf::from("./corpus")], + PathBuf::from("./crashes"), + 1337, + ) + .expect("An error occurred while fuzzing"); +} + +/// Not supported on unix right now +//#[cfg(cfg)] +//fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { +// todo!("Example not supported on Unix"); +//} + +/// The actual fuzzer +#[cfg(windows)] +fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + + // 'While the stats are state, they are usually used in the broker - which is likely never restarted + let stats = SimpleStats::new(|s| println!("{}", s)); + + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + let (state, mut restarting_mgr) = + match setup_restarting_mgr::<_, _, Win32ShMem, _>(stats, broker_port) { + Ok(res) => res, + Err(err) => match err { + Error::ShuttingDown => { + return Ok(()); + } + _ => { + panic!("Failed to setup the restarter: {}", err); + } + }, + }; + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(unsafe { + StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) + }); + + // If not restarting, create a State from scratch + let mut state = state.unwrap_or_else(|| { + State::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Feedbacks to rate the interestingness of an input + tuple_list!( + MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), + TimeFeedback::new() + ), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(objective_dir).unwrap(), + // Feedbacks to recognize an input as solution + tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()), + ) + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().is_none() { + state.add_metadata(Tokens::new(vec![ + vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header + "IHDR".as_bytes().to_vec(), + "IDAT".as_bytes().to_vec(), + "PLTE".as_bytes().to_vec(), + "IEND".as_bytes().to_vec(), + ])); + } + + // Setup a basic mutator with a mutational stage + let mutator = HavocBytesMutator::default(); + let stage = StdMutationalStage::new(mutator); + + // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + + // Create the executor for an in-process function with just one observer for edge coverage + let mut executor = InProcessExecutor::new( + "in-process(edges)", + &mut harness, + tuple_list!(edges_observer, TimeObserver::new("time")), + &mut state, + &mut restarting_mgr, + )?; + + // The actual target run starts here. + // Call LLVMFUzzerInitialize() if present. + unsafe { + if afl_libfuzzer_init() == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") + } + } + + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + state + .load_initial_inputs( + &mut executor, + &mut restarting_mgr, + fuzzer.scheduler(), + &corpus_dirs, + ) + .expect(&format!( + "Failed to load initial corpus at {:?}", + &corpus_dirs + )); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + + // Never reached + Ok(()) +} diff --git a/fuzzers/libfuzzer_windows/stb_image.h b/fuzzers/libfuzzer_windows/stb_image.h new file mode 100644 index 0000000000..6542ede682 --- /dev/null +++ b/fuzzers/libfuzzer_windows/stb_image.h @@ -0,0 +1,7762 @@ +/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko [reserved] + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); + if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/fuzzers/libfuzzer_windows/test.bat b/fuzzers/libfuzzer_windows/test.bat new file mode 100644 index 0000000000..eb4fb3e64b --- /dev/null +++ b/fuzzers/libfuzzer_windows/test.bat @@ -0,0 +1,20 @@ +mkdir crashes +del .\.libfuzzer_test.elf + +cargo build --example libfuzzer_windows --release +timeout /T 1 +cp ..\..\target\release\examples\libfuzzer_windows.exe .\.libfuzzer_test.exe +timeout /T 1 + +# The broker +start .\.libfuzzer_test.exe +# Give the broker time to spawn +timeout /T 1 +echo "Spawning client" +start .\.libfuzzer_test.exe +# .\.libfuzzer_test.exe > nul + +timeout /T 10 +echo "Finished fuzzing for a bit" +TASKKILL /IM .libfuzzer_test.exe +del .libfuzzer_test.exe diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index f49f78a635..9d896c7b8a 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -102,6 +102,7 @@ use crate::{ Error, }; +#[cfg(all(unix, feature = "std"))] use super::shmem::HasFd; /// We'll start off with 256 megabyte maps per fuzzer client @@ -450,6 +451,7 @@ where } } +#[cfg(all(unix, feature = "std"))] impl LlmpConnection where SH: ShMem + HasFd, @@ -1888,6 +1890,7 @@ where /// `n` clients connect to a broker. They share an outgoing map with the broker, /// and get incoming messages from the shared broker bus /// If the Shm has a fd, we can attach to it. +#[cfg(all(unix, feature = "std"))] impl LlmpClient where SH: ShMem + HasFd, diff --git a/libafl/src/bolts/os/windows_exceptions.rs b/libafl/src/bolts/os/windows_exceptions.rs index d4796f0397..b960270b3a 100644 --- a/libafl/src/bolts/os/windows_exceptions.rs +++ b/libafl/src/bolts/os/windows_exceptions.rs @@ -1,332 +1,334 @@ -pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS; - -use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error}; - -use alloc::vec::Vec; -use core::{ - cell::UnsafeCell, - convert::TryFrom, - fmt::{self, Display, Formatter}, - ptr::write_volatile, - sync::atomic::{compiler_fence, Ordering}, -}; -use std::os::raw::{c_long, c_void}; - -use num_enum::{IntoPrimitive, TryFromPrimitive}; - -//const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; -//const EXCEPTION_CONTINUE_SEARCH: c_long = 0; -const EXCEPTION_EXECUTE_HANDLER: c_long = 1; - -// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611 -pub const STATUS_WAIT_0: u32 = 0x00000000; -pub const STATUS_ABANDONED_WAIT_0: u32 = 0x00000080; -pub const STATUS_USER_APC: u32 = 0x000000C0; -pub const STATUS_TIMEOUT: u32 = 0x00000102; -pub const STATUS_PENDING: u32 = 0x00000103; -pub const STATUS_SEGMENT_NOTIFICATION: u32 = 0x40000005; -pub const STATUS_FATAL_APP_EXIT: u32 = 0x40000015; -pub const STATUS_GUARD_PAGE_VIOLATION: u32 = 0x80000001; -pub const STATUS_DATATYPE_MISALIGNMENT: u32 = 0x80000002; -pub const STATUS_BREAKPOINT: u32 = 0x80000003; -pub const STATUS_SINGLE_STEP: u32 = 0x80000004; -pub const STATUS_LONGJUMP: u32 = 0x80000026; -pub const STATUS_UNWIND_CONSOLIDATE: u32 = 0x80000029; -pub const STATUS_ACCESS_VIOLATION: u32 = 0xC0000005; -pub const STATUS_IN_PAGE_ERROR: u32 = 0xC0000006; -pub const STATUS_INVALID_HANDLE: u32 = 0xC0000008; -pub const STATUS_NO_MEMORY: u32 = 0xC0000017; -pub const STATUS_ILLEGAL_INSTRUCTION: u32 = 0xC000001D; -pub const STATUS_NONCONTINUABLE_EXCEPTION: u32 = 0xC0000025; -pub const STATUS_INVALID_DISPOSITION: u32 = 0xC0000026; -pub const STATUS_ARRAY_BOUNDS_EXCEEDED: u32 = 0xC000008C; -pub const STATUS_FLOAT_DENORMAL_OPERAND: u32 = 0xC000008D; -pub const STATUS_FLOAT_DIVIDE_BY_ZERO: u32 = 0xC000008E; -pub const STATUS_FLOAT_INEXACT_RESULT: u32 = 0xC000008F; -pub const STATUS_FLOAT_INVALID_OPERATION: u32 = 0xC0000090; -pub const STATUS_FLOAT_OVERFLOW: u32 = 0xC0000091; -pub const STATUS_FLOAT_STACK_CHECK: u32 = 0xC0000092; -pub const STATUS_FLOAT_UNDERFLOW: u32 = 0xC0000093; -pub const STATUS_INTEGER_DIVIDE_BY_ZERO: u32 = 0xC0000094; -pub const STATUS_INTEGER_OVERFLOW: u32 = 0xC0000095; -pub const STATUS_PRIVILEGED_INSTRUCTION: u32 = 0xC0000096; -pub const STATUS_STACK_OVERFLOW: u32 = 0xC00000FD; -pub const STATUS_DLL_NOT_FOUND: u32 = 0xC0000135; -pub const STATUS_ORDINAL_NOT_FOUND: u32 = 0xC0000138; -pub const STATUS_ENTRYPOINT_NOT_FOUND: u32 = 0xC0000139; -pub const STATUS_CONTROL_C_EXIT: u32 = 0xC000013A; -pub const STATUS_DLL_INIT_FAILED: u32 = 0xC0000142; -pub const STATUS_FLOAT_MULTIPLE_FAULTS: u32 = 0xC00002B4; -pub const STATUS_FLOAT_MULTIPLE_TRAPS: u32 = 0xC00002B5; -pub const STATUS_REG_NAT_CONSUMPTION: u32 = 0xC00002C9; -pub const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374; -pub const STATUS_STACK_BUFFER_OVERRUN: u32 = 0xC0000409; -pub const STATUS_INVALID_CRUNTIME_PARAMETER: u32 = 0xC0000417; -pub const STATUS_ASSERTION_FAILURE: u32 = 0xC0000420; -pub const STATUS_SXS_EARLY_DEACTIVATION: u32 = 0xC015000F; -pub const STATUS_SXS_INVALID_DEACTIVATION: u32 = 0xC0150010; - -#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)] -#[repr(u32)] -pub enum ExceptionCode { - // From https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode - AccessViolation = STATUS_ACCESS_VIOLATION, - ArrayBoundsExceeded = STATUS_ARRAY_BOUNDS_EXCEEDED, - Breakpoint = STATUS_BREAKPOINT, - DatatypeMisalignment = STATUS_DATATYPE_MISALIGNMENT, - FltDenormalOperand = STATUS_FLOAT_DENORMAL_OPERAND, - FltDivideByZero = STATUS_FLOAT_DIVIDE_BY_ZERO, - FltInexactResult = STATUS_FLOAT_INEXACT_RESULT, - FltInvalidOperation = STATUS_FLOAT_INVALID_OPERATION, - FltOverflow = STATUS_FLOAT_OVERFLOW, - FltStackCheck = STATUS_FLOAT_STACK_CHECK, - FltUnderflow = STATUS_FLOAT_UNDERFLOW, - GuardPageViolation = STATUS_GUARD_PAGE_VIOLATION, - IllegalInstruction = STATUS_ILLEGAL_INSTRUCTION, - InPageError = STATUS_IN_PAGE_ERROR, - IntegerDivideByZero = STATUS_INTEGER_DIVIDE_BY_ZERO, - IntegerOverflow = STATUS_INTEGER_OVERFLOW, - InvalidDisposition = STATUS_INVALID_DISPOSITION, - InvalidHandle = STATUS_INVALID_HANDLE, - NoncontinuableException = STATUS_NONCONTINUABLE_EXCEPTION, - PrivilegedInstruction = STATUS_PRIVILEGED_INSTRUCTION, - SingleStep = STATUS_SINGLE_STEP, - StackOverflow = STATUS_STACK_OVERFLOW, - UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE, - // Addition exceptions - Wait0 = STATUS_WAIT_0, - AbandonedWait0 = STATUS_ABANDONED_WAIT_0, - UserAPC = STATUS_USER_APC, - Timeout = STATUS_TIMEOUT, - Pending = STATUS_PENDING, - SegmentNotification = STATUS_SEGMENT_NOTIFICATION, - FatalAppExit = STATUS_FATAL_APP_EXIT, - Longjump = STATUS_LONGJUMP, - DLLNotFound = STATUS_DLL_NOT_FOUND, - OrdinalNotFound = STATUS_ORDINAL_NOT_FOUND, - EntryPointNotFound = STATUS_ENTRYPOINT_NOT_FOUND, - ControlCExit = STATUS_CONTROL_C_EXIT, - DllInitFailed = STATUS_DLL_INIT_FAILED, - FltMultipleFaults = STATUS_FLOAT_MULTIPLE_FAULTS, - FltMultipleTraps = STATUS_FLOAT_MULTIPLE_TRAPS, - RegNatConsumption = STATUS_REG_NAT_CONSUMPTION, - HeapCorruption = STATUS_HEAP_CORRUPTION, - StackBufferOverrun = STATUS_STACK_BUFFER_OVERRUN, - InvalidCRuntimeParameter = STATUS_INVALID_CRUNTIME_PARAMETER, - AssertionFailure = STATUS_ASSERTION_FAILURE, - SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION, - SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION, -} - -pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[ - ExceptionCode::AccessViolation, - ExceptionCode::ArrayBoundsExceeded, - ExceptionCode::FltDivideByZero, - ExceptionCode::GuardPageViolation, - ExceptionCode::IllegalInstruction, - ExceptionCode::InPageError, - ExceptionCode::IntegerDivideByZero, - ExceptionCode::InvalidHandle, - ExceptionCode::NoncontinuableException, - ExceptionCode::PrivilegedInstruction, - ExceptionCode::StackOverflow, - ExceptionCode::HeapCorruption, - ExceptionCode::StackBufferOverrun, - ExceptionCode::AssertionFailure, -]; - -impl PartialEq for ExceptionCode { - fn eq(&self, other: &Self) -> bool { - *self as u32 == *other as u32 - } -} - -impl Eq for ExceptionCode {} - -unsafe impl Sync for ExceptionCode {} - -impl Display for ExceptionCode { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - match self { - ExceptionCode::AccessViolation => write!(f, "STATUS_ACCESS_VIOLATION")?, - ExceptionCode::ArrayBoundsExceeded => write!(f, "STATUS_ARRAY_BOUNDS_EXCEEDED")?, - ExceptionCode::Breakpoint => write!(f, "STATUS_BREAKPOINT")?, - ExceptionCode::DatatypeMisalignment => write!(f, "STATUS_DATATYPE_MISALIGNMENT")?, - ExceptionCode::FltDenormalOperand => write!(f, "STATUS_FLOAT_DENORMAL_OPERAND")?, - ExceptionCode::FltDivideByZero => write!(f, "STATUS_FLOAT_DIVIDE_BY_ZERO")?, - ExceptionCode::FltInexactResult => write!(f, "STATUS_FLOAT_INEXACT_RESULT")?, - ExceptionCode::FltInvalidOperation => write!(f, "STATUS_FLOAT_INVALID_OPERATION")?, - ExceptionCode::FltOverflow => write!(f, "STATUS_FLOAT_OVERFLOW")?, - ExceptionCode::FltStackCheck => write!(f, "STATUS_FLOAT_STACK_CHECK")?, - ExceptionCode::FltUnderflow => write!(f, "STATUS_FLOAT_UNDERFLOW")?, - ExceptionCode::GuardPageViolation => write!(f, "STATUS_GUARD_PAGE_VIOLATION")?, - ExceptionCode::IllegalInstruction => write!(f, "STATUS_ILLEGAL_INSTRUCTION")?, - ExceptionCode::InPageError => write!(f, "STATUS_IN_PAGE_ERROR")?, - ExceptionCode::IntegerDivideByZero => write!(f, "STATUS_INTEGER_DIVIDE_BY_ZERO")?, - ExceptionCode::IntegerOverflow => write!(f, "STATUS_INTEGER_OVERFLOW")?, - ExceptionCode::InvalidDisposition => write!(f, "STATUS_INVALID_DISPOSITION")?, - ExceptionCode::InvalidHandle => write!(f, "STATUS_INVALID_HANDLE")?, - ExceptionCode::NoncontinuableException => write!(f, "STATUS_NONCONTINUABLE_EXCEPTION")?, - ExceptionCode::PrivilegedInstruction => write!(f, "STATUS_PRIVILEGED_INSTRUCTION")?, - ExceptionCode::SingleStep => write!(f, "STATUS_SINGLE_STEP")?, - ExceptionCode::StackOverflow => write!(f, "STATUS_STACK_OVERFLOW")?, - ExceptionCode::UnwindConsolidate => write!(f, "STATUS_UNWIND_CONSOLIDATE")?, - ExceptionCode::Wait0 => write!(f, "STATUS_WAIT_0")?, - ExceptionCode::AbandonedWait0 => write!(f, "STATUS_ABANDONED_WAIT_0")?, - ExceptionCode::UserAPC => write!(f, "STATUS_USER_APC")?, - ExceptionCode::Timeout => write!(f, "STATUS_TIMEOUT")?, - ExceptionCode::Pending => write!(f, "STATUS_PENDING")?, - ExceptionCode::SegmentNotification => write!(f, "STATUS_SEGMENT_NOTIFICATION")?, - ExceptionCode::FatalAppExit => write!(f, "STATUS_FATAL_APP_EXIT")?, - ExceptionCode::Longjump => write!(f, "STATUS_LONGJUMP")?, - ExceptionCode::DLLNotFound => write!(f, "STATUS_DLL_NOT_FOUND")?, - ExceptionCode::OrdinalNotFound => write!(f, "STATUS_ORDINAL_NOT_FOUND")?, - ExceptionCode::EntryPointNotFound => write!(f, "STATUS_ENTRYPOINT_NOT_FOUND")?, - ExceptionCode::ControlCExit => write!(f, "STATUS_CONTROL_C_EXIT")?, - ExceptionCode::DllInitFailed => write!(f, "STATUS_DLL_INIT_FAILED")?, - ExceptionCode::FltMultipleFaults => write!(f, "STATUS_FLOAT_MULTIPLE_FAULTS")?, - ExceptionCode::FltMultipleTraps => write!(f, "STATUS_FLOAT_MULTIPLE_TRAPS")?, - ExceptionCode::RegNatConsumption => write!(f, "STATUS_REG_NAT_CONSUMPTION")?, - ExceptionCode::HeapCorruption => write!(f, "STATUS_HEAP_CORRUPTION")?, - ExceptionCode::StackBufferOverrun => write!(f, "STATUS_STACK_BUFFER_OVERRUN")?, - ExceptionCode::InvalidCRuntimeParameter => { - write!(f, "STATUS_INVALID_CRUNTIME_PARAMETER")? - } - ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?, - ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?, - ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?, - }; - - Ok(()) - } -} - -pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 45] = [ - ExceptionCode::AccessViolation, - ExceptionCode::ArrayBoundsExceeded, - ExceptionCode::Breakpoint, - ExceptionCode::DatatypeMisalignment, - ExceptionCode::FltDenormalOperand, - ExceptionCode::FltDivideByZero, - ExceptionCode::FltInexactResult, - ExceptionCode::FltInvalidOperation, - ExceptionCode::FltOverflow, - ExceptionCode::FltStackCheck, - ExceptionCode::FltUnderflow, - ExceptionCode::GuardPageViolation, - ExceptionCode::IllegalInstruction, - ExceptionCode::InPageError, - ExceptionCode::IntegerDivideByZero, - ExceptionCode::IntegerOverflow, - ExceptionCode::InvalidDisposition, - ExceptionCode::InvalidHandle, - ExceptionCode::NoncontinuableException, - ExceptionCode::PrivilegedInstruction, - ExceptionCode::SingleStep, - ExceptionCode::StackOverflow, - ExceptionCode::UnwindConsolidate, - ExceptionCode::Wait0, - ExceptionCode::AbandonedWait0, - ExceptionCode::UserAPC, - ExceptionCode::Timeout, - ExceptionCode::Pending, - ExceptionCode::SegmentNotification, - ExceptionCode::FatalAppExit, - ExceptionCode::Longjump, - ExceptionCode::DLLNotFound, - ExceptionCode::OrdinalNotFound, - ExceptionCode::EntryPointNotFound, - ExceptionCode::ControlCExit, - ExceptionCode::DllInitFailed, - ExceptionCode::FltMultipleFaults, - ExceptionCode::FltMultipleTraps, - ExceptionCode::RegNatConsumption, - ExceptionCode::HeapCorruption, - ExceptionCode::StackBufferOverrun, - ExceptionCode::InvalidCRuntimeParameter, - ExceptionCode::AssertionFailure, - ExceptionCode::SXSEarlyDeactivation, - ExceptionCode::SXSInvalidDeactivation, -]; - -pub trait Handler { - /// Handle an exception - fn handle( - &mut self, - exception_code: ExceptionCode, - exception_pointers: *mut EXCEPTION_POINTERS, - ); - /// Return a list of exceptions to handle - fn exceptions(&self) -> Vec; -} - -struct HandlerHolder { - handler: UnsafeCell<*mut dyn Handler>, -} - -unsafe impl Send for HandlerHolder {} - -/// Keep track of which handler is registered for which exception -static mut EXCEPTION_HANDLERS: [Option; 64] = [ - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, -]; - -type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long; -static mut PREVIOUS_HANDLER: Option = None; - -/// Internal function that is being called whenever an exception arrives (stdcall). -unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long { - let code = exception_pointers - .as_mut() - .unwrap() - .exception_record - .as_mut() - .unwrap() - .exception_code; - let exception_code = ExceptionCode::try_from(code).unwrap(); - let index = EXCEPTION_CODES_MAPPING - .iter() - .position(|x| *x == exception_code) - .unwrap(); - let ret = match &EXCEPTION_HANDLERS[index] { - Some(handler_holder) => { - let handler = &mut **handler_holder.handler.get(); - handler.handle(exception_code, exception_pointers); - EXCEPTION_EXECUTE_HANDLER - } - None => EXCEPTION_EXECUTE_HANDLER, - }; - if let Some(prev_handler) = PREVIOUS_HANDLER { - prev_handler(exception_pointers) - } else { - ret - } -} - -/// Setup Win32 exception handlers in a somewhat rusty way. -pub unsafe fn setup_exception_handler(handler: &mut T) -> Result<(), Error> { - let exceptions = handler.exceptions(); - for exception_code in exceptions { - let index = EXCEPTION_CODES_MAPPING - .iter() - .position(|x| *x == exception_code) - .unwrap(); - write_volatile( - &mut EXCEPTION_HANDLERS[index], - Some(HandlerHolder { - handler: UnsafeCell::new(handler as *mut dyn Handler), - }), - ); - } - compiler_fence(Ordering::SeqCst); - - if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute( - handle_exception as *const c_void, - ))) { - PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void)); - } - Ok(()) -} +pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS; + +use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error}; + +use alloc::vec::Vec; +use core::{ + cell::UnsafeCell, + convert::TryFrom, + fmt::{self, Display, Formatter}, + ptr::write_volatile, + sync::atomic::{compiler_fence, Ordering}, +}; +use std::os::raw::{c_long, c_void}; + +use num_enum::{IntoPrimitive, TryFromPrimitive}; + +//const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; +//const EXCEPTION_CONTINUE_SEARCH: c_long = 0; +const EXCEPTION_EXECUTE_HANDLER: c_long = 1; + +// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611 +pub const STATUS_WAIT_0: u32 = 0x00000000; +pub const STATUS_ABANDONED_WAIT_0: u32 = 0x00000080; +pub const STATUS_USER_APC: u32 = 0x000000C0; +pub const STATUS_TIMEOUT: u32 = 0x00000102; +pub const STATUS_PENDING: u32 = 0x00000103; +pub const STATUS_SEGMENT_NOTIFICATION: u32 = 0x40000005; +pub const STATUS_FATAL_APP_EXIT: u32 = 0x40000015; +pub const STATUS_GUARD_PAGE_VIOLATION: u32 = 0x80000001; +pub const STATUS_DATATYPE_MISALIGNMENT: u32 = 0x80000002; +pub const STATUS_BREAKPOINT: u32 = 0x80000003; +pub const STATUS_SINGLE_STEP: u32 = 0x80000004; +pub const STATUS_LONGJUMP: u32 = 0x80000026; +pub const STATUS_UNWIND_CONSOLIDATE: u32 = 0x80000029; +pub const STATUS_ACCESS_VIOLATION: u32 = 0xC0000005; +pub const STATUS_IN_PAGE_ERROR: u32 = 0xC0000006; +pub const STATUS_INVALID_HANDLE: u32 = 0xC0000008; +pub const STATUS_NO_MEMORY: u32 = 0xC0000017; +pub const STATUS_ILLEGAL_INSTRUCTION: u32 = 0xC000001D; +pub const STATUS_NONCONTINUABLE_EXCEPTION: u32 = 0xC0000025; +pub const STATUS_INVALID_DISPOSITION: u32 = 0xC0000026; +pub const STATUS_ARRAY_BOUNDS_EXCEEDED: u32 = 0xC000008C; +pub const STATUS_FLOAT_DENORMAL_OPERAND: u32 = 0xC000008D; +pub const STATUS_FLOAT_DIVIDE_BY_ZERO: u32 = 0xC000008E; +pub const STATUS_FLOAT_INEXACT_RESULT: u32 = 0xC000008F; +pub const STATUS_FLOAT_INVALID_OPERATION: u32 = 0xC0000090; +pub const STATUS_FLOAT_OVERFLOW: u32 = 0xC0000091; +pub const STATUS_FLOAT_STACK_CHECK: u32 = 0xC0000092; +pub const STATUS_FLOAT_UNDERFLOW: u32 = 0xC0000093; +pub const STATUS_INTEGER_DIVIDE_BY_ZERO: u32 = 0xC0000094; +pub const STATUS_INTEGER_OVERFLOW: u32 = 0xC0000095; +pub const STATUS_PRIVILEGED_INSTRUCTION: u32 = 0xC0000096; +pub const STATUS_STACK_OVERFLOW: u32 = 0xC00000FD; +pub const STATUS_DLL_NOT_FOUND: u32 = 0xC0000135; +pub const STATUS_ORDINAL_NOT_FOUND: u32 = 0xC0000138; +pub const STATUS_ENTRYPOINT_NOT_FOUND: u32 = 0xC0000139; +pub const STATUS_CONTROL_C_EXIT: u32 = 0xC000013A; +pub const STATUS_DLL_INIT_FAILED: u32 = 0xC0000142; +pub const STATUS_FLOAT_MULTIPLE_FAULTS: u32 = 0xC00002B4; +pub const STATUS_FLOAT_MULTIPLE_TRAPS: u32 = 0xC00002B5; +pub const STATUS_REG_NAT_CONSUMPTION: u32 = 0xC00002C9; +pub const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374; +pub const STATUS_STACK_BUFFER_OVERRUN: u32 = 0xC0000409; +pub const STATUS_INVALID_CRUNTIME_PARAMETER: u32 = 0xC0000417; +pub const STATUS_ASSERTION_FAILURE: u32 = 0xC0000420; +pub const STATUS_SXS_EARLY_DEACTIVATION: u32 = 0xC015000F; +pub const STATUS_SXS_INVALID_DEACTIVATION: u32 = 0xC0150010; + +#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)] +#[repr(u32)] +pub enum ExceptionCode { + // From https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode + AccessViolation = STATUS_ACCESS_VIOLATION, + ArrayBoundsExceeded = STATUS_ARRAY_BOUNDS_EXCEEDED, + Breakpoint = STATUS_BREAKPOINT, + DatatypeMisalignment = STATUS_DATATYPE_MISALIGNMENT, + FltDenormalOperand = STATUS_FLOAT_DENORMAL_OPERAND, + FltDivideByZero = STATUS_FLOAT_DIVIDE_BY_ZERO, + FltInexactResult = STATUS_FLOAT_INEXACT_RESULT, + FltInvalidOperation = STATUS_FLOAT_INVALID_OPERATION, + FltOverflow = STATUS_FLOAT_OVERFLOW, + FltStackCheck = STATUS_FLOAT_STACK_CHECK, + FltUnderflow = STATUS_FLOAT_UNDERFLOW, + GuardPageViolation = STATUS_GUARD_PAGE_VIOLATION, + IllegalInstruction = STATUS_ILLEGAL_INSTRUCTION, + InPageError = STATUS_IN_PAGE_ERROR, + IntegerDivideByZero = STATUS_INTEGER_DIVIDE_BY_ZERO, + IntegerOverflow = STATUS_INTEGER_OVERFLOW, + InvalidDisposition = STATUS_INVALID_DISPOSITION, + InvalidHandle = STATUS_INVALID_HANDLE, + NoncontinuableException = STATUS_NONCONTINUABLE_EXCEPTION, + PrivilegedInstruction = STATUS_PRIVILEGED_INSTRUCTION, + SingleStep = STATUS_SINGLE_STEP, + StackOverflow = STATUS_STACK_OVERFLOW, + UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE, + // Addition exceptions + Wait0 = STATUS_WAIT_0, + AbandonedWait0 = STATUS_ABANDONED_WAIT_0, + UserAPC = STATUS_USER_APC, + Timeout = STATUS_TIMEOUT, + Pending = STATUS_PENDING, + SegmentNotification = STATUS_SEGMENT_NOTIFICATION, + FatalAppExit = STATUS_FATAL_APP_EXIT, + Longjump = STATUS_LONGJUMP, + DLLNotFound = STATUS_DLL_NOT_FOUND, + OrdinalNotFound = STATUS_ORDINAL_NOT_FOUND, + EntryPointNotFound = STATUS_ENTRYPOINT_NOT_FOUND, + ControlCExit = STATUS_CONTROL_C_EXIT, + DllInitFailed = STATUS_DLL_INIT_FAILED, + FltMultipleFaults = STATUS_FLOAT_MULTIPLE_FAULTS, + FltMultipleTraps = STATUS_FLOAT_MULTIPLE_TRAPS, + RegNatConsumption = STATUS_REG_NAT_CONSUMPTION, + HeapCorruption = STATUS_HEAP_CORRUPTION, + StackBufferOverrun = STATUS_STACK_BUFFER_OVERRUN, + InvalidCRuntimeParameter = STATUS_INVALID_CRUNTIME_PARAMETER, + AssertionFailure = STATUS_ASSERTION_FAILURE, + SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION, + SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION, +} + +pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[ + ExceptionCode::AccessViolation, + ExceptionCode::ArrayBoundsExceeded, + ExceptionCode::FltDivideByZero, + ExceptionCode::GuardPageViolation, + ExceptionCode::IllegalInstruction, + ExceptionCode::InPageError, + ExceptionCode::IntegerDivideByZero, + ExceptionCode::InvalidHandle, + ExceptionCode::NoncontinuableException, + ExceptionCode::PrivilegedInstruction, + ExceptionCode::StackOverflow, + ExceptionCode::HeapCorruption, + ExceptionCode::StackBufferOverrun, + ExceptionCode::AssertionFailure, +]; + +impl PartialEq for ExceptionCode { + fn eq(&self, other: &Self) -> bool { + *self as u32 == *other as u32 + } +} + +impl Eq for ExceptionCode {} + +unsafe impl Sync for ExceptionCode {} + +impl Display for ExceptionCode { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + match self { + ExceptionCode::AccessViolation => write!(f, "STATUS_ACCESS_VIOLATION")?, + ExceptionCode::ArrayBoundsExceeded => write!(f, "STATUS_ARRAY_BOUNDS_EXCEEDED")?, + ExceptionCode::Breakpoint => write!(f, "STATUS_BREAKPOINT")?, + ExceptionCode::DatatypeMisalignment => write!(f, "STATUS_DATATYPE_MISALIGNMENT")?, + ExceptionCode::FltDenormalOperand => write!(f, "STATUS_FLOAT_DENORMAL_OPERAND")?, + ExceptionCode::FltDivideByZero => write!(f, "STATUS_FLOAT_DIVIDE_BY_ZERO")?, + ExceptionCode::FltInexactResult => write!(f, "STATUS_FLOAT_INEXACT_RESULT")?, + ExceptionCode::FltInvalidOperation => write!(f, "STATUS_FLOAT_INVALID_OPERATION")?, + ExceptionCode::FltOverflow => write!(f, "STATUS_FLOAT_OVERFLOW")?, + ExceptionCode::FltStackCheck => write!(f, "STATUS_FLOAT_STACK_CHECK")?, + ExceptionCode::FltUnderflow => write!(f, "STATUS_FLOAT_UNDERFLOW")?, + ExceptionCode::GuardPageViolation => write!(f, "STATUS_GUARD_PAGE_VIOLATION")?, + ExceptionCode::IllegalInstruction => write!(f, "STATUS_ILLEGAL_INSTRUCTION")?, + ExceptionCode::InPageError => write!(f, "STATUS_IN_PAGE_ERROR")?, + ExceptionCode::IntegerDivideByZero => write!(f, "STATUS_INTEGER_DIVIDE_BY_ZERO")?, + ExceptionCode::IntegerOverflow => write!(f, "STATUS_INTEGER_OVERFLOW")?, + ExceptionCode::InvalidDisposition => write!(f, "STATUS_INVALID_DISPOSITION")?, + ExceptionCode::InvalidHandle => write!(f, "STATUS_INVALID_HANDLE")?, + ExceptionCode::NoncontinuableException => write!(f, "STATUS_NONCONTINUABLE_EXCEPTION")?, + ExceptionCode::PrivilegedInstruction => write!(f, "STATUS_PRIVILEGED_INSTRUCTION")?, + ExceptionCode::SingleStep => write!(f, "STATUS_SINGLE_STEP")?, + ExceptionCode::StackOverflow => write!(f, "STATUS_STACK_OVERFLOW")?, + ExceptionCode::UnwindConsolidate => write!(f, "STATUS_UNWIND_CONSOLIDATE")?, + ExceptionCode::Wait0 => write!(f, "STATUS_WAIT_0")?, + ExceptionCode::AbandonedWait0 => write!(f, "STATUS_ABANDONED_WAIT_0")?, + ExceptionCode::UserAPC => write!(f, "STATUS_USER_APC")?, + ExceptionCode::Timeout => write!(f, "STATUS_TIMEOUT")?, + ExceptionCode::Pending => write!(f, "STATUS_PENDING")?, + ExceptionCode::SegmentNotification => write!(f, "STATUS_SEGMENT_NOTIFICATION")?, + ExceptionCode::FatalAppExit => write!(f, "STATUS_FATAL_APP_EXIT")?, + ExceptionCode::Longjump => write!(f, "STATUS_LONGJUMP")?, + ExceptionCode::DLLNotFound => write!(f, "STATUS_DLL_NOT_FOUND")?, + ExceptionCode::OrdinalNotFound => write!(f, "STATUS_ORDINAL_NOT_FOUND")?, + ExceptionCode::EntryPointNotFound => write!(f, "STATUS_ENTRYPOINT_NOT_FOUND")?, + ExceptionCode::ControlCExit => write!(f, "STATUS_CONTROL_C_EXIT")?, + ExceptionCode::DllInitFailed => write!(f, "STATUS_DLL_INIT_FAILED")?, + ExceptionCode::FltMultipleFaults => write!(f, "STATUS_FLOAT_MULTIPLE_FAULTS")?, + ExceptionCode::FltMultipleTraps => write!(f, "STATUS_FLOAT_MULTIPLE_TRAPS")?, + ExceptionCode::RegNatConsumption => write!(f, "STATUS_REG_NAT_CONSUMPTION")?, + ExceptionCode::HeapCorruption => write!(f, "STATUS_HEAP_CORRUPTION")?, + ExceptionCode::StackBufferOverrun => write!(f, "STATUS_STACK_BUFFER_OVERRUN")?, + ExceptionCode::InvalidCRuntimeParameter => { + write!(f, "STATUS_INVALID_CRUNTIME_PARAMETER")? + } + ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?, + ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?, + ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?, + }; + + Ok(()) + } +} + +pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 45] = [ + ExceptionCode::AccessViolation, + ExceptionCode::ArrayBoundsExceeded, + ExceptionCode::Breakpoint, + ExceptionCode::DatatypeMisalignment, + ExceptionCode::FltDenormalOperand, + ExceptionCode::FltDivideByZero, + ExceptionCode::FltInexactResult, + ExceptionCode::FltInvalidOperation, + ExceptionCode::FltOverflow, + ExceptionCode::FltStackCheck, + ExceptionCode::FltUnderflow, + ExceptionCode::GuardPageViolation, + ExceptionCode::IllegalInstruction, + ExceptionCode::InPageError, + ExceptionCode::IntegerDivideByZero, + ExceptionCode::IntegerOverflow, + ExceptionCode::InvalidDisposition, + ExceptionCode::InvalidHandle, + ExceptionCode::NoncontinuableException, + ExceptionCode::PrivilegedInstruction, + ExceptionCode::SingleStep, + ExceptionCode::StackOverflow, + ExceptionCode::UnwindConsolidate, + ExceptionCode::Wait0, + ExceptionCode::AbandonedWait0, + ExceptionCode::UserAPC, + ExceptionCode::Timeout, + ExceptionCode::Pending, + ExceptionCode::SegmentNotification, + ExceptionCode::FatalAppExit, + ExceptionCode::Longjump, + ExceptionCode::DLLNotFound, + ExceptionCode::OrdinalNotFound, + ExceptionCode::EntryPointNotFound, + ExceptionCode::ControlCExit, + ExceptionCode::DllInitFailed, + ExceptionCode::FltMultipleFaults, + ExceptionCode::FltMultipleTraps, + ExceptionCode::RegNatConsumption, + ExceptionCode::HeapCorruption, + ExceptionCode::StackBufferOverrun, + ExceptionCode::InvalidCRuntimeParameter, + ExceptionCode::AssertionFailure, + ExceptionCode::SXSEarlyDeactivation, + ExceptionCode::SXSInvalidDeactivation, +]; + +pub trait Handler { + /// Handle an exception + fn handle( + &mut self, + exception_code: ExceptionCode, + exception_pointers: *mut EXCEPTION_POINTERS, + ); + /// Return a list of exceptions to handle + fn exceptions(&self) -> Vec; +} + +struct HandlerHolder { + handler: UnsafeCell<*mut dyn Handler>, +} + +unsafe impl Send for HandlerHolder {} + +/// Keep track of which handler is registered for which exception +static mut EXCEPTION_HANDLERS: [Option; 64] = [ + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, +]; + +type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long; +static mut PREVIOUS_HANDLER: Option = None; + +/// Internal function that is being called whenever an exception arrives (stdcall). +unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long { + let code = exception_pointers + .as_mut() + .unwrap() + .exception_record + .as_mut() + .unwrap() + .exception_code; + let exception_code = ExceptionCode::try_from(code).unwrap(); + let index = EXCEPTION_CODES_MAPPING + .iter() + .position(|x| *x == exception_code) + .unwrap(); + let ret = match &EXCEPTION_HANDLERS[index] { + Some(handler_holder) => { + let handler = &mut **handler_holder.handler.get(); + handler.handle(exception_code, exception_pointers); + EXCEPTION_EXECUTE_HANDLER + } + None => EXCEPTION_EXECUTE_HANDLER, + }; + if let Some(prev_handler) = PREVIOUS_HANDLER { + prev_handler(exception_pointers) + } else { + ret + } +} + +/// Setup Win32 exception handlers in a somewhat rusty way. +/// # Safety +/// Exception handlers are usually ugly, handle with care! +pub unsafe fn setup_exception_handler(handler: &mut T) -> Result<(), Error> { + let exceptions = handler.exceptions(); + for exception_code in exceptions { + let index = EXCEPTION_CODES_MAPPING + .iter() + .position(|x| *x == exception_code) + .unwrap(); + write_volatile( + &mut EXCEPTION_HANDLERS[index], + Some(HandlerHolder { + handler: UnsafeCell::new(handler as *mut dyn Handler), + }), + ); + } + compiler_fence(Ordering::SeqCst); + + if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute( + handle_exception as *const c_void, + ))) { + PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void)); + } + Ok(()) +} diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 999e21591b..f3a95d6092 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -98,6 +98,7 @@ pub trait ShMem: Sized + Debug { } /// shared maps that have an id can use this trait +//#[cfg(all(unix, feature = "std"))] pub trait HasFd { /// Retrieve the id of this shared map fn shm_id(&self) -> i32; @@ -519,9 +520,8 @@ pub mod shmem { String::from_utf8_lossy(map_str_bytes) ))); } - let map = - MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; - if map == ptr::null_mut() { + let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; + if map.is_null() { return Err(Error::Unknown(format!( "Cannot map shared memory {}", String::from_utf8_lossy(map_str_bytes) @@ -558,8 +558,7 @@ pub mod shmem { String::from_utf8_lossy(map_str_bytes) ))); } - let map = - MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; + let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; if map == ptr::null_mut() { return Err(Error::Unknown(format!( "Cannot map shared memory {}", @@ -568,9 +567,9 @@ pub mod shmem { } let mut ret = Self { shm_str: [0; 20], - handle: handle, - map: map, - map_size: map_size, + handle, + map, + map_size, }; ret.shm_str.clone_from_slice(&map_str_bytes[0..20]); Ok(ret) diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index c8984d0dad..e8ec17ffc0 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -16,10 +16,14 @@ use crate::utils::{fork, ForkResult}; #[cfg(all(feature = "std", unix))] use crate::bolts::shmem::UnixShMem; + +#[cfg(all(feature = "std", unix))] +use crate::bolts::shmem::HasFd; + use crate::{ bolts::{ llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag}, - shmem::{HasFd, ShMem}, + shmem::ShMem, }, corpus::CorpusScheduler, events::{BrokerEventResult, Event, EventManager}, @@ -304,6 +308,7 @@ where } } +#[cfg(all(feature = "std", unix))] impl LlmpEventManager where I: Input, @@ -511,7 +516,7 @@ pub fn setup_restarting_mgr( where I: Input, S: DeserializeOwned + IfInteresting, - SH: ShMem + HasFd, // Todo: HasFd is only needed for Android + SH: ShMem, // Todo: HasFd is only needed for Android ST: Stats, { let mut mgr; diff --git a/libafl_cc/src/runtime.rs b/libafl_cc/src/runtime.rs index 9a2cc92586..aeebf41085 100644 --- a/libafl_cc/src/runtime.rs +++ b/libafl_cc/src/runtime.rs @@ -13,11 +13,13 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { #[no_mangle] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { - if start == stop || *start != 0 { return } - + if start == stop || *start != 0 { + return; + } + while start < stop { MAX_EDGES_NUM += 1; - *start = (MAX_EDGES_NUM & (MAP_SIZE -1)) as u32; + *start = (MAX_EDGES_NUM & (MAP_SIZE - 1)) as u32; start = start.offset(1); } } From 1d2897442be4b241d8f684c22e73c5724beb8d85 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 23 Mar 2021 14:55:55 +0100 Subject: [PATCH 016/104] clang wrapper extend api --- libafl_cc/src/lib.rs | 71 ++++++++++++++++++++++++++++++++-------- libafl_cc/src/runtime.rs | 23 ------------- 2 files changed, 57 insertions(+), 37 deletions(-) delete mode 100644 libafl_cc/src/runtime.rs diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 7c58bcbea9..e366d8cae0 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -9,13 +9,30 @@ pub enum Error { /// Wrap a compiler hijacking its arguments pub trait CompilerWrapper { /// Set the wrapper arguments parsing a command line set of arguments - fn from_args<'a>(&'a mut self, args: Vec) -> Result<&'a mut Self, Error>; + fn from_args<'a>(&'a mut self, args: &[String]) -> Result<&'a mut Self, Error>; /// Add a compiler argument fn add_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error>; + /// Add a compiler argument only when compiling + fn add_cc_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error>; + + /// Add a compiler argument only when linking + fn add_link_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error>; + + /// Command to run the compiler + fn command(&mut self) -> Result, Error>; + + /// Get if in linking mode + fn is_linking(&self) -> bool; + /// Run the compiler - fn compile(&mut self) -> Result<(), Error>; + fn run(&mut self) -> Result<(), Error> { + // TODO subproc + let args = self.command()?; + println!("{:?}", args); + Ok(()) + } } /// Wrap Clang @@ -30,11 +47,13 @@ pub struct ClangWrapper { x_set: bool, bit_mode: u32, - args: Vec, + base_args: Vec, + cc_args: Vec, + link_args: Vec, } impl CompilerWrapper for ClangWrapper { - fn from_args<'a>(&'a mut self, args: Vec) -> Result<&'a mut Self, Error> { + fn from_args<'a>(&'a mut self, args: &[String]) -> Result<&'a mut Self, Error> { let mut new_args = vec![]; if args.len() < 1 { return Err(Error::InvalidArguments( @@ -83,26 +102,43 @@ impl CompilerWrapper for ClangWrapper { // Fuzzing define common among tools new_args.push("-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1".into()); - self.args = new_args; + self.base_args = new_args; Ok(self) } fn add_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error> { - self.args.push(arg); + self.base_args.push(arg); Ok(self) } - fn compile(&mut self) -> Result<(), Error> { + fn add_cc_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error> { + self.cc_args.push(arg); + Ok(self) + } + + fn add_link_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error> { + self.link_args.push(arg); + Ok(self) + } + + fn command(&mut self) -> Result, Error> { + let mut args = self.base_args.clone(); if self.linking { if self.x_set { - self.args.push("-x".into()); - self.args.push("none".into()); + args.push("-x".into()); + args.push("none".into()); } + + args.extend_from_slice(self.link_args.as_slice()); + } else { + args.extend_from_slice(self.cc_args.as_slice()); } - println!("{:?}", self.args); + Ok(args) + } - Ok(()) + fn is_linking(&self) -> bool { + self.linking } } @@ -117,9 +153,16 @@ impl ClangWrapper { linking: false, x_set: false, bit_mode: 0, - args: vec![], + base_args: vec![], + cc_args: vec![], + link_args: vec![], } } + + pub fn dont_optimize<'a>(&'a mut self) -> &'a mut Self { + self.optimize = false; + self + } } #[cfg(test)] @@ -129,9 +172,9 @@ mod tests { #[test] fn test_clang_version() { ClangWrapper::new("clang", "clang++") - .from_args(vec!["my-clang".into(), "-v".into()]) + .from_args(&["my-clang".into(), "-v".into()]) .unwrap() - .compile() + .run() .unwrap(); } } diff --git a/libafl_cc/src/runtime.rs b/libafl_cc/src/runtime.rs deleted file mode 100644 index 9a2cc92586..0000000000 --- a/libafl_cc/src/runtime.rs +++ /dev/null @@ -1,23 +0,0 @@ -pub const MAP_SIZE: usize = 65536; - -pub static mut EDGES_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; -pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; -pub static mut MAX_EDGES_NUM: usize = 0; - -#[no_mangle] -pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { - let pos = *guard as usize; - let val = (EDGES_MAP[pos] as u8).wrapping_add(1); - EDGES_MAP[pos] = val; -} - -#[no_mangle] -pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { - if start == stop || *start != 0 { return } - - while start < stop { - MAX_EDGES_NUM += 1; - *start = (MAX_EDGES_NUM & (MAP_SIZE -1)) as u32; - start = start.offset(1); - } -} From 1c8cdc76a850dad7832f304d1d9d47cd9174bf7a Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 23 Mar 2021 20:39:23 +0100 Subject: [PATCH 017/104] create libafl_targets and start new structure for libfuzzer_libpng --- Cargo.toml | 6 +- fuzzers/libfuzzer_libpng/Cargo.toml | 21 +- fuzzers/libfuzzer_libpng/src/bin/cc.rs | 13 ++ fuzzers/libfuzzer_libpng_old/.gitignore | 1 + fuzzers/libfuzzer_libpng_old/Cargo.toml | 31 +++ fuzzers/libfuzzer_libpng_old/README.md | 25 +++ .../build.rs | 0 .../libfuzzer_libpng_old/corpus/not_kitty.png | Bin 0 -> 218 bytes .../corpus/not_kitty_alpha.png | Bin 0 -> 376 bytes .../corpus/not_kitty_gamma.png | Bin 0 -> 228 bytes .../corpus/not_kitty_icc.png | Bin 0 -> 427 bytes .../harness.cc | 0 fuzzers/libfuzzer_libpng_old/src/fuzzer.rs | 179 ++++++++++++++++++ .../test.sh | 0 libafl_cc/src/bin/libafl-cc.rs | 3 - libafl_cc/src/lib.rs | 2 +- libafl_targets/Cargo.toml | 15 ++ libafl_targets/build.rs | 22 +++ libafl_targets/libfuzzer_compatibility.c | 25 +++ libafl_targets/src/lib.rs | 10 + libafl_targets/src/sancov.rs | 26 +++ 21 files changed, 363 insertions(+), 16 deletions(-) create mode 100644 fuzzers/libfuzzer_libpng/src/bin/cc.rs create mode 100644 fuzzers/libfuzzer_libpng_old/.gitignore create mode 100644 fuzzers/libfuzzer_libpng_old/Cargo.toml create mode 100644 fuzzers/libfuzzer_libpng_old/README.md rename fuzzers/{libfuzzer_libpng => libfuzzer_libpng_old}/build.rs (100%) create mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png create mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty_alpha.png create mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png create mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png rename fuzzers/{libfuzzer_libpng => libfuzzer_libpng_old}/harness.cc (100%) create mode 100644 fuzzers/libfuzzer_libpng_old/src/fuzzer.rs rename fuzzers/{libfuzzer_libpng => libfuzzer_libpng_old}/test.sh (100%) delete mode 100644 libafl_cc/src/bin/libafl-cc.rs create mode 100644 libafl_targets/Cargo.toml create mode 100644 libafl_targets/build.rs create mode 100644 libafl_targets/libfuzzer_compatibility.c create mode 100644 libafl_targets/src/lib.rs create mode 100644 libafl_targets/src/sancov.rs diff --git a/Cargo.toml b/Cargo.toml index bc3ba2904a..38b637badc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,15 @@ members = [ "libafl", "libafl_derive", "libafl_cc", + "libafl_targets", #example fuzzers - "fuzzers/libfuzzer_libpng", + #"fuzzers/libfuzzer_libpng", "fuzzers/frida_libpng", "fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libpng_cmpalloc", "fuzzers/libfuzzer_windows", ] +exclude = [ + "fuzzers/libfuzzer_libpng", +] diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index 7c0615cfd0..b4892d868c 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -3,7 +3,6 @@ name = "libfuzzer_libpng" version = "0.1.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" -build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,21 +10,21 @@ build = "build.rs" default = ["std"] std = [] -#[profile.release] -#lto = true -#codegen-units = 1 -#opt-level = 3 -#debug = true - -[build-dependencies] -cc = { version = "1.0", features = ["parallel"] } -num_cpus = "1.0" +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = true [dependencies] libafl = { path = "../../libafl/" } +# TODO Include it only when building cc +libafl_cc = { path = "../../libafl_cc/" } -[[example]] +[lib] name = "libfuzzer_libpng" path = "./src/fuzzer.rs" test = false bench = false +crate-type = ["staticlib"] + diff --git a/fuzzers/libfuzzer_libpng/src/bin/cc.rs b/fuzzers/libfuzzer_libpng/src/bin/cc.rs new file mode 100644 index 0000000000..ad820b5904 --- /dev/null +++ b/fuzzers/libfuzzer_libpng/src/bin/cc.rs @@ -0,0 +1,13 @@ +use libafl_cc::{ClangWrapper, CompilerWrapper}; +use std::env; + +fn main() { + let args: Vec = env::args().collect(); + ClangWrapper::new("clang", "clang++") + .from_args(&args) + .unwrap() + .add_arg("-fsanitize=trace-pc-guard".into()) + .unwrap() + .run() + .unwrap(); +} diff --git a/fuzzers/libfuzzer_libpng_old/.gitignore b/fuzzers/libfuzzer_libpng_old/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/libfuzzer_libpng_old/Cargo.toml b/fuzzers/libfuzzer_libpng_old/Cargo.toml new file mode 100644 index 0000000000..7c0615cfd0 --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "libfuzzer_libpng" +version = "0.1.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["std"] +std = [] + +#[profile.release] +#lto = true +#codegen-units = 1 +#opt-level = 3 +#debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +num_cpus = "1.0" + +[dependencies] +libafl = { path = "../../libafl/" } + +[[example]] +name = "libfuzzer_libpng" +path = "./src/fuzzer.rs" +test = false +bench = false diff --git a/fuzzers/libfuzzer_libpng_old/README.md b/fuzzers/libfuzzer_libpng_old/README.md new file mode 100644 index 0000000000..f56138c2b5 --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/README.md @@ -0,0 +1,25 @@ +# Libfuzzer for libpng + +This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. +To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. +It has been tested on Linux. + +## Build + +To build this example, run `cargo build --example libfuzzer_libpng --release`. +This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. +Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. +Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. + +## Run + +The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. + +Each following execution will run a fuzzer client. +As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). +This means each client will start itself again to listen for crashes and timeouts. +By restarting the actual fuzzer, it can recover from these exit conditions. + +In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). + +For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/libfuzzer_libpng/build.rs b/fuzzers/libfuzzer_libpng_old/build.rs similarity index 100% rename from fuzzers/libfuzzer_libpng/build.rs rename to fuzzers/libfuzzer_libpng_old/build.rs diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png new file mode 100644 index 0000000000000000000000000000000000000000..eff7c1707b936a8f8df725814f604d454b78b5c3 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X_yc@GT+_~+`TzevkY_wIZRYx+5&y#hyq+?%!C8<`)MX5lF!N|bSRM)^r*U&J;z}U*bz{;0L z1Vuw`eoAIqC5i?kD`P_|6GMoGiCWXn12ss3YzWRzD=AMbN@Z|N$xljE@XSq2PYp^< WOsOn9nQ8-6#Ng@b=d#Wzp$PyV*n0l} literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..939d9d29a9b9f95bac5e9a72854361ee85469921 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmTQ929t;oCfmw1AIbU z)6Sgv|NlRbXFM})=KnKxKI=t+9LW;bh?3y^w370~qErUQl>DSr1<%~X^wgl##FWay zlc_d9MbVxvjv*GO?@o5)YH;9THa`3B|5>?^8?LvjJ}xLe>!7e@k)r^sLedir0mCVe z=5sMjEm$*~tHD+}{NS_$nMdb|ABqg-@UGMMsZ=uY-X%Cq@&3vmZ%&@H{P?6&+U!yq VvuXWlo?M_c44$rjF6*2UngF4cP+$N6 literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c7804d99829cc6307c1c6ae9915cf42d555414 GIT binary patch literal 427 zcmV;c0aX5pP)9xSWu9|B*4Isn^#g47m^r~thH)GiR<@yX0fO)OF<2Kt#qCldyUF#H?{4jV?XGw9)psxE&K1B1m^ z1_tH{2(hG@3=G>_85ksPA;eS`Ffj19FfeR8pIlm01~rBeWCZ{dbvfq;rA3DT000kA zOjJc?%*_A){{R30GnreSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM003J_L_t(I%iWVf3V=Wi12fJ3|IHp$*hSlV@t||fKp?cDK@bHXV&o_g zF_hw;3ILUGteXmeJsVfSmcVJno)^MdQwU3bFHCtNG)uY>mLcD%`0UBaIq~Fq8#dBr V12uok3~c}a002ovPDHLkV1nKBo!S5Z literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_libpng/harness.cc b/fuzzers/libfuzzer_libpng_old/harness.cc similarity index 100% rename from fuzzers/libfuzzer_libpng/harness.cc rename to fuzzers/libfuzzer_libpng_old/harness.cc diff --git a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs new file mode 100644 index 0000000000..163a4c43ed --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs @@ -0,0 +1,179 @@ +//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts +//! The example harness is built for libpng. + +#[cfg(unix)] +use core::time::Duration; +use std::{env, path::PathBuf}; + +#[cfg(unix)] +use libafl::{ + bolts::{shmem::UnixShMem, tuples::tuple_list}, + corpus::{ + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + QueueCorpusScheduler, + }, + events::setup_restarting_mgr, + executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, + mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, HasMetadata, State}, + stats::SimpleStats, + utils::{current_nanos, StdRand}, + Error, +}; + +/// We will interact with a C++ target, so use external c functionality +#[cfg(unix)] +extern "C" { + /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) + fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; + + // afl_libfuzzer_init calls LLVMFUzzerInitialize() + fn afl_libfuzzer_init() -> i32; + + static __lafl_edges_map: *mut u8; + static __lafl_cmp_map: *mut u8; + static __lafl_max_edges_size: u32; +} + +/// The main fn, usually parsing parameters, and starting the fuzzer +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + fuzz( + vec![PathBuf::from("./corpus")], + PathBuf::from("./crashes"), + 1337, + ) + .expect("An error occurred while fuzzing"); +} + +/// Not supported on windows right now +#[cfg(windows)] +fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { + todo!("Example not supported on Windows"); +} + +/// The actual fuzzer +#[cfg(unix)] +fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { + // 'While the stats are state, they are usually used in the broker - which is likely never restarted + let stats = SimpleStats::new(|s| println!("{}", s)); + + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + let (state, mut restarting_mgr) = + match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { + Ok(res) => res, + Err(err) => match err { + Error::ShuttingDown => { + return Ok(()); + } + _ => { + panic!("Failed to setup the restarter: {}", err); + } + }, + }; + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(unsafe { + StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) + }); + + // If not restarting, create a State from scratch + let mut state = state.unwrap_or_else(|| { + State::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Feedbacks to rate the interestingness of an input + tuple_list!( + MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), + TimeFeedback::new() + ), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(objective_dir).unwrap(), + // Feedbacks to recognize an input as solution + tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()), + ) + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().is_none() { + state.add_metadata(Tokens::new(vec![ + vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header + "IHDR".as_bytes().to_vec(), + "IDAT".as_bytes().to_vec(), + "PLTE".as_bytes().to_vec(), + "IEND".as_bytes().to_vec(), + ])); + } + + // Setup a basic mutator with a mutational stage + let mutator = HavocBytesMutator::default(); + let stage = StdMutationalStage::new(mutator); + + // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + + // Create the executor for an in-process function with just one observer for edge coverage + let mut executor = TimeoutExecutor::new( + InProcessExecutor::new( + "in-process(edges)", + &mut harness, + tuple_list!(edges_observer, TimeObserver::new("time")), + &mut state, + &mut restarting_mgr, + )?, + // 10 seconds timeout + Duration::new(10, 0), + ); + + // The actual target run starts here. + // Call LLVMFUzzerInitialize() if present. + unsafe { + if afl_libfuzzer_init() == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") + } + } + + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + state + .load_initial_inputs( + &mut executor, + &mut restarting_mgr, + fuzzer.scheduler(), + &corpus_dirs, + ) + .expect(&format!( + "Failed to load initial corpus at {:?}", + &corpus_dirs + )); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + + // Never reached + Ok(()) +} diff --git a/fuzzers/libfuzzer_libpng/test.sh b/fuzzers/libfuzzer_libpng_old/test.sh similarity index 100% rename from fuzzers/libfuzzer_libpng/test.sh rename to fuzzers/libfuzzer_libpng_old/test.sh diff --git a/libafl_cc/src/bin/libafl-cc.rs b/libafl_cc/src/bin/libafl-cc.rs deleted file mode 100644 index f3e95c9cc1..0000000000 --- a/libafl_cc/src/bin/libafl-cc.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - todo!("libafl-cc"); -} diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index e366d8cae0..85ad5b5cbc 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -75,7 +75,7 @@ impl CompilerWrapper for ClangWrapper { let mut linking = true; // Detect stray -v calls from ./configure scripts. - if args.len() == 1 && args[1] == "-v" { + if args.len() > 1 && args[1] == "-v" { linking = false; } diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml new file mode 100644 index 0000000000..f7c5fb3646 --- /dev/null +++ b/libafl_targets/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "libafl_targets" +version = "0.1.0" +authors = ["Andrea Fioraldi "] +edition = "2018" + +[features] +default = ["sancov", "libfuzzer_compatibility"] +sancov = [] +libfuzzer_compatibility = [] + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } + +[dependencies] diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs new file mode 100644 index 0000000000..5c2a27f6f3 --- /dev/null +++ b/libafl_targets/build.rs @@ -0,0 +1,22 @@ +// build.rs + +use std::env; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = out_dir.to_string_lossy().to_string(); + //let out_dir_path = Path::new(&out_dir); + + #[cfg(feature = "libfuzzer_compatibility")] + { + println!("cargo:rerun-if-changed=libfuzzer_compatibility.c"); + + cc::Build::new() + .file("libfuzzer_compatibility.c") + .compile("libfuzzer-compatibility"); + } + + println!("cargo:rustc-link-search=native={}", &out_dir); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/libafl_targets/libfuzzer_compatibility.c b/libafl_targets/libfuzzer_compatibility.c new file mode 100644 index 0000000000..d5a304348a --- /dev/null +++ b/libafl_targets/libfuzzer_compatibility.c @@ -0,0 +1,25 @@ +static int orig_argc; +static char **orig_argv; +static char **orig_envp; + +static void save_main_args(int argc, char** argv, char** envp) { + orig_argc = argc; + orig_argv = argv; + orig_envp = envp; +} + +__attribute__((section(".init_array"))) +void (*p_libafl_targets_save_main_args)(int, char*[], char*[]) = &save_main_args; + +__attribute__((weak)) +int LLVMFuzzerInitialize(int *argc, char ***argv); + +int libafl_targets_libfuzzer_init() { + + if (LLVMFuzzerInitialize) { + return LLVMFuzzerInitialize(&orig_argc, &orig_argv); + } else { + return 0; + } + +} diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs new file mode 100644 index 0000000000..78dcf55923 --- /dev/null +++ b/libafl_targets/src/lib.rs @@ -0,0 +1,10 @@ +#[cfg(feature = "sancov")] +pub mod sancov; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/libafl_targets/src/sancov.rs b/libafl_targets/src/sancov.rs new file mode 100644 index 0000000000..6b99a15124 --- /dev/null +++ b/libafl_targets/src/sancov.rs @@ -0,0 +1,26 @@ +// TODO compile time flag +pub const MAP_SIZE: usize = 65536; + +pub static mut EDGES_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut MAX_EDGES_NUM: usize = 0; + +#[no_mangle] +pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { + let pos = *guard as usize; + let val = (EDGES_MAP[pos] as u8).wrapping_add(1); + EDGES_MAP[pos] = val; +} + +#[no_mangle] +pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { + if start == stop || *start != 0 { + return; + } + + while start < stop { + MAX_EDGES_NUM += 1; + *start = (MAX_EDGES_NUM & (MAP_SIZE - 1)) as u32; + start = start.offset(1); + } +} From 8d2713c4d9572866b49afe7376770ca5daee0814 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 24 Mar 2021 15:28:58 +0100 Subject: [PATCH 018/104] libfuzzer_libpng wrappers --- fuzzers/libfuzzer_libpng/src/bin/cc.rs | 10 +++++-- fuzzers/libfuzzer_libpng/src/bin/cxx.rs | 20 ++++++++++++++ libafl_cc/src/lib.rs | 36 ++++++++++++++++++------- 3 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 fuzzers/libfuzzer_libpng/src/bin/cxx.rs diff --git a/fuzzers/libfuzzer_libpng/src/bin/cc.rs b/fuzzers/libfuzzer_libpng/src/bin/cc.rs index ad820b5904..8a34130ba4 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cc.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/cc.rs @@ -3,11 +3,17 @@ use std::env; fn main() { let args: Vec = env::args().collect(); - ClangWrapper::new("clang", "clang++") + if args.len() > 1 { + let mut dir = env::current_exe().unwrap(); + dir.pop(); + ClangWrapper::new("clang", "clang++") .from_args(&args) .unwrap() - .add_arg("-fsanitize=trace-pc-guard".into()) + .add_arg("-fsanitize-coverage=trace-pc-guard".into()) + .unwrap() + .add_link_arg(dir.join("liblibfuzzer_libpng.a").display().to_string()) .unwrap() .run() .unwrap(); + } } diff --git a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs new file mode 100644 index 0000000000..aaf901835e --- /dev/null +++ b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs @@ -0,0 +1,20 @@ +use libafl_cc::{ClangWrapper, CompilerWrapper}; +use std::env; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + let mut dir = env::current_exe().unwrap(); + dir.pop(); + ClangWrapper::new("clang", "clang++") + .is_cpp() + .from_args(&args) + .unwrap() + .add_arg("-fsanitize-coverage=trace-pc-guard".into()) + .unwrap() + .add_link_arg(dir.join("liblibfuzzer_libpng.a").display().to_string()) + .unwrap() + .run() + .unwrap(); + } +} diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 85ad5b5cbc..9b27de117b 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -1,8 +1,9 @@ -use std::{string::String, vec::Vec}; +use std::{process::Command, string::String, vec::Vec}; #[derive(Debug)] pub enum Error { InvalidArguments(String), + IOError(std::io::Error), Unknown(String), } @@ -28,9 +29,18 @@ pub trait CompilerWrapper { /// Run the compiler fn run(&mut self) -> Result<(), Error> { - // TODO subproc let args = self.command()?; - println!("{:?}", args); + dbg!(&args); + if args.len() < 1 { + return Err(Error::InvalidArguments( + "The number of arguments cannot be 0".into(), + )); + } + let status = match Command::new(&args[0]).args(&args[1..]).status() { + Ok(s) => s, + Err(e) => return Err(Error::IOError(e)), + }; + dbg!(status); Ok(()) } } @@ -63,12 +73,7 @@ impl CompilerWrapper for ClangWrapper { self.name = args[0].clone(); // Detect C++ compiler looking at the wrapper name - self.is_cpp = self.name.ends_with("++"); - if self.is_cpp { - new_args.push(self.wrapped_cxx.clone()); - } else { - new_args.push(self.wrapped_cc.clone()); - } + self.is_cpp = self.is_cpp || self.name.ends_with("++"); // Sancov flag // new_args.push("-fsanitize-coverage=trace-pc-guard".into()); @@ -122,7 +127,13 @@ impl CompilerWrapper for ClangWrapper { } fn command(&mut self) -> Result, Error> { - let mut args = self.base_args.clone(); + let mut args = vec![]; + if self.is_cpp { + args.push(self.wrapped_cxx.clone()); + } else { + args.push(self.wrapped_cc.clone()); + } + args.extend_from_slice(self.base_args.as_slice()); if self.linking { if self.x_set { args.push("-x".into()); @@ -163,6 +174,11 @@ impl ClangWrapper { self.optimize = false; self } + + pub fn is_cpp<'a>(&'a mut self) -> &'a mut Self { + self.is_cpp = true; + self + } } #[cfg(test)] From 9c0fdee00765b91c5611cc59ebcf1b36fede4332 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 24 Mar 2021 16:15:07 +0100 Subject: [PATCH 019/104] complete libfuzzer_libpng example with compiler wrapper --- fuzzers/libfuzzer_libpng/Cargo.toml | 5 +- fuzzers/libfuzzer_libpng/harness.cc | 197 ++++++++++++++++++ .../src/{fuzzer.rs => lib.rs} | 28 +-- libafl/src/observers/map.rs | 5 +- libafl_targets/src/lib.rs | 7 + libafl_targets/src/libfuzzer_compatibility.rs | 16 ++ 6 files changed, 231 insertions(+), 27 deletions(-) create mode 100644 fuzzers/libfuzzer_libpng/harness.cc rename fuzzers/libfuzzer_libpng/src/{fuzzer.rs => lib.rs} (87%) create mode 100644 libafl_targets/src/libfuzzer_compatibility.rs diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index b4892d868c..a6dab10d39 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -18,13 +18,10 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } +libafl_targets = { path = "../../libafl_targets/" } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } [lib] name = "libfuzzer_libpng" -path = "./src/fuzzer.rs" -test = false -bench = false crate-type = ["staticlib"] - diff --git a/fuzzers/libfuzzer_libpng/harness.cc b/fuzzers/libfuzzer_libpng/harness.cc new file mode 100644 index 0000000000..65faff685d --- /dev/null +++ b/fuzzers/libfuzzer_libpng/harness.cc @@ -0,0 +1,197 @@ +// libpng_read_fuzzer.cc +// Copyright 2017-2018 Glenn Randers-Pehrson +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that may +// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE + +// Last changed in libpng 1.6.35 [July 15, 2018] + +// The modifications in 2017 by Glenn Randers-Pehrson include +// 1. addition of a PNG_CLEANUP macro, +// 2. setting the option to ignore ADLER32 checksums, +// 3. adding "#include " which is needed on some platforms +// to provide memcpy(). +// 4. adding read_end_info() and creating an end_info structure. +// 5. adding calls to png_set_*() transforms commonly used by browsers. + +#include +#include +#include + +#include + +#define PNG_INTERNAL +#include "png.h" + +#define PNG_CLEANUP \ + if(png_handler.png_ptr) \ + { \ + if (png_handler.row_ptr) \ + png_free(png_handler.png_ptr, png_handler.row_ptr); \ + if (png_handler.end_info_ptr) \ + png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ + &png_handler.end_info_ptr); \ + else if (png_handler.info_ptr) \ + png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ + nullptr); \ + else \ + png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ + png_handler.png_ptr = nullptr; \ + png_handler.row_ptr = nullptr; \ + png_handler.info_ptr = nullptr; \ + png_handler.end_info_ptr = nullptr; \ + } + +struct BufState { + const uint8_t* data; + size_t bytes_left; +}; + +struct PngObjectHandler { + png_infop info_ptr = nullptr; + png_structp png_ptr = nullptr; + png_infop end_info_ptr = nullptr; + png_voidp row_ptr = nullptr; + BufState* buf_state = nullptr; + + ~PngObjectHandler() { + if (row_ptr) + png_free(png_ptr, row_ptr); + if (end_info_ptr) + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); + else if (info_ptr) + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + else + png_destroy_read_struct(&png_ptr, nullptr, nullptr); + delete buf_state; + } +}; + +void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { + BufState* buf_state = static_cast(png_get_io_ptr(png_ptr)); + if (length > buf_state->bytes_left) { + png_error(png_ptr, "read error"); + } + memcpy(data, buf_state->data, length); + buf_state->bytes_left -= length; + buf_state->data += length; +} + +static const int kPngHeaderSize = 8; + +// Entry point for LibFuzzer. +// Roughly follows the libpng book example: +// http://www.libpng.org/pub/png/book/chapter13.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < kPngHeaderSize) { + return 0; + } + + std::vector v(data, data + size); + if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { + // not a PNG. + return 0; + } + + PngObjectHandler png_handler; + png_handler.png_ptr = nullptr; + png_handler.row_ptr = nullptr; + png_handler.info_ptr = nullptr; + png_handler.end_info_ptr = nullptr; + + png_handler.png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_handler.png_ptr) { + return 0; + } + + png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); + if (!png_handler.info_ptr) { + PNG_CLEANUP + return 0; + } + + png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); + if (!png_handler.end_info_ptr) { + PNG_CLEANUP + return 0; + } + + png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); +#ifdef PNG_IGNORE_ADLER32 + png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); +#endif + + // Setting up reading from buffer. + png_handler.buf_state = new BufState(); + png_handler.buf_state->data = data + kPngHeaderSize; + png_handler.buf_state->bytes_left = size - kPngHeaderSize; + png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); + png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); + + if (setjmp(png_jmpbuf(png_handler.png_ptr))) { + PNG_CLEANUP + return 0; + } + + // Reading. + png_read_info(png_handler.png_ptr, png_handler.info_ptr); + + // reset error handler to put png_deleter into scope. + if (setjmp(png_jmpbuf(png_handler.png_ptr))) { + PNG_CLEANUP + return 0; + } + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, compression_type; + int filter_type; + + if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, + &height, &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_type)) { + PNG_CLEANUP + return 0; + } + + // This is going to be too slow. + if (width && height > 100000000 / width) { + PNG_CLEANUP +#ifdef HAS_DUMMY_CRASH + #ifdef __aarch64__ + asm volatile (".word 0xf7f0a000\n"); + #else + asm("ud2"); + #endif +#endif + return 0; + } + + // Set several transforms that browsers typically use: + png_set_gray_to_rgb(png_handler.png_ptr); + png_set_expand(png_handler.png_ptr); + png_set_packing(png_handler.png_ptr); + png_set_scale_16(png_handler.png_ptr); + png_set_tRNS_to_alpha(png_handler.png_ptr); + + int passes = png_set_interlace_handling(png_handler.png_ptr); + + png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); + + png_handler.row_ptr = png_malloc( + png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, + png_handler.info_ptr)); + + for (int pass = 0; pass < passes; ++pass) { + for (png_uint_32 y = 0; y < height; ++y) { + png_read_row(png_handler.png_ptr, + static_cast(png_handler.row_ptr), nullptr); + } + } + + png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); + + PNG_CLEANUP + return 0; +} + diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/lib.rs similarity index 87% rename from fuzzers/libfuzzer_libpng/src/fuzzer.rs rename to fuzzers/libfuzzer_libpng/src/lib.rs index 163a4c43ed..8f7c53a243 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -5,7 +5,6 @@ use core::time::Duration; use std::{env, path::PathBuf}; -#[cfg(unix)] use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{ @@ -25,21 +24,10 @@ use libafl::{ Error, }; -/// We will interact with a C++ target, so use external c functionality -#[cfg(unix)] -extern "C" { - /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; +use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM}; - // afl_libfuzzer_init calls LLVMFUzzerInitialize() - fn afl_libfuzzer_init() -> i32; - - static __lafl_edges_map: *mut u8; - static __lafl_cmp_map: *mut u8; - static __lafl_max_edges_size: u32; -} - -/// The main fn, usually parsing parameters, and starting the fuzzer +/// The main fn, no_mangle as it is a C main +#[no_mangle] pub fn main() { // Registry the metadata types used in this fuzzer // Needed only on no_std @@ -85,7 +73,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create an observation channel using the coverage map let edges_observer = HitcountsMapObserver::new(unsafe { - StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) + StdMapObserver::new("edges", &mut EDGES_MAP, MAX_EDGES_NUM) }); // If not restarting, create a State from scratch @@ -131,7 +119,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { - unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + libfuzzer_test_one_input(buf); ExitKind::Ok }; @@ -150,10 +138,8 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. - unsafe { - if afl_libfuzzer_init() == -1 { - println!("Warning: LLVMFuzzerInitialize failed with -1") - } + if libfuzzer_initialize() == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") } // In case the corpus is empty (on first run), reset diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 85d5d2d6be..d1e8f13a80 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -120,10 +120,11 @@ where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { /// Creates a new MapObserver - pub fn new(name: &'static str, map: &'static mut [T]) -> Self { + pub fn new(name: &'static str, map: &'static mut [T], len: usize) -> Self { + assert!(map.len() >= len); let initial = if map.is_empty() { T::default() } else { map[0] }; Self { - map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())), + map: ArrayMut::Cptr((map.as_mut_ptr(), len)), name: name.to_string(), initial, } diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 78dcf55923..6f4232f69c 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -1,5 +1,12 @@ #[cfg(feature = "sancov")] pub mod sancov; +#[cfg(feature = "sancov")] +pub use sancov::*; + +#[cfg(feature = "libfuzzer_compatibility")] +pub mod libfuzzer_compatibility; +#[cfg(feature = "libfuzzer_compatibility")] +pub use libfuzzer_compatibility::*; #[cfg(test)] mod tests { diff --git a/libafl_targets/src/libfuzzer_compatibility.rs b/libafl_targets/src/libfuzzer_compatibility.rs new file mode 100644 index 0000000000..a4a29fd362 --- /dev/null +++ b/libafl_targets/src/libfuzzer_compatibility.rs @@ -0,0 +1,16 @@ +/// We will interact with a C++ target, so use external c functionality +extern "C" { + /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) + fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; + + // libafl_targets_libfuzzer_init calls LLVMFUzzerInitialize() + fn libafl_targets_libfuzzer_init() -> i32; +} + +pub fn libfuzzer_initialize() -> i32 { + unsafe { libafl_targets_libfuzzer_init() } +} + +pub fn libfuzzer_test_one_input(buf: &[u8]) -> i32 { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) } +} From 79dbdf82510064d5555a499f5761dab62b7cd2df Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 24 Mar 2021 16:23:08 +0100 Subject: [PATCH 020/104] readme --- fuzzers/libfuzzer_libpng/README.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index f56138c2b5..924cf52339 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -6,10 +6,29 @@ It has been tested on Linux. ## Build -To build this example, run `cargo build --example libfuzzer_libpng --release`. -This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. -Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. -Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. +To build this example, run `cargo build --release`. +This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. +In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target. + +Then download libpng from https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz and unpack the archive. + +Now compile it with: + +``` +cd libpng-1.6.37 +./configure +make CC=/path/to/libfuzzer_libpng/target/release/cc -j `nproc` +``` + +You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`. + +Now, we have to build the libfuzzer harness and link all togheter to create our fuzzer binary. + +``` +/path/to/libfuzzer_libpng/target/debug/cxx /path/to/libfuzzer_libpng/harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer -lz -lm +``` + +Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`. ## Run From 21b790060d109c209feaf575fdd634b63e520cb2 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 24 Mar 2021 16:24:37 +0100 Subject: [PATCH 021/104] readme --- fuzzers/libfuzzer_libpng/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index 924cf52339..acb7426f8a 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -41,4 +41,3 @@ By restarting the actual fuzzer, it can recover from these exit conditions. In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). -For convenience, you may just run `./test.sh` in this folder to test it. From 82f5dad7845367a1defadecac917f7b72d55448d Mon Sep 17 00:00:00 2001 From: Toka Date: Thu, 25 Mar 2021 21:04:18 +0900 Subject: [PATCH 022/104] Add mutator stats method (#40) * add LogMutation trait * change &self to &mut self * move self.scheduler out of StdFuzzer * reorder generics?, implement post_exec * append metadata to the corresponding testcase in the corpus * turn mutations into Mutators * impl Named for mutations * add LoggerScheduledMutator, add fn get_name() to MutatorTuple * Fix BytesDeleteMutator, and format * remove TupleList bound on Tail * turn TokenInsert, TokenReplace into Mutator, fill havoc_mutations * libfuzzer_libpng * libfuzzer_libpng_cmpalloc * libfuzzer_libmozjpeg * fix tests * fix libfuzzer_libmozjpeg * fix tests * fix LoggerScheduledMutator::mutate * use vec instead of String * fix post_exec and get_name * fmt * NamedTuple and HasNameIdTuple * always clear mutations log * fix tests * format * remove libafl_targets default features * use vec instead of vec> * add alloc::string::String * format Co-authored-by: Andrea Fioraldi --- fuzzers/frida_libpng/src/fuzzer.rs | 18 +- fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs | 18 +- fuzzers/libfuzzer_libpng/Cargo.toml | 2 +- fuzzers/libfuzzer_libpng/src/lib.rs | 18 +- .../libfuzzer_libpng_cmpalloc/src/fuzzer.rs | 41 +- fuzzers/libfuzzer_libpng_cmpalloc/test.sh | 6 +- fuzzers/libfuzzer_libpng_old/src/fuzzer.rs | 15 +- libafl/Cargo.toml | 2 +- libafl/src/bolts/tuples.rs | 80 +- libafl/src/events/mod.rs | 2 +- libafl/src/feedbacks/mod.rs | 4 +- libafl/src/fuzzer.rs | 51 +- libafl/src/lib.rs | 10 +- libafl/src/mutators/mod.rs | 164 +- libafl/src/mutators/mutations.rs | 2102 ++++++++++++----- libafl/src/mutators/scheduled.rs | 406 ++-- libafl/src/mutators/token_mutations.rs | 198 +- libafl/src/observers/mod.rs | 6 +- libafl/src/stages/mod.rs | 15 +- libafl/src/stages/mutational.rs | 11 +- libafl/src/state/mod.rs | 15 +- libafl_targets/Cargo.toml | 2 +- 22 files changed, 2293 insertions(+), 893 deletions(-) diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index 51950b31ef..885a62279f 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -13,9 +13,10 @@ use libafl::{ events::{setup_restarting_mgr, EventManager}, executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, feedbacks::{CrashFeedback, MaxMapFeedback}, - fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, + fuzzer::{Fuzzer, StdFuzzer}, inputs::{HasTargetBytes, Input}, - mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver}, stages::mutational::StdMutationalStage, state::{HasCorpus, HasMetadata, State}, @@ -470,12 +471,12 @@ unsafe fn fuzz( } // Setup a basic mutator with a mutational stage - let mutator = HavocBytesMutator::default(); + let mutator = StdScheduledMutator::new(havoc_mutations()); let stage = StdMutationalStage::new(mutator); // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); // Create the executor for an in-process function with just one observer for edge coverage let mut executor = FridaInProcessExecutor::new( @@ -502,12 +503,7 @@ unsafe fn fuzz( // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs( - &mut executor, - &mut restarting_mgr, - fuzzer.scheduler(), - &corpus_dirs, - ) + .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -515,7 +511,7 @@ unsafe fn fuzz( println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; // Never reached Ok(()) diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs index 4634c7fd31..0d31d983e2 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs @@ -10,8 +10,8 @@ use libafl::{ events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, - fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - mutators::scheduled::HavocBytesMutator, + fuzzer::{Fuzzer, StdFuzzer}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::StdMapObserver, stages::mutational::StdMutationalStage, @@ -100,11 +100,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> } // Setup a basic mutator with a mutational stage - let mutator = HavocBytesMutator::default(); + let mutator = StdScheduledMutator::new(havoc_mutations()); let stage = StdMutationalStage::new(mutator); + let scheduler = RandCorpusScheduler::new(); // A fuzzer with just one stage and a random policy to get testcasess from the corpus - let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage)); + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { @@ -132,12 +133,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs( - &mut executor, - &mut restarting_mgr, - fuzzer.scheduler(), - &corpus_dirs, - ) + .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -145,7 +141,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; // Never reached Ok(()) diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index a6dab10d39..ba4698b223 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -18,7 +18,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/" } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov", "libfuzzer_compatibility"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 8f7c53a243..56dfdead1a 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -14,8 +14,9 @@ use libafl::{ events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, - fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + fuzzer::{Fuzzer, StdFuzzer}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, state::{HasCorpus, HasMetadata, State}, @@ -110,12 +111,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> } // Setup a basic mutator with a mutational stage - let mutator = HavocBytesMutator::default(); + let mutator = StdScheduledMutator::new(havoc_mutations()); let stage = StdMutationalStage::new(mutator); // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { @@ -145,12 +146,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs( - &mut executor, - &mut restarting_mgr, - fuzzer.scheduler(), - &corpus_dirs, - ) + .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -158,7 +154,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; // Never reached Ok(()) diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs index c78a6d88d4..5d53ac67a9 100644 --- a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs @@ -6,16 +6,14 @@ use std::{env, path::PathBuf}; #[cfg(unix)] use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, - corpus::{ - Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, - QueueCorpusScheduler, - }, + corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, ExitKind}, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, - fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, - observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, + feedbacks::{CrashFeedback, MaxMapFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::token_mutations::Tokens, + observers::{HitcountsMapObserver, StdMapObserver}, stages::mutational::StdMutationalStage, state::{HasCorpus, HasMetadata, State}, stats::SimpleStats, @@ -109,7 +107,6 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), MaxMapFeedback::new_with_observer(&cmps_observer), MaxMapFeedback::new_with_observer(&allocs_observer), - TimeFeedback::new() ), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer @@ -133,12 +130,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> } // Setup a basic mutator with a mutational stage - let mutator = HavocBytesMutator::default(); + let mutator = StdScheduledMutator::new(havoc_mutations()); let stage = StdMutationalStage::new(mutator); - // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + let scheduler = RandCorpusScheduler::new(); + // A fuzzer with just one stage and a random policy to get testcasess from the corpus + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { @@ -148,14 +145,9 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( - "in-process(edges,cmps,allocs)", + "in-process(edges)", &mut harness, - tuple_list!( - edges_observer, - cmps_observer, - allocs_observer, - TimeObserver::new("time") - ), + tuple_list!(edges_observer, cmps_observer, allocs_observer), &mut state, &mut restarting_mgr, )?; @@ -171,12 +163,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs( - &mut executor, - &mut restarting_mgr, - fuzzer.scheduler(), - &corpus_dirs, - ) + .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -184,7 +171,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; // Never reached Ok(()) diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/test.sh b/fuzzers/libfuzzer_libpng_cmpalloc/test.sh index f707f77271..47e79afb3f 100755 --- a/fuzzers/libfuzzer_libpng_cmpalloc/test.sh +++ b/fuzzers/libfuzzer_libpng_cmpalloc/test.sh @@ -2,16 +2,16 @@ mkdir -p ./crashes -cargo build --example libfuzzer_libpng --release || exit 1 +cargo build --example libfuzzer_libpng_cmpalloc --release || exit 1 cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf # The broker -RUST_BACKTRACE=full taskset 0 ./.libfuzzer_test.elf & +RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf & # Give the broker time to spawn sleep 2 echo "Spawning client" # The 1st fuzzer client, pin to cpu 0x1 -RUST_BACKTRACE=full taskset 1 ./.libfuzzer_test.elf 2>/dev/null +RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf 2>/dev/null killall .libfuzzer_test.elf rm -rf ./.libfuzzer_test.elf diff --git a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs index 163a4c43ed..8653a61586 100644 --- a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs @@ -15,7 +15,7 @@ use libafl::{ events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, - fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, + fuzzer::{Fuzzer, StdFuzzer}, mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, @@ -122,12 +122,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> } // Setup a basic mutator with a mutational stage - let mutator = HavocBytesMutator::default(); + let mutator = StdScheduledMutator::new(havoc_mutations()); let stage = StdMutationalStage::new(mutator); // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { @@ -159,12 +159,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs( - &mut executor, - &mut restarting_mgr, - fuzzer.scheduler(), - &corpus_dirs, - ) + .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -172,7 +167,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; // Never reached Ok(()) diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index fbaefb34af..2c1506c38e 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -46,7 +46,7 @@ required-features = ["std"] tuple_list = "0.1.2" hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible num = "*" -xxhash-rust = { version = "0.8.0", features = ["xxh3"] } # xxh3 hashing for rust +xxhash-rust = { version = "0.8.0", features = ["xxh3", "const_xxh3"] } # xxh3 hashing for rust serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib erased-serde = "0.3.12" postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat diff --git a/libafl/src/bolts/tuples.rs b/libafl/src/bolts/tuples.rs index c44f74102d..4b5bf0513c 100644 --- a/libafl/src/bolts/tuples.rs +++ b/libafl/src/bolts/tuples.rs @@ -4,6 +4,8 @@ pub use tuple_list::{tuple_list, tuple_list_type, TupleList}; use core::any::TypeId; +use xxhash_rust::const_xxh3::xxh3_64; + pub trait HasLen { fn len(&self) -> usize; fn is_empty(&self) -> bool { @@ -19,13 +21,59 @@ impl HasLen for () { impl HasLen for (Head, Tail) where - Tail: TupleList + HasLen, + Tail: HasLen, { fn len(&self) -> usize { 1 + self.1.len() } } +pub trait HasNameId { + fn const_name(&self) -> &'static str; + + fn name_id(&self) -> u64 { + xxh3_64(self.const_name().as_bytes()) + } +} + +pub trait HasNameIdTuple: HasLen { + fn get_const_name(&self, index: usize) -> Option<&'static str>; + + fn get_name_id(&self, index: usize) -> Option; +} + +impl HasNameIdTuple for () { + fn get_const_name(&self, _index: usize) -> Option<&'static str> { + None + } + + fn get_name_id(&self, _index: usize) -> Option { + None + } +} + +impl HasNameIdTuple for (Head, Tail) +where + Head: 'static + HasNameId, + Tail: HasNameIdTuple, +{ + fn get_const_name(&self, index: usize) -> Option<&'static str> { + if index == 0 { + Some(self.0.const_name()) + } else { + self.1.get_const_name(index - 1) + } + } + + fn get_name_id(&self, index: usize) -> Option { + if index == 0 { + Some(self.0.name_id()) + } else { + self.1.get_name_id(index - 1) + } + } +} + pub trait MatchFirstType { fn match_first_type(&self) -> Option<&T>; fn match_first_type_mut(&mut self) -> Option<&mut T>; @@ -43,7 +91,7 @@ impl MatchFirstType for () { impl MatchFirstType for (Head, Tail) where Head: 'static, - Tail: TupleList + MatchFirstType, + Tail: MatchFirstType, { fn match_first_type(&self) -> Option<&T> { if TypeId::of::() == TypeId::of::() { @@ -75,7 +123,7 @@ impl MatchType for () { impl MatchType for (Head, Tail) where Head: 'static, - Tail: TupleList + MatchType, + Tail: MatchType, { fn match_type(&self, f: fn(t: &T)) { if TypeId::of::() == TypeId::of::() { @@ -98,6 +146,30 @@ pub trait Named { fn name(&self) -> &str; } +pub trait NamedTuple: HasLen { + fn get_name(&self, index: usize) -> Option<&str>; +} + +impl NamedTuple for () { + fn get_name(&self, _index: usize) -> Option<&str> { + None + } +} + +impl NamedTuple for (Head, Tail) +where + Head: 'static + Named, + Tail: NamedTuple, +{ + fn get_name(&self, index: usize) -> Option<&str> { + if index == 0 { + Some(self.0.name()) + } else { + self.1.get_name(index - 1) + } + } +} + pub trait MatchNameAndType { fn match_name_type(&self, name: &str) -> Option<&T>; fn match_name_type_mut(&mut self, name: &str) -> Option<&mut T>; @@ -115,7 +187,7 @@ impl MatchNameAndType for () { impl MatchNameAndType for (Head, Tail) where Head: 'static + Named, - Tail: TupleList + MatchNameAndType, + Tail: MatchNameAndType, { fn match_name_type(&self, name: &str) -> Option<&T> { if TypeId::of::() == TypeId::of::() && name == self.0.name() { diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index 01cb94038d..b2c089a699 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -246,7 +246,7 @@ mod tests { #[test] fn test_event_serde() { - let obv = StdMapObserver::new("test", unsafe { &mut MAP }); + let obv = StdMapObserver::new("test", unsafe { &mut MAP }, unsafe { MAP.len() }); let map = tuple_list!(obv); let observers_buf = postcard::to_allocvec(&map).unwrap(); diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 5a156539eb..53e2cf4b8e 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -7,7 +7,7 @@ pub use map::*; use serde::{Deserialize, Serialize}; use crate::{ - bolts::tuples::{Named, TupleList}, + bolts::tuples::Named, corpus::Testcase, executors::ExitKind, inputs::Input, @@ -92,7 +92,7 @@ where impl FeedbacksTuple for (Head, Tail) where Head: Feedback, - Tail: FeedbacksTuple + TupleList, + Tail: FeedbacksTuple, I: Input, { fn is_interesting_all( diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 2bddff0b5a..062ae077cc 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -42,17 +42,29 @@ where } /// The main fuzzer trait. -pub trait Fuzzer { +pub trait Fuzzer { /// Fuzz for a single iteration /// Returns the index of the last fuzzed corpus item - fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result; + fn fuzz_one( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + scheduler: &CS, + ) -> Result; /// Fuzz forever (or until stopped) - fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<(), Error> { + fn fuzz_loop( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + scheduler: &CS, + ) -> Result { let mut last = current_time(); let stats_timeout = STATS_TIMEOUT_DEFAULT; loop { - self.fuzz_one(state, executor, manager)?; + self.fuzz_one(state, executor, manager, scheduler)?; last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; } } @@ -60,10 +72,11 @@ pub trait Fuzzer { /// Fuzz for n iterations /// Returns the index of the last fuzzed corpus item fn fuzz_loop_for( - &self, + &mut self, state: &mut S, executor: &mut E, manager: &mut EM, + scheduler: &CS, iters: u64, ) -> Result { if iters == 0 { @@ -77,7 +90,7 @@ pub trait Fuzzer { let stats_timeout = STATS_TIMEOUT_DEFAULT; for _ in 0..iters { - ret = self.fuzz_one(state, executor, manager)?; + ret = self.fuzz_one(state, executor, manager, scheduler)?; last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; } Ok(ret) @@ -104,9 +117,8 @@ where EM: EventManager, I: Input, { - scheduler: CS, stages: ST, - phantom: PhantomData<(E, EM, I, OT, S)>, + phantom: PhantomData<(CS, E, EM, I, OT, S)>, } impl HasStages for StdFuzzer @@ -126,6 +138,7 @@ where } } +/* impl HasCorpusScheduler for StdFuzzer where CS: CorpusScheduler, @@ -142,8 +155,9 @@ where &mut self.scheduler } } +*/ -impl Fuzzer for StdFuzzer +impl Fuzzer for StdFuzzer where CS: CorpusScheduler, S: HasExecutions, @@ -178,13 +192,19 @@ where } } - fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result { - let idx = self.scheduler().next(state)?; + fn fuzz_one( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + scheduler: &CS, + ) -> Result { + let idx = scheduler.next(state)?; - self.stages() - .perform_all(state, executor, manager, self.scheduler(), idx)?; + self.stages_mut() + .perform_all(state, executor, manager, scheduler, idx)?; - manager.process(state, executor, self.scheduler())?; + manager.process(state, executor, scheduler)?; Ok(idx) } } @@ -197,9 +217,8 @@ where EM: EventManager, I: Input, { - pub fn new(scheduler: CS, stages: ST) -> Self { + pub fn new(stages: ST) -> Self { Self { - scheduler, stages, phantom: PhantomData, } diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index c58307a045..0cc2676e27 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -139,7 +139,7 @@ mod tests { corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase}, executors::{ExitKind, InProcessExecutor}, inputs::BytesInput, - mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator}, + mutators::{mutations::BitFlipMutator, StdScheduledMutator}, stages::StdMutationalStage, state::{HasCorpus, State}, stats::SimpleStats, @@ -182,14 +182,14 @@ mod tests { ) .unwrap(); - let mut mutator = StdScheduledMutator::new(); - mutator.add_mutation(mutation_bitflip); + let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new())); let stage = StdMutationalStage::new(mutator); - let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage)); + let scheduler = RandCorpusScheduler::new(); + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); for i in 0..1000 { fuzzer - .fuzz_one(&mut state, &mut executor, &mut event_manager) + .fuzz_one(&mut state, &mut executor, &mut event_manager, &scheduler) .expect(&format!("Error in iter {}", i)); } diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index e0eaf420ea..878beaa3dc 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -7,11 +7,24 @@ pub use mutations::*; pub mod token_mutations; pub use token_mutations::*; -use crate::{inputs::Input, Error}; +use crate::{ + bolts::tuples::{HasLen, Named}, + inputs::Input, + Error, +}; // TODO mutator stats method that produces something that can be sent with the NewTestcase event // We can use it to report which mutations generated the testcase in the broker logs +/// The result of a mutation. +/// If the mutation got skipped, the target +/// will not be executed with the returned input. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum MutationResult { + Mutated, + Skipped, +} + /// A mutator takes input, and mutates it. /// Simple as that. pub trait Mutator @@ -19,15 +32,158 @@ where I: Input, { /// Mutate a given input - fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error>; + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result; /// Post-process given the outcome of the execution fn post_exec( - &self, + &mut self, _state: &mut S, - _is_interesting: u32, _stage_idx: i32, + _corpus_idx: Option, ) -> Result<(), Error> { Ok(()) } } + +pub trait MutatorsTuple: HasLen +where + I: Input, +{ + fn mutate_all( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result; + + fn post_exec_all( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; + + fn get_and_mutate( + &mut self, + index: usize, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result; + + fn get_and_post_exec( + &mut self, + index: usize, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; +} + +impl MutatorsTuple for () +where + I: Input, +{ + fn mutate_all( + &mut self, + _state: &mut S, + _input: &mut I, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn post_exec_all( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } + + fn get_and_mutate( + &mut self, + _index: usize, + _state: &mut S, + _input: &mut I, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn get_and_post_exec( + &mut self, + _index: usize, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } +} + +impl MutatorsTuple for (Head, Tail) +where + Head: Mutator + Named, + Tail: MutatorsTuple, + I: Input, +{ + fn mutate_all( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + let r = self.0.mutate(state, input, stage_idx)?; + if self.1.mutate_all(state, input, stage_idx)? == MutationResult::Mutated { + Ok(MutationResult::Mutated) + } else { + Ok(r) + } + } + + fn post_exec_all( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + self.0.post_exec(state, stage_idx, corpus_idx)?; + self.1.post_exec_all(state, stage_idx, corpus_idx) + } + + fn get_and_mutate( + &mut self, + index: usize, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + if index == 0 { + self.0.mutate(state, input, stage_idx) + } else { + self.1.get_and_mutate(index - 1, state, input, stage_idx) + } + } + + fn get_and_post_exec( + &mut self, + index: usize, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + if index == 0 { + self.0.post_exec(state, stage_idx, corpus_idx) + } else { + self.1 + .get_and_post_exec(index - 1, state, stage_idx, corpus_idx) + } + } +} diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 041c4b057a..d1743572b1 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1,42 +1,20 @@ //! A wide variety of mutations used during fuzzing. use crate::{ + bolts::tuples::Named, corpus::Corpus, inputs::{HasBytesVec, Input}, + mutators::{MutationResult, Mutator}, state::{HasCorpus, HasMaxSize, HasRand}, utils::Rand, Error, }; use alloc::{borrow::ToOwned, vec::Vec}; -use core::cmp::{max, min}; - -/// The result of a mutation. -/// If the mutation got skipped, the target -/// will not be executed with the returned input. -#[derive(Clone, Copy, Debug)] -pub enum MutationResult { - Mutated, - Skipped, -} - -// TODO maybe the mutator arg is not needed -/// The generic function type that identifies mutations -pub type MutationFunction = fn(&mut S, &mut I) -> Result; - -pub trait ComposedByMutations -where - I: Input, -{ - /// Get a mutation by index - fn mutation_by_idx(&self, index: usize) -> MutationFunction; - - /// Get the number of mutations - fn mutations_count(&self) -> usize; - - /// Add a mutation - fn add_mutation(&mut self, mutation: MutationFunction); -} +use core::{ + cmp::{max, min}, + marker::PhantomData, +}; /// Mem move in the own vec #[inline] @@ -112,578 +90,1530 @@ const INTERESTING_32: [i32; 27] = [ ]; /// Bitflip mutation for inputs with a bytes vector -pub fn mutation_bitflip(state: &mut S, input: &mut I) -> Result +#[derive(Default)] +pub struct BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BitFlipMutator" + } +} + +impl BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byteflip mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) ^= 0xff; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteFlipMutator" + } +} + +impl ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte increment mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx); + *ptr = (*ptr).wrapping_add(1); + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteIncMutator" + } +} + +impl ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte decrement mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx); + *ptr = (*ptr).wrapping_sub(1); + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteDecMutator" + } +} + +impl ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte negate mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx)); + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteNegMutator" + } +} + +impl ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte random mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) = state.rand_mut().below(256) as u8; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteRandMutator" + } +} + +impl ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut u8; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u8; + match state.rand_mut().below(2) { + 0 => *ptr = (*ptr).wrapping_add(num), + _ => *ptr = (*ptr).wrapping_sub(num), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteAddMutator" + } +} + +impl ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Word add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 2 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u16; + match state.rand_mut().below(4) { + 0 => *ptr = (*ptr).wrapping_add(num), + 1 => *ptr = (*ptr).wrapping_sub(num), + 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), + _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "WordAddMutator" + } +} + +impl WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Dword add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 4 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u32; + match state.rand_mut().below(4) { + 0 => *ptr = (*ptr).wrapping_add(num), + 1 => *ptr = (*ptr).wrapping_sub(num), + 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), + _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "DwordAddMutator" + } +} + +impl DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Qword add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 8 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 7) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u64; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u64; + match state.rand_mut().below(4) { + 0 => *ptr = (*ptr).wrapping_add(num), + 1 => *ptr = (*ptr).wrapping_sub(num), + 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), + _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "QwordAddMutator" + } +} + +impl QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte interesting mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + let val = + INTERESTING_8[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u8; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) = val; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteInterestingMutator" + } +} + +impl ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Word interesting mutation for inputs with a bytes vector +#[derive(Default)] +pub struct WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 2 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; + let val = + INTERESTING_16[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u16; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; + if state.rand_mut().below(2) == 0 { + *ptr = val; + } else { + *ptr = val.swap_bytes(); + } + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "WordInterestingMutator" + } +} + +impl WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Dword interesting mutation for inputs with a bytes vector +#[derive(Default)] +pub struct DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 4 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; + let val = + INTERESTING_32[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u32; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; + if state.rand_mut().below(2) == 0 { + *ptr = val; + } else { + *ptr = val.swap_bytes(); + } + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "DwordInterestingMutator" + } +} + +impl DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes delete mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size <= 2 { + return Ok(MutationResult::Skipped); + } + + let off = state.rand_mut().below(size as u64) as usize; + let len = state.rand_mut().below((size - off) as u64) as usize; + input.bytes_mut().drain(off..off + len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesDeleteMutator" + } +} + +impl BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes expand mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesExpandMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8; - } - Ok(MutationResult::Mutated) - } + phantom: PhantomData<(I, R, S)>, } -pub fn mutation_byteflip(state: &mut S, input: &mut I) -> Result +impl Mutator for BytesExpandMutator where I: Input + HasBytesVec, - S: HasRand, + S: HasRand + HasMaxSize, R: Rand, { - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) ^= 0xff; - } - Ok(MutationResult::Mutated) - } -} + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; -pub fn mutation_byteinc(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx); - *ptr = (*ptr).wrapping_add(1); - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_bytedec(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx); - *ptr = (*ptr).wrapping_sub(1); - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byteneg(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx)); - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byterand(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) = state.rand_mut().below(256) as u8; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byteadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut u8; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u8; - match state.rand_mut().below(2) { - 0 => *ptr = (*ptr).wrapping_add(num), - _ => *ptr = (*ptr).wrapping_sub(num), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_wordadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 2 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u16; - match state.rand_mut().below(4) { - 0 => *ptr = (*ptr).wrapping_add(num), - 1 => *ptr = (*ptr).wrapping_sub(num), - 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), - _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_dwordadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 4 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u32; - match state.rand_mut().below(4) { - 0 => *ptr = (*ptr).wrapping_add(num), - 1 => *ptr = (*ptr).wrapping_sub(num), - 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), - _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_qwordadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 8 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 7) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u64; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u64; - match state.rand_mut().below(4) { - 0 => *ptr = (*ptr).wrapping_add(num), - 1 => *ptr = (*ptr).wrapping_sub(num), - 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), - _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byteinteresting( - state: &mut S, - input: &mut I, -) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - let val = INTERESTING_8[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u8; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) = val; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_wordinteresting( - state: &mut S, - input: &mut I, -) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 2 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; - let val = - INTERESTING_16[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u16; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; - if state.rand_mut().below(2) == 0 { - *ptr = val; + if size + len > max_size { + if max_size > size { + len = max_size - size; } else { - *ptr = val.swap_bytes(); + return Ok(MutationResult::Skipped); } } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + Ok(MutationResult::Mutated) } } -pub fn mutation_dwordinteresting( - state: &mut S, - input: &mut I, -) -> Result +impl Named for BytesExpandMutator where I: Input + HasBytesVec, - S: HasRand, + S: HasRand + HasMaxSize, R: Rand, { - if input.bytes().len() < 4 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; - let val = - INTERESTING_32[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u32; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; - if state.rand_mut().below(2) == 0 { - *ptr = val; + fn name(&self) -> &str { + "BytesExpandMutator" + } +} + +impl BytesExpandMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes insert mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; } else { - *ptr = val.swap_bytes(); + return Ok(MutationResult::Skipped); } } + + let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + Ok(MutationResult::Mutated) } } -pub fn mutation_bytesdelete(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - let size = input.bytes().len(); - if size <= 2 { - return Ok(MutationResult::Skipped); - } - - let off = state.rand_mut().below(size as u64) as usize; - let len = state.rand_mut().below((size - off) as u64) as usize; - input.bytes_mut().drain(off..off + len); - - Ok(MutationResult::Mutated) -} - -pub fn mutation_bytesexpand(state: &mut S, input: &mut I) -> Result +impl Named for BytesInsertMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } + fn name(&self) -> &str { + "BytesInsertMutator" } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesinsert(state: &mut S, input: &mut I) -> Result +impl BytesInsertMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); + pub fn new() -> Self { + Self { + phantom: PhantomData, } } - - let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesrandinsert( - state: &mut S, - input: &mut I, -) -> Result +/// Bytes random insert mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesRandInsertMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; + phantom: PhantomData<(I, R, S)>, +} - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); +impl Mutator for BytesRandInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let val = state.rand_mut().below(256) as u8; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + fn name(&self) -> &str { + "BytesRandInsertMutator" + } +} + +impl BytesRandInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, } } - - let val = state.rand_mut().below(256) as u8; - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesset(state: &mut S, input: &mut I) -> Result +/// Bytes set mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below(size as u64) as usize; - let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; - - let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; - - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) + phantom: PhantomData<(I, R, S)>, } -pub fn mutation_bytesrandset(state: &mut S, input: &mut I) -> Result +impl Mutator for BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) } - let off = state.rand_mut().below(size as u64) as usize; - let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; - - let val = state.rand_mut().below(256) as u8; - - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytescopy(state: &mut S, input: &mut I) -> Result +impl Named for BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size <= 1 { - return Ok(MutationResult::Skipped); + fn name(&self) -> &str { + "BytesSetMutator" } - - let from = state.rand_mut().below(input.bytes().len() as u64) as usize; - let to = state.rand_mut().below(input.bytes().len() as u64) as usize; - let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize; - - buffer_self_copy(input.bytes_mut(), from, to, len); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesswap(state: &mut S, input: &mut I) -> Result +impl BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size <= 1 { - return Ok(MutationResult::Skipped); + pub fn new() -> Self { + Self { + phantom: PhantomData, + } } - - let first = state.rand_mut().below(input.bytes().len() as u64) as usize; - let second = state.rand_mut().below(input.bytes().len() as u64) as usize; - let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize; - - let tmp = input.bytes()[first..(first + len)].to_vec(); - buffer_self_copy(input.bytes_mut(), second, first, len); - buffer_copy(input.bytes_mut(), &tmp, 0, second, len); - - Ok(MutationResult::Mutated) } -/// Crossover insert mutation -pub fn mutation_crossover_insert( - state: &mut S, - input: &mut I, -) -> Result +/// Bytes random set mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = state.rand_mut().below(256) as u8; + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesRandSetMutator" + } +} + +impl BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes copy mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let from = state.rand_mut().below(input.bytes().len() as u64) as usize; + let to = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize; + + buffer_self_copy(input.bytes_mut(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesCopyMutator" + } +} + +impl BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes swap mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let first = state.rand_mut().below(input.bytes().len() as u64) as usize; + let second = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize; + + let tmp = input.bytes()[first..(first + len)].to_vec(); + buffer_self_copy(input.bytes_mut(), second, first, len); + buffer_copy(input.bytes_mut(), &tmp, 0, second, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesSwapMutator" + } +} + +impl BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Crossover insert mutation for inputs with a bytes vector +#[derive(Default)] +pub struct CrossoverInsertMutator where C: Corpus, I: Input + HasBytesVec, R: Rand, S: HasRand + HasCorpus + HasMaxSize, { - let size = input.bytes().len(); - - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { - return Ok(MutationResult::Skipped); - } - } - - let other_size = state - .corpus() - .get(idx)? - .borrow_mut() - .load_input()? - .bytes() - .len(); - if other_size < 2 { - return Ok(MutationResult::Skipped); - } - - let max_size = state.max_size(); - let from = state.rand_mut().below(other_size as u64) as usize; - let to = state.rand_mut().below(size as u64) as usize; - let mut len = state.rand_mut().below((other_size - from) as u64) as usize; - - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), to, to + len, size - to); - buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); - - Ok(MutationResult::Mutated) + phantom: PhantomData<(C, I, R, S)>, } -/// Crossover replace mutation -pub fn mutation_crossover_replace( - state: &mut S, - input: &mut I, -) -> Result +impl Mutator for CrossoverInsertMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus + HasMaxSize, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let max_size = state.max_size(); + let from = state.rand_mut().below(other_size as u64) as usize; + let to = state.rand_mut().below(size as u64) as usize; + let mut len = state.rand_mut().below((other_size - from) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), to, to + len, size - to); + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverInsertMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus + HasMaxSize, +{ + fn name(&self) -> &str { + "CrossoverInsertMutator" + } +} + +impl CrossoverInsertMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus + HasMaxSize, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Crossover replace mutation for inputs with a bytes vector +#[derive(Default)] +pub struct CrossoverReplaceMutator where C: Corpus, I: Input + HasBytesVec, R: Rand, S: HasRand + HasCorpus, { - let size = input.bytes().len(); + phantom: PhantomData<(C, I, R, S)>, +} - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { +impl Mutator for CrossoverReplaceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { return Ok(MutationResult::Skipped); } + + let from = state.rand_mut().below(other_size as u64) as usize; + let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; + let to = state.rand_mut().below((size - len) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) } +} - let other_size = state - .corpus() - .get(idx)? - .borrow_mut() - .load_input()? - .bytes() - .len(); - if other_size < 2 { - return Ok(MutationResult::Skipped); +impl Named for CrossoverReplaceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn name(&self) -> &str { + "CrossoverReplaceMutator" } +} - let from = state.rand_mut().below(other_size as u64) as usize; - let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; - let to = state.rand_mut().below((size - len) as u64) as usize; - - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); - - Ok(MutationResult::Mutated) +impl CrossoverReplaceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } } /// Returns the first and last diff position between the given vectors, stopping at the min len @@ -702,52 +1632,96 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) { (first_diff, last_diff) } -/// Splicing mutation from AFL -pub fn mutation_splice(state: &mut S, input: &mut I) -> Result +/// Splice mutation for inputs with a bytes vector +#[derive(Default)] +pub struct SpliceMutator where C: Corpus, I: Input + HasBytesVec, R: Rand, S: HasRand + HasCorpus, { - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { - return Ok(MutationResult::Skipped); - } - } + phantom: PhantomData<(C, I, R, S)>, +} - let (first_diff, last_diff) = { - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - let mut counter = 0; - loop { - let (f, l) = locate_diffs(input.bytes(), other.bytes()); - - if f != l && f >= 0 && l >= 2 { - break (f, l); - } - if counter == 3 { +impl Mutator for SpliceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { return Ok(MutationResult::Skipped); } - counter += 1; } - }; - let split_at = state - .rand_mut() - .between(first_diff as u64, last_diff as u64) as usize; + let (first_diff, last_diff) = { + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - input - .bytes_mut() - .splice(split_at.., other.bytes()[split_at..].iter().cloned()); + let mut counter = 0; + loop { + let (f, l) = locate_diffs(input.bytes(), other.bytes()); - Ok(MutationResult::Mutated) + if f != l && f >= 0 && l >= 2 { + break (f, l); + } + if counter == 3 { + return Ok(MutationResult::Skipped); + } + counter += 1; + } + }; + + let split_at = state + .rand_mut() + .between(first_diff as u64, last_diff as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + input + .bytes_mut() + .splice(split_at.., other.bytes()[split_at..].iter().cloned()); + + Ok(MutationResult::Mutated) + } +} + +impl Named for SpliceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn name(&self) -> &str { + "SpliceMutator" + } +} + +impl SpliceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } } // Converts a hex u8 to its u8 value: 'A' -> 10 etc. @@ -801,13 +1775,50 @@ mod tests { use super::*; use crate::{ + bolts::tuples::tuple_list, + bolts::tuples::HasLen, corpus::{Corpus, InMemoryCorpus}, inputs::BytesInput, - mutators::{mutation_tokeninsert, mutation_tokenreplace}, - state::State, + mutators::MutatorsTuple, + state::{HasMetadata, State}, utils::StdRand, }; + fn test_mutations() -> impl MutatorsTuple + where + I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata + HasMaxSize, + C: Corpus, + R: Rand, + { + tuple_list!( + BitFlipMutator::new(), + ByteFlipMutator::new(), + ByteIncMutator::new(), + ByteDecMutator::new(), + ByteNegMutator::new(), + ByteRandMutator::new(), + ByteAddMutator::new(), + WordAddMutator::new(), + DwordAddMutator::new(), + QwordAddMutator::new(), + ByteInterestingMutator::new(), + WordInterestingMutator::new(), + DwordInterestingMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesExpandMutator::new(), + BytesInsertMutator::new(), + BytesRandInsertMutator::new(), + BytesSetMutator::new(), + BytesRandSetMutator::new(), + BytesCopyMutator::new(), + BytesSwapMutator::new(), + ) + } + #[test] fn test_mutators() { let mut inputs = vec![ @@ -829,43 +1840,16 @@ mod tests { let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ()); - let mut mutations: Vec> = vec![]; - - mutations.push(mutation_bitflip); - mutations.push(mutation_byteflip); - mutations.push(mutation_byteinc); - mutations.push(mutation_bytedec); - mutations.push(mutation_byteneg); - mutations.push(mutation_byterand); - mutations.push(mutation_byteadd); - mutations.push(mutation_wordadd); - mutations.push(mutation_dwordadd); - mutations.push(mutation_qwordadd); - mutations.push(mutation_byteinteresting); - mutations.push(mutation_wordinteresting); - mutations.push(mutation_dwordinteresting); - - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesexpand); - mutations.push(mutation_bytesinsert); - mutations.push(mutation_bytesrandinsert); - mutations.push(mutation_bytesset); - mutations.push(mutation_bytesrandset); - mutations.push(mutation_bytescopy); - mutations.push(mutation_bytesswap); - - mutations.push(mutation_tokeninsert); - mutations.push(mutation_tokenreplace); - + let mut mutations = test_mutations(); for _ in 0..2 { let mut new_testcases = vec![]; - for mutation in &mutations { + for idx in 0..(mutations.len()) { for input in inputs.iter() { let mut mutant = input.clone(); - match mutation(&mut state, &mut mutant).unwrap() { + match mutations + .get_and_mutate(idx, &mut state, &mut mutant, 0) + .unwrap() + { MutationResult::Mutated => new_testcases.push(mutant), MutationResult::Skipped => (), }; diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index 2c309d46df..5aefac6747 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -1,108 +1,161 @@ +use alloc::string::String; use alloc::vec::Vec; use core::{ - default::Default, fmt::{self, Debug}, marker::PhantomData, }; +use serde::{Deserialize, Serialize}; use crate::{ + bolts::tuples::{tuple_list, NamedTuple}, corpus::Corpus, inputs::{HasBytesVec, Input}, - mutators::Mutator, + mutators::{MutationResult, Mutator, MutatorsTuple}, state::{HasCorpus, HasMaxSize, HasMetadata, HasRand}, - utils::Rand, + utils::{AsSlice, Rand}, Error, }; pub use crate::mutators::mutations::*; pub use crate::mutators::token_mutations::*; -pub trait ScheduledMutator: Mutator + ComposedByMutations +#[derive(Serialize, Deserialize)] +pub struct MutationsMetadata { + pub list: Vec, +} + +crate::impl_serdeany!(MutationsMetadata); + +impl AsSlice for MutationsMetadata { + fn as_slice(&self) -> &[String] { + self.list.as_slice() + } +} + +impl MutationsMetadata { + pub fn new(list: Vec) -> Self { + Self { list } + } +} + +pub trait ComposedByMutations where I: Input, + MT: MutatorsTuple, +{ + /// Get the mutations + fn mutations(&self) -> &MT; + + // Get the mutations (mut) + fn mutations_mut(&mut self) -> &mut MT; +} + +pub trait ScheduledMutator: ComposedByMutations + Mutator +where + I: Input, + MT: MutatorsTuple, { /// Compute the number of iterations used to apply stacked mutations fn iterations(&self, state: &mut S, input: &I) -> u64; /// Get the next mutation to apply - fn schedule(&self, mutations_count: usize, state: &mut S, input: &I) -> usize; + fn schedule(&self, state: &mut S, input: &I) -> usize; /// New default implementation for mutate /// Implementations must forward mutate() to this method - fn scheduled_mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> { + fn scheduled_mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + let mut r = MutationResult::Skipped; let num = self.iterations(state, input); for _ in 0..num { - let idx = self.schedule(self.mutations_count(), state, input); - self.mutation_by_idx(idx)(state, input)?; + let idx = self.schedule(state, input); + let outcome = self + .mutations_mut() + .get_and_mutate(idx, state, input, stage_idx)?; + if outcome == MutationResult::Mutated { + r = MutationResult::Mutated; + } } - Ok(()) + Ok(r) } } -pub struct StdScheduledMutator +pub struct StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { - mutations: Vec>, - phantom: PhantomData, + mutations: MT, + phantom: PhantomData<(I, R, S)>, } -impl Debug for StdScheduledMutator +impl Debug for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "StdScheduledMutator with {} Mutations for Input type {}", + "StdScheduledMutator with {} mutations for Input type {}", self.mutations.len(), core::any::type_name::() ) } } -impl Mutator for StdScheduledMutator +impl Mutator for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { - fn mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> { - self.scheduled_mutate(state, input, _stage_idx) + #[inline] + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) } } -impl ComposedByMutations for StdScheduledMutator +impl ComposedByMutations for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { + /// Get the mutations #[inline] - fn mutation_by_idx(&self, index: usize) -> MutationFunction { - self.mutations[index] + fn mutations(&self) -> &MT { + &self.mutations } + // Get the mutations (mut) #[inline] - fn mutations_count(&self) -> usize { - self.mutations.len() - } - - #[inline] - fn add_mutation(&mut self, mutation: MutationFunction) { - self.mutations.push(mutation) + fn mutations_mut(&mut self) -> &mut MT { + &mut self.mutations } } -impl ScheduledMutator for StdScheduledMutator +impl ScheduledMutator for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { /// Compute the number of iterations used to apply stacked mutations fn iterations(&self, state: &mut S, _: &I) -> u64 { @@ -110,161 +163,222 @@ where } /// Get the next mutation to apply - fn schedule(&self, mutations_count: usize, state: &mut S, _: &I) -> usize { - debug_assert!(mutations_count > 0); - state.rand_mut().below(mutations_count as u64) as usize + fn schedule(&self, state: &mut S, _: &I) -> usize { + debug_assert!(!self.mutations().is_empty()); + state.rand_mut().below(self.mutations().len() as u64) as usize } } -impl StdScheduledMutator +impl StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { - /// Create a new StdScheduledMutator instance without mutations and corpus - pub fn new() -> Self { - Self { - mutations: vec![], - phantom: PhantomData, - } - } - /// Create a new StdScheduledMutator instance specifying mutations - pub fn with_mutations(mutations: Vec>) -> Self { + pub fn new(mutations: MT) -> Self { StdScheduledMutator { - mutations, + mutations: mutations, phantom: PhantomData, } } } -impl Default for StdScheduledMutator +/// Get the mutations that compose the Havoc mutator +pub fn havoc_mutations() -> impl MutatorsTuple where - I: Input, - S: HasRand, - R: Rand, -{ - fn default() -> Self { - Self::new() - } -} - -/// Schedule some selected byte level mutations given a ScheduledMutator type -#[derive(Clone, Debug)] -pub struct HavocBytesMutator -where - SM: ScheduledMutator, I: Input + HasBytesVec, S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, R: Rand, +{ + tuple_list!( + BitFlipMutator::new(), + ByteFlipMutator::new(), + ByteIncMutator::new(), + ByteDecMutator::new(), + ByteNegMutator::new(), + ByteRandMutator::new(), + ByteAddMutator::new(), + WordAddMutator::new(), + DwordAddMutator::new(), + QwordAddMutator::new(), + ByteInterestingMutator::new(), + WordInterestingMutator::new(), + DwordInterestingMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesExpandMutator::new(), + BytesInsertMutator::new(), + BytesRandInsertMutator::new(), + BytesSetMutator::new(), + BytesRandSetMutator::new(), + BytesCopyMutator::new(), + BytesSwapMutator::new(), + TokenInsert::new(), + TokenReplace::new(), + CrossoverInsertMutator::new(), + CrossoverReplaceMutator::new(), + ) +} + +//wraps around StdScheduledMutator +pub struct LoggerScheduledMutator +where + C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, + R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { scheduled: SM, - phantom: PhantomData<(C, I, R, S)>, + mutation_log: Vec, + phantom: PhantomData<(C, I, MT, R, S)>, } -impl Mutator for HavocBytesMutator +impl Debug for LoggerScheduledMutator where - SM: ScheduledMutator, - I: Input + HasBytesVec, - S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { - /// Mutate bytes - fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error> { - self.scheduled.mutate(state, input, stage_idx)?; - /*let num = self.scheduled.iterations(state, input); - for _ in 0..num { - let idx = self.scheduled.schedule(14, state, input); - let mutation = match idx { - 0 => mutation_bitflip, - 1 => mutation_byteflip, - 2 => mutation_byteinc, - 3 => mutation_bytedec, - 4 => mutation_byteneg, - 5 => mutation_byterand, + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "LoggerScheduledMutator with {} mutations for Input type {}", + self.scheduled.mutations().len(), + core::any::type_name::() + ) + } +} - 6 => mutation_byteadd, - 7 => mutation_wordadd, - 8 => mutation_dwordadd, - 9 => mutation_byteinteresting, - 10 => mutation_wordinteresting, - 11 => mutation_dwordinteresting, - _ => mutation_splice, - }; - mutation(self, state, input)?; - }*/ +impl Mutator for LoggerScheduledMutator +where + C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, + R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) + } + + fn post_exec( + &mut self, + state: &mut S, + _stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + if let Some(idx) = corpus_idx { + let mut testcase = (*state.corpus_mut().get(idx)?).borrow_mut(); + let mut log = Vec::::new(); + while let Some(idx) = self.mutation_log.pop() { + let name = String::from(self.scheduled.mutations().get_name(idx).unwrap()); // TODO maybe return an Error on None + log.push(name) + } + let meta = MutationsMetadata::new(log); + testcase.add_metadata(meta); + }; + // Always reset the log for each run + self.mutation_log.clear(); Ok(()) } } -impl HavocBytesMutator +impl ComposedByMutations + for LoggerScheduledMutator where - SM: ScheduledMutator, - I: Input + HasBytesVec, - S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { - /// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap - pub fn new(mut scheduled: SM) -> Self { - scheduled.add_mutation(mutation_bitflip); - scheduled.add_mutation(mutation_splice); - Self { - scheduled, - phantom: PhantomData, - } + #[inline] + fn mutations(&self) -> &MT { + self.scheduled.mutations() + } + + #[inline] + fn mutations_mut(&mut self) -> &mut MT { + self.scheduled.mutations_mut() } } -impl Default for HavocBytesMutator> +impl ScheduledMutator for LoggerScheduledMutator where - I: Input + HasBytesVec, - S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { - /// Create a new HavocBytesMutator instance wrapping StdScheduledMutator - fn default() -> Self { - let mut scheduled = StdScheduledMutator::::new(); - scheduled.add_mutation(mutation_bitflip); - scheduled.add_mutation(mutation_byteflip); - scheduled.add_mutation(mutation_byteinc); - scheduled.add_mutation(mutation_bytedec); - scheduled.add_mutation(mutation_byteneg); - scheduled.add_mutation(mutation_byterand); + /// Compute the number of iterations used to apply stacked mutations + fn iterations(&self, state: &mut S, _: &I) -> u64 { + 1 << (1 + state.rand_mut().below(6)) + } - scheduled.add_mutation(mutation_byteadd); - scheduled.add_mutation(mutation_wordadd); - scheduled.add_mutation(mutation_dwordadd); - scheduled.add_mutation(mutation_qwordadd); - scheduled.add_mutation(mutation_byteinteresting); - scheduled.add_mutation(mutation_wordinteresting); - scheduled.add_mutation(mutation_dwordinteresting); + /// Get the next mutation to apply + fn schedule(&self, state: &mut S, _: &I) -> usize { + debug_assert!(!self.scheduled.mutations().is_empty()); + state + .rand_mut() + .below(self.scheduled.mutations().len() as u64) as usize + } - scheduled.add_mutation(mutation_bytesdelete); - scheduled.add_mutation(mutation_bytesdelete); - scheduled.add_mutation(mutation_bytesdelete); - scheduled.add_mutation(mutation_bytesdelete); - scheduled.add_mutation(mutation_bytesexpand); - scheduled.add_mutation(mutation_bytesinsert); - scheduled.add_mutation(mutation_bytesrandinsert); - scheduled.add_mutation(mutation_bytesset); - scheduled.add_mutation(mutation_bytesrandset); - scheduled.add_mutation(mutation_bytescopy); - scheduled.add_mutation(mutation_bytesswap); + fn scheduled_mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + let mut r = MutationResult::Skipped; + let num = self.iterations(state, input); + self.mutation_log.clear(); + for _ in 0..num { + let idx = self.schedule(state, input); + self.mutation_log.push(idx); + let outcome = self + .mutations_mut() + .get_and_mutate(idx, state, input, stage_idx)?; + if outcome == MutationResult::Mutated { + r = MutationResult::Mutated; + } + } + Ok(r) + } +} - scheduled.add_mutation(mutation_tokeninsert); - scheduled.add_mutation(mutation_tokenreplace); - - scheduled.add_mutation(mutation_crossover_insert); - scheduled.add_mutation(mutation_crossover_replace); - //scheduled.add_mutation(mutation_splice); - - HavocBytesMutator { - scheduled, +impl LoggerScheduledMutator +where + C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, + R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, +{ + /// Create a new StdScheduledMutator instance without mutations and corpus + pub fn new(scheduled: SM) -> Self { + Self { + scheduled: scheduled, + mutation_log: vec![], phantom: PhantomData, } } @@ -276,7 +390,8 @@ mod tests { corpus::{Corpus, InMemoryCorpus, Testcase}, inputs::{BytesInput, HasBytesVec}, mutators::{ - scheduled::{mutation_splice, HavocBytesMutator, StdScheduledMutator}, + mutations::SpliceMutator, + scheduled::{havoc_mutations, StdScheduledMutator}, Mutator, }, state::State, @@ -302,7 +417,8 @@ mod tests { rand.set_seed(5); - mutation_splice(&mut state, &mut input).unwrap(); + let mut splice = SpliceMutator::new(); + splice.mutate(&mut state, &mut input, 0).unwrap(); #[cfg(feature = "std")] println!("{:?}", input.bytes()); @@ -330,7 +446,7 @@ mod tests { let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ()); - let havoc = HavocBytesMutator::new(StdScheduledMutator::new()); + let mut havoc = StdScheduledMutator::new(havoc_mutations()); assert_eq!(input, input_prior); diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 715bac7f35..240f2afae0 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -1,6 +1,5 @@ //! Tokens are what afl calls extras or dictionaries. //! They may be inserted as part of mutations during fuzzing. - #[cfg(feature = "std")] use std::{ fs::File, @@ -15,6 +14,7 @@ use crate::{ utils::Rand, Error, }; +use core::marker::PhantomData; use alloc::vec::Vec; use serde::{Deserialize, Serialize}; @@ -121,84 +121,164 @@ impl Tokens { } } -/// Insert a dictionary token -pub fn mutation_tokeninsert(state: &mut S, input: &mut I) -> Result +#[derive(Default)] +pub struct TokenInsert where I: Input + HasBytesVec, S: HasMetadata + HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let tokens_len = { - let meta = state.metadata().get::(); - if meta.is_none() { - return Ok(MutationResult::Skipped); - } - if meta.unwrap().tokens().is_empty() { - return Ok(MutationResult::Skipped); - } - meta.unwrap().tokens().len() - }; - let token_idx = state.rand_mut().below(tokens_len as u64) as usize; - - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - - let meta = state.metadata().get::().unwrap(); - let token = &meta.tokens()[token_idx]; - let mut len = token.len(); - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_copy(input.bytes_mut(), token, 0, off, len); - - Ok(MutationResult::Mutated) + phantom: PhantomData<(I, R, S)>, } -/// Overwrite with a dictionary token -pub fn mutation_tokenreplace(state: &mut S, input: &mut I) -> Result +impl Mutator for TokenInsert where I: Input + HasBytesVec, - S: HasMetadata + HasRand, + S: HasMetadata + HasRand + HasMaxSize, R: Rand, { - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let tokens_len = { + let meta = state.metadata().get::(); + if meta.is_none() { + return Ok(MutationResult::Skipped); + } + if meta.unwrap().tokens().is_empty() { + return Ok(MutationResult::Skipped); + } + meta.unwrap().tokens().len() + }; + let token_idx = state.rand_mut().below(tokens_len as u64) as usize; - let tokens_len = { - let meta = state.metadata().get::(); - if meta.is_none() { + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + + let meta = state.metadata().get::().unwrap(); + let token = &meta.tokens()[token_idx]; + let mut len = token.len(); + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_copy(input.bytes_mut(), token, 0, off, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for TokenInsert +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + fn name(&self) -> &str { + "TokenInsert" + } +} + +impl TokenInsert +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +#[derive(Default)] +pub struct TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size == 0 { return Ok(MutationResult::Skipped); } - if meta.unwrap().tokens().is_empty() { - return Ok(MutationResult::Skipped); + + let tokens_len = { + let meta = state.metadata().get::(); + if meta.is_none() { + return Ok(MutationResult::Skipped); + } + if meta.unwrap().tokens().is_empty() { + return Ok(MutationResult::Skipped); + } + meta.unwrap().tokens().len() + }; + let token_idx = state.rand_mut().below(tokens_len as u64) as usize; + + let off = state.rand_mut().below(size as u64) as usize; + + let meta = state.metadata().get::().unwrap(); + let token = &meta.tokens()[token_idx]; + let mut len = token.len(); + if off + len > size { + len = size - off; } - meta.unwrap().tokens().len() - }; - let token_idx = state.rand_mut().below(tokens_len as u64) as usize; - let off = state.rand_mut().below(size as u64) as usize; + buffer_copy(input.bytes_mut(), token, 0, off, len); - let meta = state.metadata().get::().unwrap(); - let token = &meta.tokens()[token_idx]; - let mut len = token.len(); - if off + len > size { - len = size - off; + Ok(MutationResult::Mutated) } +} - buffer_copy(input.bytes_mut(), token, 0, off, len); +impl Named for TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + fn name(&self) -> &str { + "TokenReplace" + } +} - Ok(MutationResult::Mutated) +impl TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } } #[cfg(test)] diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 7404ceeeb9..26798fdaf1 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -9,7 +9,7 @@ use core::time::Duration; use serde::{Deserialize, Serialize}; use crate::{ - bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named, TupleList}, + bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named}, utils::current_time, Error, }; @@ -75,7 +75,7 @@ impl ObserversTuple for () { impl ObserversTuple for (Head, Tail) where Head: Observer, - Tail: ObserversTuple + TupleList, + Tail: ObserversTuple, { fn pre_exec_all(&mut self) -> Result<(), Error> { self.0.pre_exec()?; @@ -145,7 +145,7 @@ mod tests { fn test_observer_serde() { let obv = tuple_list!( TimeObserver::new("time"), - StdMapObserver::new("map", unsafe { &mut MAP }) + StdMapObserver::new("map", unsafe { &mut MAP }, unsafe { MAP.len() }) ); let vec = postcard::to_allocvec(&obv).unwrap(); println!("{:?}", vec); diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 6d91508a58..b324779990 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -18,7 +18,7 @@ where { /// Run the stage fn perform( - &self, + &mut self, state: &mut S, executor: &mut E, manager: &mut EM, @@ -34,7 +34,7 @@ where I: Input, { fn perform_all( - &self, + &mut self, state: &mut S, executor: &mut E, manager: &mut EM, @@ -49,7 +49,14 @@ where E: Executor, I: Input, { - fn perform_all(&self, _: &mut S, _: &mut E, _: &mut EM, _: &CS, _: usize) -> Result<(), Error> { + fn perform_all( + &mut self, + _: &mut S, + _: &mut E, + _: &mut EM, + _: &CS, + _: usize, + ) -> Result<(), Error> { Ok(()) } } @@ -63,7 +70,7 @@ where I: Input, { fn perform_all( - &self, + &mut self, state: &mut S, executor: &mut E, manager: &mut EM, diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index cd872b3462..9c9b4d3731 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -40,7 +40,7 @@ where /// Runs this (mutational) stage for the given testcase fn perform_mutational( - &self, + &mut self, state: &mut S, executor: &mut E, manager: &mut EM, @@ -55,11 +55,10 @@ where .borrow_mut() .load_input()? .clone(); - self.mutator().mutate(state, &mut input_mut, i as i32)?; + self.mutator_mut().mutate(state, &mut input_mut, i as i32)?; + let (_, corpus_idx) = state.evaluate_input(input_mut, executor, manager, scheduler)?; - let fitness = state.evaluate_input(input_mut, executor, manager, scheduler)?; - - self.mutator().post_exec(state, fitness, i as i32)?; + self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; } Ok(()) } @@ -131,7 +130,7 @@ where { #[inline] fn perform( - &self, + &mut self, state: &mut S, executor: &mut E, manager: &mut EM, diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 5f4cf154f5..b55cec8fc1 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -198,7 +198,7 @@ where executor: &mut E, manager: &mut EM, scheduler: &CS, - ) -> Result + ) -> Result<(u32, Option), Error> where E: Executor + HasObservers, OT: ObserversTuple, @@ -499,7 +499,7 @@ where executor: &mut E, manager: &mut EM, scheduler: &CS, - ) -> Result + ) -> Result<(u32, Option), Error> where E: Executor + HasObservers, OT: ObserversTuple, @@ -514,11 +514,8 @@ where // If the input is a solution, add it to the respective corpus self.solutions_mut().add(Testcase::new(input.clone()))?; } - - if self - .add_if_interesting(&input, fitness, scheduler)? - .is_some() - { + let corpus_idx = self.add_if_interesting(&input, fitness, scheduler)?; + if corpus_idx.is_some() { let observers_buf = manager.serialize_observers(observers)?; manager.fire( self, @@ -533,7 +530,7 @@ where )?; } - Ok(fitness) + Ok((fitness, corpus_idx)) } } @@ -683,7 +680,7 @@ where let mut added = 0; for _ in 0..num { let input = generator.generate(self.rand_mut())?; - let fitness = self.evaluate_input(input, executor, manager, scheduler)?; + let (fitness, _) = self.evaluate_input(input, executor, manager, scheduler)?; if fitness > 0 { added += 1; } diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index f7c5fb3646..efec6f17cf 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi "] edition = "2018" [features] -default = ["sancov", "libfuzzer_compatibility"] +default = [] sancov = [] libfuzzer_compatibility = [] From a0550b31542b703aea82fa2ea53497e8fee49075 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Mar 2021 18:50:40 +0100 Subject: [PATCH 023/104] compile libfuzzer_libpng on windows --- fuzzers/libfuzzer_libpng/src/lib.rs | 15 +--- libafl/src/bolts/shmem.rs | 8 +- libafl/src/executors/mod.rs | 3 - libafl/src/executors/timeout.rs | 7 -- libafl_targets/libfuzzer_compatibility.c | 89 +++++++++++++++---- libafl_targets/src/libfuzzer_compatibility.rs | 8 +- 6 files changed, 87 insertions(+), 43 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 56dfdead1a..15d55b30a3 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -1,12 +1,11 @@ //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! The example harness is built for libpng. -#[cfg(unix)] use core::time::Duration; use std::{env, path::PathBuf}; use libafl::{ - bolts::{shmem::UnixShMem, tuples::tuple_list}, + bolts::{shmem::StdShMem, tuples::tuple_list}, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, @@ -46,21 +45,14 @@ pub fn main() { .expect("An error occurred while fuzzing"); } -/// Not supported on windows right now -#[cfg(windows)] -fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { - todo!("Example not supported on Windows"); -} - /// The actual fuzzer -#[cfg(unix)] fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { // 'While the stats are state, they are usually used in the broker - which is likely never restarted let stats = SimpleStats::new(|s| println!("{}", s)); // The restarting state will spawn the same process again as child, then restarted it each time it crashes. let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { + match setup_restarting_mgr::<_, _, StdShMem, _>(stats, broker_port) { Ok(res) => res, Err(err) => match err { Error::ShuttingDown => { @@ -139,7 +131,8 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. - if libfuzzer_initialize() == -1 { + let args: Vec = env::args().collect(); + if libfuzzer_initialize(&args) == -1 { println!("Warning: LLVMFuzzerInitialize failed with -1") } diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index f3a95d6092..3c704be519 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -3,9 +3,13 @@ #[cfg(all(feature = "std", unix))] pub use unix_shmem::UnixShMem; +#[cfg(all(feature = "std", unix))] +pub type StdShMem = UnixShMem; #[cfg(all(windows, feature = "std"))] -pub use shmem::Win32ShMem; +pub use win32_shmem::Win32ShMem; +#[cfg(all(windows, feature = "std"))] +pub type StdShMem = Win32ShMem; use alloc::string::{String, ToString}; use core::fmt::Debug; @@ -442,7 +446,7 @@ pub mod unix_shmem { } #[cfg(all(feature = "std", windows))] -pub mod shmem { +pub mod win32_shmem { use super::ShMem; use crate::{ diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 705b317d33..3c751e080e 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -3,10 +3,7 @@ pub mod inprocess; pub use inprocess::InProcessExecutor; pub mod timeout; -#[cfg(unix)] pub use timeout::TimeoutExecutor; -#[cfg(feature = "runtime")] -pub mod runtime; use core::cmp::PartialEq; use core::marker::PhantomData; diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index dd88613455..6acb753032 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -1,9 +1,7 @@ //! A TimeoutExecutor set a timeout before each target run -#[cfg(unix)] use core::{marker::PhantomData, time::Duration}; -#[cfg(unix)] use crate::{ bolts::tuples::Named, events::EventManager, @@ -41,7 +39,6 @@ extern "C" { const ITIMER_REAL: c_int = 0; /// The timeout excutor is a wrapper that set a timeout before each run -#[cfg(unix)] pub struct TimeoutExecutor where E: Executor + HasObservers, @@ -53,7 +50,6 @@ where phantom: PhantomData<(I, OT)>, } -#[cfg(unix)] impl Named for TimeoutExecutor where E: Executor + HasObservers, @@ -65,7 +61,6 @@ where } } -#[cfg(unix)] impl HasObservers for TimeoutExecutor where E: Executor + HasObservers, @@ -83,7 +78,6 @@ where } } -#[cfg(unix)] impl TimeoutExecutor where E: Executor + HasObservers, @@ -99,7 +93,6 @@ where } } -#[cfg(unix)] impl Executor for TimeoutExecutor where E: Executor + HasObservers, diff --git a/libafl_targets/libfuzzer_compatibility.c b/libafl_targets/libfuzzer_compatibility.c index d5a304348a..79d2391b4a 100644 --- a/libafl_targets/libfuzzer_compatibility.c +++ b/libafl_targets/libfuzzer_compatibility.c @@ -1,25 +1,80 @@ -static int orig_argc; -static char **orig_argv; -static char **orig_envp; +#include +#include +#include -static void save_main_args(int argc, char** argv, char** envp) { - orig_argc = argc; - orig_argv = argv; - orig_envp = envp; +#define true 1 +#define false 0 + +#ifdef _WIN32 + +#ifdef _MSC_VER +#define LIBFUZZER_MSVC 1 +#else +#define LIBFUZZER_MSVC 0 +#endif // _MSC_VER + +// From Libfuzzer +// Intermediate macro to ensure the parameter is expanded before stringified. +#define STRINGIFY_(A) #A +#define STRINGIFY(A) STRINGIFY_(A) + +#if LIBFUZZER_MSVC +// Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h +#if defined(_M_IX86) || defined(__i386__) +#define WIN_SYM_PREFIX "_" +#else +#define WIN_SYM_PREFIX +#endif + +// Declare external functions as having alternativenames, so that we can +// determine if they are not defined. +#define EXTERNAL_FUNC(Name, Default) \ + __pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ + Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) +#else +// Declare external functions as weak to allow them to default to a specified +// function if not defined explicitly. We must use weak symbols because clang's +// support for alternatename is not 100%, see +// https://bugs.llvm.org/show_bug.cgi?id=40218 for more details. +#define EXTERNAL_FUNC(Name, Default) \ + __attribute__((weak, alias(STRINGIFY(Default)))) +#endif // LIBFUZZER_MSVC + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE NAME##Def FUNC_SIG { \ + printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ + exit(1); \ + } \ + EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG + +#else + +// Declare these symbols as weak to allow them to be optionally defined. +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG + +#endif + +EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); +EXT_FUNC(LLVMFuzzerCustomMutator, size_t, + (uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed), + false); +EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, + (const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, unsigned int Seed), + false); + +#undef EXT_FUNC + +int libafl_targets_has_libfuzzer_init() { + return LLVMFuzzerInitialize != NULL; } -__attribute__((section(".init_array"))) -void (*p_libafl_targets_save_main_args)(int, char*[], char*[]) = &save_main_args; - -__attribute__((weak)) -int LLVMFuzzerInitialize(int *argc, char ***argv); - -int libafl_targets_libfuzzer_init() { - +int libafl_targets_libfuzzer_init(int *argc, char ***argv) { if (LLVMFuzzerInitialize) { - return LLVMFuzzerInitialize(&orig_argc, &orig_argv); + return LLVMFuzzerInitialize(argc, argv); } else { return 0; } - } diff --git a/libafl_targets/src/libfuzzer_compatibility.rs b/libafl_targets/src/libfuzzer_compatibility.rs index a4a29fd362..f17eade2d9 100644 --- a/libafl_targets/src/libfuzzer_compatibility.rs +++ b/libafl_targets/src/libfuzzer_compatibility.rs @@ -4,11 +4,13 @@ extern "C" { fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; // libafl_targets_libfuzzer_init calls LLVMFUzzerInitialize() - fn libafl_targets_libfuzzer_init() -> i32; + fn libafl_targets_libfuzzer_init(argc: *const i32, argv: *const *const *const u8) -> i32; } -pub fn libfuzzer_initialize() -> i32 { - unsafe { libafl_targets_libfuzzer_init() } +pub fn libfuzzer_initialize(args: &[String]) -> i32 { + let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); + let argc = argv.len() as i32; + unsafe { libafl_targets_libfuzzer_init(&argc as *const i32, &argv.as_ptr() as *const *const *const u8) } } pub fn libfuzzer_test_one_input(buf: &[u8]) -> i32 { From 6ddc3ef85a333104e408821b41b634527b4975df Mon Sep 17 00:00:00 2001 From: andreafioraldi Date: Thu, 25 Mar 2021 18:53:40 +0100 Subject: [PATCH 024/104] format --- libafl_targets/src/libfuzzer_compatibility.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libafl_targets/src/libfuzzer_compatibility.rs b/libafl_targets/src/libfuzzer_compatibility.rs index f17eade2d9..903f394e50 100644 --- a/libafl_targets/src/libfuzzer_compatibility.rs +++ b/libafl_targets/src/libfuzzer_compatibility.rs @@ -8,9 +8,14 @@ extern "C" { } pub fn libfuzzer_initialize(args: &[String]) -> i32 { - let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); - let argc = argv.len() as i32; - unsafe { libafl_targets_libfuzzer_init(&argc as *const i32, &argv.as_ptr() as *const *const *const u8) } + let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); + let argc = argv.len() as i32; + unsafe { + libafl_targets_libfuzzer_init( + &argc as *const i32, + &argv.as_ptr() as *const *const *const u8, + ) + } } pub fn libfuzzer_test_one_input(buf: &[u8]) -> i32 { From 40fe286cf9e20e5ddf921967c44b0fd4e667d60a Mon Sep 17 00:00:00 2001 From: andreafioraldi Date: Thu, 25 Mar 2021 20:04:52 +0100 Subject: [PATCH 025/104] run on win32 using the clang wrapper --- fuzzers/libfuzzer_libpng/src/bin/cc.rs | 8 ++++++-- fuzzers/libfuzzer_libpng/src/bin/cxx.rs | 8 ++++++-- fuzzers/libfuzzer_libpng/src/lib.rs | 2 +- libafl_cc/src/lib.rs | 11 +++++++++++ libafl_targets/libfuzzer_compatibility.c | 17 +++++++++-------- 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/fuzzers/libfuzzer_libpng/src/bin/cc.rs b/fuzzers/libfuzzer_libpng/src/bin/cc.rs index 8a34130ba4..f8f4ca1d2d 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cc.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/cc.rs @@ -1,4 +1,4 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX}; use std::env; fn main() { @@ -11,7 +11,11 @@ fn main() { .unwrap() .add_arg("-fsanitize-coverage=trace-pc-guard".into()) .unwrap() - .add_link_arg(dir.join("liblibfuzzer_libpng.a").display().to_string()) + .add_link_arg( + dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT)) + .display() + .to_string(), + ) .unwrap() .run() .unwrap(); diff --git a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs index aaf901835e..bad4aa18d4 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs @@ -1,4 +1,4 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX}; use std::env; fn main() { @@ -12,7 +12,11 @@ fn main() { .unwrap() .add_arg("-fsanitize-coverage=trace-pc-guard".into()) .unwrap() - .add_link_arg(dir.join("liblibfuzzer_libpng.a").display().to_string()) + .add_link_arg( + dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT)) + .display() + .to_string(), + ) .unwrap() .run() .unwrap(); diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 15d55b30a3..f64e92e385 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -131,7 +131,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. - let args: Vec = env::args().collect(); + let args: Vec = env::args().collect(); if libfuzzer_initialize(&args) == -1 { println!("Warning: LLVMFuzzerInitialize failed with -1") } diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 9b27de117b..4898150c66 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -7,6 +7,17 @@ pub enum Error { Unknown(String), } +// TODO macOS +#[cfg(windows)] +pub const LIB_EXT: &'static str = "lib"; +#[cfg(not(windows))] +pub const LIB_EXT: &'static str = "a"; + +#[cfg(windows)] +pub const LIB_PREFIX: &'static str = ""; +#[cfg(not(windows))] +pub const LIB_PREFIX: &'static str = "lib"; + /// Wrap a compiler hijacking its arguments pub trait CompilerWrapper { /// Set the wrapper arguments parsing a command line set of arguments diff --git a/libafl_targets/libfuzzer_compatibility.c b/libafl_targets/libfuzzer_compatibility.c index 79d2391b4a..8136c7237f 100644 --- a/libafl_targets/libfuzzer_compatibility.c +++ b/libafl_targets/libfuzzer_compatibility.c @@ -31,6 +31,8 @@ #define EXTERNAL_FUNC(Name, Default) \ __pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) + +#define CHECK_WEAK_FN(Name) (Name != &Name##Def) #else // Declare external functions as weak to allow them to default to a specified // function if not defined explicitly. We must use weak symbols because clang's @@ -38,21 +40,20 @@ // https://bugs.llvm.org/show_bug.cgi?id=40218 for more details. #define EXTERNAL_FUNC(Name, Default) \ __attribute__((weak, alias(STRINGIFY(Default)))) + +#define CHECK_WEAK_FN(Name) (Name != NULL) #endif // LIBFUZZER_MSVC #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ - RETURN_TYPE NAME##Def FUNC_SIG { \ - printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ - exit(1); \ - } \ + RETURN_TYPE (*NAME##Def) FUNC_SIG = NULL; \ EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG - #else // Declare these symbols as weak to allow them to be optionally defined. #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG +#define CHECK_WEAK_FN(Name) (Name != NULL) #endif EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); @@ -68,13 +69,13 @@ EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, #undef EXT_FUNC int libafl_targets_has_libfuzzer_init() { - return LLVMFuzzerInitialize != NULL; + return CHECK_WEAK_FN(LLVMFuzzerInitialize); } int libafl_targets_libfuzzer_init(int *argc, char ***argv) { - if (LLVMFuzzerInitialize) { + if (libafl_targets_has_libfuzzer_init()) { return LLVMFuzzerInitialize(argc, argv); } else { return 0; } -} +} \ No newline at end of file From 7564ce1e87a80b1d86f16c185a25c0a9e83b7ffb Mon Sep 17 00:00:00 2001 From: andreafioraldi Date: Fri, 26 Mar 2021 10:39:02 +0100 Subject: [PATCH 026/104] libfuzzer_stb_image with build.rs and win32 fixes --- Cargo.toml | 2 +- fuzzers/libfuzzer_libpng/Cargo.toml | 2 +- fuzzers/libfuzzer_libpng/src/bin/cc.rs | 18 +- fuzzers/libfuzzer_libpng/src/bin/cxx.rs | 18 +- fuzzers/libfuzzer_libpng/src/lib.rs | 6 +- fuzzers/libfuzzer_stb_image/.gitignore | 1 + fuzzers/libfuzzer_stb_image/Cargo.toml | 24 + fuzzers/libfuzzer_stb_image/README.md | 43 + fuzzers/libfuzzer_stb_image/build.rs | 27 + .../libfuzzer_stb_image/corpus/not_kitty.png | Bin 0 -> 218 bytes .../corpus/not_kitty_alpha.png | Bin 0 -> 376 bytes .../corpus/not_kitty_gamma.png | Bin 0 -> 228 bytes .../corpus/not_kitty_icc.png | Bin 0 -> 427 bytes fuzzers/libfuzzer_stb_image/harness.c | 61 + fuzzers/libfuzzer_stb_image/src/main.rs | 150 + fuzzers/libfuzzer_stb_image/stb_image.h | 7762 +++++++++++++++++ fuzzers/libfuzzer_windows/harness.cc | 6 +- fuzzers/libfuzzer_windows/src/fuzzer.rs | 18 +- libafl_targets/Cargo.toml | 6 +- libafl_targets/build.rs | 7 +- libafl_targets/libfuzzer_compatibility.c | 10 +- libafl_targets/src/lib.rs | 24 +- ...ibfuzzer_compatibility.rs => libfuzzer.rs} | 0 libafl_targets/src/{sancov.rs => pcguard.rs} | 18 +- 24 files changed, 8149 insertions(+), 54 deletions(-) create mode 100644 fuzzers/libfuzzer_stb_image/.gitignore create mode 100644 fuzzers/libfuzzer_stb_image/Cargo.toml create mode 100644 fuzzers/libfuzzer_stb_image/README.md create mode 100644 fuzzers/libfuzzer_stb_image/build.rs create mode 100644 fuzzers/libfuzzer_stb_image/corpus/not_kitty.png create mode 100644 fuzzers/libfuzzer_stb_image/corpus/not_kitty_alpha.png create mode 100644 fuzzers/libfuzzer_stb_image/corpus/not_kitty_gamma.png create mode 100644 fuzzers/libfuzzer_stb_image/corpus/not_kitty_icc.png create mode 100644 fuzzers/libfuzzer_stb_image/harness.c create mode 100644 fuzzers/libfuzzer_stb_image/src/main.rs create mode 100644 fuzzers/libfuzzer_stb_image/stb_image.h rename libafl_targets/src/{libfuzzer_compatibility.rs => libfuzzer.rs} (100%) rename libafl_targets/src/{sancov.rs => pcguard.rs} (54%) diff --git a/Cargo.toml b/Cargo.toml index 38b637badc..519d09a76f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ "libafl_targets", #example fuzzers - #"fuzzers/libfuzzer_libpng", "fuzzers/frida_libpng", "fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libpng_cmpalloc", @@ -20,4 +19,5 @@ members = [ ] exclude = [ "fuzzers/libfuzzer_libpng", + "fuzzers/libfuzzer_stb_image", ] diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index ba4698b223..5d1c03c221 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -18,7 +18,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/", features = ["sancov", "libfuzzer_compatibility"] } +libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } diff --git a/fuzzers/libfuzzer_libpng/src/bin/cc.rs b/fuzzers/libfuzzer_libpng/src/bin/cc.rs index f8f4ca1d2d..3100d34c99 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cc.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/cc.rs @@ -6,18 +6,26 @@ fn main() { if args.len() > 1 { let mut dir = env::current_exe().unwrap(); dir.pop(); - ClangWrapper::new("clang", "clang++") - .from_args(&args) + + let mut cc = ClangWrapper::new("clang", "clang++"); + cc.from_args(&args) .unwrap() .add_arg("-fsanitize-coverage=trace-pc-guard".into()) .unwrap() .add_link_arg( - dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT)) + dir.join(format!("{}libfuzzer_stb_image.{}", LIB_PREFIX, LIB_EXT)) .display() .to_string(), ) - .unwrap() - .run() .unwrap(); + // Libraries needed by libafl on Windows + #[cfg(windows)] + cc.add_link_arg("-lws2_32".into()) + .unwrap() + .add_link_arg("-lBcrypt".into()) + .unwrap() + .add_link_arg("-lAdvapi32".into()) + .unwrap(); + cc.run().unwrap(); } } diff --git a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs index bad4aa18d4..3a7701d555 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs @@ -6,19 +6,27 @@ fn main() { if args.len() > 1 { let mut dir = env::current_exe().unwrap(); dir.pop(); - ClangWrapper::new("clang", "clang++") - .is_cpp() + + let mut cc = ClangWrapper::new("clang", "clang++"); + cc.is_cpp() .from_args(&args) .unwrap() .add_arg("-fsanitize-coverage=trace-pc-guard".into()) .unwrap() .add_link_arg( - dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT)) + dir.join(format!("{}libfuzzer_stb_image.{}", LIB_PREFIX, LIB_EXT)) .display() .to_string(), ) - .unwrap() - .run() .unwrap(); + // Libraries needed by libafl on Windows + #[cfg(windows)] + cc.add_link_arg("-lws2_32".into()) + .unwrap() + .add_link_arg("-lBcrypt".into()) + .unwrap() + .add_link_arg("-lAdvapi32".into()) + .unwrap(); + cc.run().unwrap(); } } diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index f64e92e385..92cd079c24 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -107,9 +107,11 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let stage = StdMutationalStage::new(mutator); // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + // A minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { libfuzzer_test_one_input(buf); @@ -119,7 +121,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create the executor for an in-process function with just one observer for edge coverage let mut executor = TimeoutExecutor::new( InProcessExecutor::new( - "in-process(edges)", + "in-process(edges,time)", &mut harness, tuple_list!(edges_observer, TimeObserver::new("time")), &mut state, diff --git a/fuzzers/libfuzzer_stb_image/.gitignore b/fuzzers/libfuzzer_stb_image/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/libfuzzer_stb_image/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/libfuzzer_stb_image/Cargo.toml b/fuzzers/libfuzzer_stb_image/Cargo.toml new file mode 100644 index 0000000000..6328090379 --- /dev/null +++ b/fuzzers/libfuzzer_stb_image/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "libfuzzer_stb_image" +version = "0.1.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" +build = "build.rs" + +[features] +default = ["std"] +std = [] + +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[dependencies] +libafl = { path = "../../libafl/" } +libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "libfuzzer"] } + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +num_cpus = "1.0" \ No newline at end of file diff --git a/fuzzers/libfuzzer_stb_image/README.md b/fuzzers/libfuzzer_stb_image/README.md new file mode 100644 index 0000000000..acb7426f8a --- /dev/null +++ b/fuzzers/libfuzzer_stb_image/README.md @@ -0,0 +1,43 @@ +# Libfuzzer for libpng + +This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. +To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. +It has been tested on Linux. + +## Build + +To build this example, run `cargo build --release`. +This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. +In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target. + +Then download libpng from https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz and unpack the archive. + +Now compile it with: + +``` +cd libpng-1.6.37 +./configure +make CC=/path/to/libfuzzer_libpng/target/release/cc -j `nproc` +``` + +You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`. + +Now, we have to build the libfuzzer harness and link all togheter to create our fuzzer binary. + +``` +/path/to/libfuzzer_libpng/target/debug/cxx /path/to/libfuzzer_libpng/harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer -lz -lm +``` + +Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`. + +## Run + +The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. + +Each following execution will run a fuzzer client. +As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). +This means each client will start itself again to listen for crashes and timeouts. +By restarting the actual fuzzer, it can recover from these exit conditions. + +In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). + diff --git a/fuzzers/libfuzzer_stb_image/build.rs b/fuzzers/libfuzzer_stb_image/build.rs new file mode 100644 index 0000000000..50cb1582bc --- /dev/null +++ b/fuzzers/libfuzzer_stb_image/build.rs @@ -0,0 +1,27 @@ +// build.rs + +use std::env; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = out_dir.to_string_lossy().to_string(); + + println!("cargo:rerun-if-changed=harness.c"); + + // Enforce clang for its -fsanitize-coverage support. + std::env::set_var("CC", "clang"); + std::env::set_var("CXX", "clang++"); + + cc::Build::new() + // Use sanitizer coverage to track the edges in the PUT + .flag("-fsanitize-coverage=trace-pc-guard") + // Take advantage of LTO (needs lld-link set in your cargo config) + //.flag("-flto=thin") + .flag("-Wno-sign-compare") + .file("./harness.c") + .compile("harness"); + + println!("cargo:rustc-link-search=native={}", &out_dir); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/fuzzers/libfuzzer_stb_image/corpus/not_kitty.png b/fuzzers/libfuzzer_stb_image/corpus/not_kitty.png new file mode 100644 index 0000000000000000000000000000000000000000..eff7c1707b936a8f8df725814f604d454b78b5c3 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X_yc@GT+_~+`TzevkY_wIZRYx+5&y#hyq+?%!C8<`)MX5lF!N|bSRM)^r*U&J;z}U*bz{;0L z1Vuw`eoAIqC5i?kD`P_|6GMoGiCWXn12ss3YzWRzD=AMbN@Z|N$xljE@XSq2PYp^< WOsOn9nQ8-6#Ng@b=d#Wzp$PyV*n0l} literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_stb_image/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_stb_image/corpus/not_kitty_gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..939d9d29a9b9f95bac5e9a72854361ee85469921 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmTQ929t;oCfmw1AIbU z)6Sgv|NlRbXFM})=KnKxKI=t+9LW;bh?3y^w370~qErUQl>DSr1<%~X^wgl##FWay zlc_d9MbVxvjv*GO?@o5)YH;9THa`3B|5>?^8?LvjJ}xLe>!7e@k)r^sLedir0mCVe z=5sMjEm$*~tHD+}{NS_$nMdb|ABqg-@UGMMsZ=uY-X%Cq@&3vmZ%&@H{P?6&+U!yq VvuXWlo?M_c44$rjF6*2UngF4cP+$N6 literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_stb_image/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_stb_image/corpus/not_kitty_icc.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c7804d99829cc6307c1c6ae9915cf42d555414 GIT binary patch literal 427 zcmV;c0aX5pP)9xSWu9|B*4Isn^#g47m^r~thH)GiR<@yX0fO)OF<2Kt#qCldyUF#H?{4jV?XGw9)psxE&K1B1m^ z1_tH{2(hG@3=G>_85ksPA;eS`Ffj19FfeR8pIlm01~rBeWCZ{dbvfq;rA3DT000kA zOjJc?%*_A){{R30GnreSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM003J_L_t(I%iWVf3V=Wi12fJ3|IHp$*hSlV@t||fKp?cDK@bHXV&o_g zF_hw;3ILUGteXmeJsVfSmcVJno)^MdQwU3bFHCtNG)uY>mLcD%`0UBaIq~Fq8#dBr V12uok3~c}a002ovPDHLkV1nKBo!S5Z literal 0 HcmV?d00001 diff --git a/fuzzers/libfuzzer_stb_image/harness.c b/fuzzers/libfuzzer_stb_image/harness.c new file mode 100644 index 0000000000..af93f52493 --- /dev/null +++ b/fuzzers/libfuzzer_stb_image/harness.c @@ -0,0 +1,61 @@ +#include + +#define STBI_ASSERT(x) +#define STBI_NO_SIMD +#define STBI_NO_LINEAR +#define STBI_NO_STDIO +#define STB_IMAGE_IMPLEMENTATION + +#include "stb_image.h" + +int target_func(const uint8_t *buf, size_t size) { + + /*printf("BUF (%ld): ", size); + for (int i = 0; i < size; i++) { + printf("%02X", buf[i]); + } + printf("\n");*/ + + if (size == 0) return 0; + + switch (buf[0]) { + + case 1: + if (buf[1] == 0x44) { + //__builtin_trap(); + return 8; + } + + break; + case 0xff: + if (buf[2] == 0xff) { + if (buf[1] == 0x44) { + //*(char *)(0xdeadbeef) = 1; + return 9; + } + } + + break; + default: + break; + + } + + return 1; + +} +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{return target_func(data, size); + int x, y, channels; + + if(!stbi_info_from_memory(data, size, &x, &y, &channels)) return 0; + + /* exit if the image is larger than ~80MB */ + if(y && x > (80000000 / 4) / y) return 0; + + unsigned char *img = stbi_load_from_memory(data, size, &x, &y, &channels, 4); + + free(img); + + return 0; +} diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs new file mode 100644 index 0000000000..cb858e88fe --- /dev/null +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -0,0 +1,150 @@ +//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts +//! The example harness is built for stb_image. + +use std::{env, path::PathBuf}; + +use libafl::{ + bolts::{shmem::StdShMem, tuples::tuple_list}, + corpus::{ + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + QueueCorpusScheduler, + }, + events::setup_restarting_mgr, + executors::{inprocess::InProcessExecutor, ExitKind}, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::token_mutations::Tokens, + observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, HasMetadata, State}, + stats::SimpleStats, + utils::{current_nanos, StdRand}, + Error, +}; + +use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM}; + +/// The main fn, no_mangle as it is a C main +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + fuzz( + vec![PathBuf::from("./corpus")], + PathBuf::from("./crashes"), + 1337, + ) + .expect("An error occurred while fuzzing"); +} + +/// The actual fuzzer +fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { + // 'While the stats are state, they are usually used in the broker - which is likely never restarted + let stats = SimpleStats::new(|s| println!("{}", s)); + + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + let (state, mut restarting_mgr) = + match setup_restarting_mgr::<_, _, StdShMem, _>(stats, broker_port) { + Ok(res) => res, + Err(err) => match err { + Error::ShuttingDown => { + return Ok(()); + } + _ => { + panic!("Failed to setup the restarter: {}", err); + } + }, + }; + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(unsafe { + StdMapObserver::new("edges", &mut EDGES_MAP, MAX_EDGES_NUM) + }); + + // If not restarting, create a State from scratch + let mut state = state.unwrap_or_else(|| { + State::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Feedbacks to rate the interestingness of an input + tuple_list!( + MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), + TimeFeedback::new() + ), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(objective_dir).unwrap(), + // Feedbacks to recognize an input as solution + tuple_list!(CrashFeedback::new()), + ) + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().is_none() { + state.add_metadata(Tokens::new(vec![ + vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header + "IHDR".as_bytes().to_vec(), + "IDAT".as_bytes().to_vec(), + "PLTE".as_bytes().to_vec(), + "IEND".as_bytes().to_vec(), + ])); + } + + // Setup a basic mutator with a mutational stage + let mutator = StdScheduledMutator::new(havoc_mutations()); + let stage = StdMutationalStage::new(mutator); + + // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); + + // A minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + libfuzzer_test_one_input(buf); + ExitKind::Ok + }; + + // Create the executor for an in-process function with just one observer for edge coverage + let mut executor = InProcessExecutor::new( + "in-process(edges,time)", + &mut harness, + tuple_list!(edges_observer, TimeObserver::new("time")), + &mut state, + &mut restarting_mgr, + )?; + + // The actual target run starts here. + // Call LLVMFUzzerInitialize() if present. + let args: Vec = env::args().collect(); + if libfuzzer_initialize(&args) == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") + } + + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + state + .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) + .expect(&format!( + "Failed to load initial corpus at {:?}", + &corpus_dirs + )); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; + + // Never reached + Ok(()) +} diff --git a/fuzzers/libfuzzer_stb_image/stb_image.h b/fuzzers/libfuzzer_stb_image/stb_image.h new file mode 100644 index 0000000000..6542ede682 --- /dev/null +++ b/fuzzers/libfuzzer_stb_image/stb_image.h @@ -0,0 +1,7762 @@ +/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko [reserved] + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); + if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/fuzzers/libfuzzer_windows/harness.cc b/fuzzers/libfuzzer_windows/harness.cc index 2c256bcc71..65365fdc8c 100644 --- a/fuzzers/libfuzzer_windows/harness.cc +++ b/fuzzers/libfuzzer_windows/harness.cc @@ -219,13 +219,13 @@ int load_stbi(const uint8_t *data, int size) extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - //return target_func(Data, Size); + return target_func(Data, Size); if(Size > 0x4000) return 0; int size = Size; const unsigned char * data = Data; - //return load_stbi(data, size); - return parse_pe(data, size); + return load_stbi(data, size); + //return parse_pe(data, size); } diff --git a/fuzzers/libfuzzer_windows/src/fuzzer.rs b/fuzzers/libfuzzer_windows/src/fuzzer.rs index be97929037..0366b8486e 100644 --- a/fuzzers/libfuzzer_windows/src/fuzzer.rs +++ b/fuzzers/libfuzzer_windows/src/fuzzer.rs @@ -14,8 +14,9 @@ use libafl::{ events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, - fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + fuzzer::{Fuzzer, StdFuzzer}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, state::{HasCorpus, HasMetadata, State}, @@ -133,12 +134,12 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> } // Setup a basic mutator with a mutational stage - let mutator = HavocBytesMutator::default(); + let mutator = StdScheduledMutator::new(havoc_mutations()); let stage = StdMutationalStage::new(mutator); // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( @@ -160,12 +161,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs( - &mut executor, - &mut restarting_mgr, - fuzzer.scheduler(), - &corpus_dirs, - ) + .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) .expect(&format!( "Failed to load initial corpus at {:?}", &corpus_dirs @@ -173,7 +169,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; // Never reached Ok(()) diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index efec6f17cf..cbf30ef790 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -6,8 +6,10 @@ edition = "2018" [features] default = [] -sancov = [] -libfuzzer_compatibility = [] +pcguard_edges = [] +pcguard_hitcounts = [] +libfuzzer = [] +pcguard = ["pcguard_hitcounts"] [build-dependencies] cc = { version = "1.0", features = ["parallel"] } diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 5c2a27f6f3..a8d3dbce4e 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -7,13 +7,16 @@ fn main() { let out_dir = out_dir.to_string_lossy().to_string(); //let out_dir_path = Path::new(&out_dir); - #[cfg(feature = "libfuzzer_compatibility")] + std::env::set_var("CC", "clang"); + std::env::set_var("CXX", "clang++"); + + #[cfg(feature = "libfuzzer")] { println!("cargo:rerun-if-changed=libfuzzer_compatibility.c"); cc::Build::new() .file("libfuzzer_compatibility.c") - .compile("libfuzzer-compatibility"); + .compile("libfuzzer_compatibility"); } println!("cargo:rustc-link-search=native={}", &out_dir); diff --git a/libafl_targets/libfuzzer_compatibility.c b/libafl_targets/libfuzzer_compatibility.c index 8136c7237f..77a61e9df4 100644 --- a/libafl_targets/libfuzzer_compatibility.c +++ b/libafl_targets/libfuzzer_compatibility.c @@ -13,6 +13,8 @@ #define LIBFUZZER_MSVC 0 #endif // _MSC_VER +#define EXPORT_FN __declspec(dllexport) + // From Libfuzzer // Intermediate macro to ensure the parameter is expanded before stringified. #define STRINGIFY_(A) #A @@ -32,7 +34,7 @@ __pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) -#define CHECK_WEAK_FN(Name) (Name != &Name##Def) +#define CHECK_WEAK_FN(Name) ((void*)Name != (void*)&Name##Def) #else // Declare external functions as weak to allow them to default to a specified // function if not defined explicitly. We must use weak symbols because clang's @@ -49,6 +51,8 @@ EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG #else +#define EXPORT_FN + // Declare these symbols as weak to allow them to be optionally defined. #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG @@ -68,11 +72,11 @@ EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, #undef EXT_FUNC -int libafl_targets_has_libfuzzer_init() { +EXPORT_FN int libafl_targets_has_libfuzzer_init() { return CHECK_WEAK_FN(LLVMFuzzerInitialize); } -int libafl_targets_libfuzzer_init(int *argc, char ***argv) { +EXPORT_FN int libafl_targets_libfuzzer_init(int *argc, char ***argv) { if (libafl_targets_has_libfuzzer_init()) { return LLVMFuzzerInitialize(argc, argv); } else { diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 6f4232f69c..d1ccae18c4 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -1,17 +1,9 @@ -#[cfg(feature = "sancov")] -pub mod sancov; -#[cfg(feature = "sancov")] -pub use sancov::*; +#[cfg(any(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] +pub mod pcguard; +#[cfg(any(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] +pub use pcguard::*; -#[cfg(feature = "libfuzzer_compatibility")] -pub mod libfuzzer_compatibility; -#[cfg(feature = "libfuzzer_compatibility")] -pub use libfuzzer_compatibility::*; - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +#[cfg(feature = "libfuzzer")] +pub mod libfuzzer; +#[cfg(feature = "libfuzzer")] +pub use libfuzzer::*; diff --git a/libafl_targets/src/libfuzzer_compatibility.rs b/libafl_targets/src/libfuzzer.rs similarity index 100% rename from libafl_targets/src/libfuzzer_compatibility.rs rename to libafl_targets/src/libfuzzer.rs diff --git a/libafl_targets/src/sancov.rs b/libafl_targets/src/pcguard.rs similarity index 54% rename from libafl_targets/src/sancov.rs rename to libafl_targets/src/pcguard.rs index 6b99a15124..a90b3a659d 100644 --- a/libafl_targets/src/sancov.rs +++ b/libafl_targets/src/pcguard.rs @@ -1,15 +1,27 @@ +#[cfg(all(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] +compile_error!( + "the libafl_targets `pcguard_edges` and `pcguard_hitcounts` features are mutually exclusive." +); + // TODO compile time flag pub const MAP_SIZE: usize = 65536; pub static mut EDGES_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; -pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +//pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; pub static mut MAX_EDGES_NUM: usize = 0; #[no_mangle] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { let pos = *guard as usize; - let val = (EDGES_MAP[pos] as u8).wrapping_add(1); - EDGES_MAP[pos] = val; + #[cfg(feature = "pcguard_edges")] + { + *EDGES_MAP.get_unchecked_mut(pos) = 1; + } + #[cfg(feature = "pcguard_hitcounts")] + { + let val = (*EDGES_MAP.get_unchecked(pos) as u8).wrapping_add(1); + *EDGES_MAP.get_unchecked_mut(pos) = val; + } } #[no_mangle] From 9c1f836ff25df3bbfc36824c13283d687179c630 Mon Sep 17 00:00:00 2001 From: andreafioraldi Date: Fri, 26 Mar 2021 10:55:23 +0100 Subject: [PATCH 027/104] fix libfuzzer_stb_image --- fuzzers/libfuzzer_stb_image/harness.c | 38 +------------------------ fuzzers/libfuzzer_stb_image/src/main.rs | 8 +++--- 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/fuzzers/libfuzzer_stb_image/harness.c b/fuzzers/libfuzzer_stb_image/harness.c index af93f52493..6e4f5f9ae7 100644 --- a/fuzzers/libfuzzer_stb_image/harness.c +++ b/fuzzers/libfuzzer_stb_image/harness.c @@ -8,44 +8,8 @@ #include "stb_image.h" -int target_func(const uint8_t *buf, size_t size) { - - /*printf("BUF (%ld): ", size); - for (int i = 0; i < size; i++) { - printf("%02X", buf[i]); - } - printf("\n");*/ - - if (size == 0) return 0; - - switch (buf[0]) { - - case 1: - if (buf[1] == 0x44) { - //__builtin_trap(); - return 8; - } - - break; - case 0xff: - if (buf[2] == 0xff) { - if (buf[1] == 0x44) { - //*(char *)(0xdeadbeef) = 1; - return 9; - } - } - - break; - default: - break; - - } - - return 1; - -} int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) -{return target_func(data, size); +{ int x, y, channels; if(!stbi_info_from_memory(data, size, &x, &y, &channels)) return 0; diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index cb858e88fe..abc8492c83 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -15,7 +15,7 @@ use libafl::{ fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, - observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, + observers::{StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, state::{HasCorpus, HasMetadata, State}, stats::SimpleStats, @@ -63,9 +63,9 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> }; // Create an observation channel using the coverage map - let edges_observer = HitcountsMapObserver::new(unsafe { - StdMapObserver::new("edges", &mut EDGES_MAP, MAX_EDGES_NUM) - }); + // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) + let edges_observer = + StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM }); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { From 2cd046e1e8cfe2b38266d997a2aa606a0bb4ab5e Mon Sep 17 00:00:00 2001 From: andreafioraldi Date: Fri, 26 Mar 2021 11:42:26 +0100 Subject: [PATCH 028/104] catch assertion errors on win32 --- fuzzers/libfuzzer_stb_image/harness.c | 3 + libafl/src/bolts/os/windows_exceptions.rs | 704 ++++++++++++---------- 2 files changed, 373 insertions(+), 334 deletions(-) diff --git a/fuzzers/libfuzzer_stb_image/harness.c b/fuzzers/libfuzzer_stb_image/harness.c index 6e4f5f9ae7..be70ac5d98 100644 --- a/fuzzers/libfuzzer_stb_image/harness.c +++ b/fuzzers/libfuzzer_stb_image/harness.c @@ -1,4 +1,5 @@ #include +#include #define STBI_ASSERT(x) #define STBI_NO_SIMD @@ -20,6 +21,8 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) unsigned char *img = stbi_load_from_memory(data, size, &x, &y, &channels, 4); free(img); + + // if (x > 10000) free(img); // free crash return 0; } diff --git a/libafl/src/bolts/os/windows_exceptions.rs b/libafl/src/bolts/os/windows_exceptions.rs index b960270b3a..838d0d21cb 100644 --- a/libafl/src/bolts/os/windows_exceptions.rs +++ b/libafl/src/bolts/os/windows_exceptions.rs @@ -1,334 +1,370 @@ -pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS; - -use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error}; - -use alloc::vec::Vec; -use core::{ - cell::UnsafeCell, - convert::TryFrom, - fmt::{self, Display, Formatter}, - ptr::write_volatile, - sync::atomic::{compiler_fence, Ordering}, -}; -use std::os::raw::{c_long, c_void}; - -use num_enum::{IntoPrimitive, TryFromPrimitive}; - -//const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; -//const EXCEPTION_CONTINUE_SEARCH: c_long = 0; -const EXCEPTION_EXECUTE_HANDLER: c_long = 1; - -// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611 -pub const STATUS_WAIT_0: u32 = 0x00000000; -pub const STATUS_ABANDONED_WAIT_0: u32 = 0x00000080; -pub const STATUS_USER_APC: u32 = 0x000000C0; -pub const STATUS_TIMEOUT: u32 = 0x00000102; -pub const STATUS_PENDING: u32 = 0x00000103; -pub const STATUS_SEGMENT_NOTIFICATION: u32 = 0x40000005; -pub const STATUS_FATAL_APP_EXIT: u32 = 0x40000015; -pub const STATUS_GUARD_PAGE_VIOLATION: u32 = 0x80000001; -pub const STATUS_DATATYPE_MISALIGNMENT: u32 = 0x80000002; -pub const STATUS_BREAKPOINT: u32 = 0x80000003; -pub const STATUS_SINGLE_STEP: u32 = 0x80000004; -pub const STATUS_LONGJUMP: u32 = 0x80000026; -pub const STATUS_UNWIND_CONSOLIDATE: u32 = 0x80000029; -pub const STATUS_ACCESS_VIOLATION: u32 = 0xC0000005; -pub const STATUS_IN_PAGE_ERROR: u32 = 0xC0000006; -pub const STATUS_INVALID_HANDLE: u32 = 0xC0000008; -pub const STATUS_NO_MEMORY: u32 = 0xC0000017; -pub const STATUS_ILLEGAL_INSTRUCTION: u32 = 0xC000001D; -pub const STATUS_NONCONTINUABLE_EXCEPTION: u32 = 0xC0000025; -pub const STATUS_INVALID_DISPOSITION: u32 = 0xC0000026; -pub const STATUS_ARRAY_BOUNDS_EXCEEDED: u32 = 0xC000008C; -pub const STATUS_FLOAT_DENORMAL_OPERAND: u32 = 0xC000008D; -pub const STATUS_FLOAT_DIVIDE_BY_ZERO: u32 = 0xC000008E; -pub const STATUS_FLOAT_INEXACT_RESULT: u32 = 0xC000008F; -pub const STATUS_FLOAT_INVALID_OPERATION: u32 = 0xC0000090; -pub const STATUS_FLOAT_OVERFLOW: u32 = 0xC0000091; -pub const STATUS_FLOAT_STACK_CHECK: u32 = 0xC0000092; -pub const STATUS_FLOAT_UNDERFLOW: u32 = 0xC0000093; -pub const STATUS_INTEGER_DIVIDE_BY_ZERO: u32 = 0xC0000094; -pub const STATUS_INTEGER_OVERFLOW: u32 = 0xC0000095; -pub const STATUS_PRIVILEGED_INSTRUCTION: u32 = 0xC0000096; -pub const STATUS_STACK_OVERFLOW: u32 = 0xC00000FD; -pub const STATUS_DLL_NOT_FOUND: u32 = 0xC0000135; -pub const STATUS_ORDINAL_NOT_FOUND: u32 = 0xC0000138; -pub const STATUS_ENTRYPOINT_NOT_FOUND: u32 = 0xC0000139; -pub const STATUS_CONTROL_C_EXIT: u32 = 0xC000013A; -pub const STATUS_DLL_INIT_FAILED: u32 = 0xC0000142; -pub const STATUS_FLOAT_MULTIPLE_FAULTS: u32 = 0xC00002B4; -pub const STATUS_FLOAT_MULTIPLE_TRAPS: u32 = 0xC00002B5; -pub const STATUS_REG_NAT_CONSUMPTION: u32 = 0xC00002C9; -pub const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374; -pub const STATUS_STACK_BUFFER_OVERRUN: u32 = 0xC0000409; -pub const STATUS_INVALID_CRUNTIME_PARAMETER: u32 = 0xC0000417; -pub const STATUS_ASSERTION_FAILURE: u32 = 0xC0000420; -pub const STATUS_SXS_EARLY_DEACTIVATION: u32 = 0xC015000F; -pub const STATUS_SXS_INVALID_DEACTIVATION: u32 = 0xC0150010; - -#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)] -#[repr(u32)] -pub enum ExceptionCode { - // From https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode - AccessViolation = STATUS_ACCESS_VIOLATION, - ArrayBoundsExceeded = STATUS_ARRAY_BOUNDS_EXCEEDED, - Breakpoint = STATUS_BREAKPOINT, - DatatypeMisalignment = STATUS_DATATYPE_MISALIGNMENT, - FltDenormalOperand = STATUS_FLOAT_DENORMAL_OPERAND, - FltDivideByZero = STATUS_FLOAT_DIVIDE_BY_ZERO, - FltInexactResult = STATUS_FLOAT_INEXACT_RESULT, - FltInvalidOperation = STATUS_FLOAT_INVALID_OPERATION, - FltOverflow = STATUS_FLOAT_OVERFLOW, - FltStackCheck = STATUS_FLOAT_STACK_CHECK, - FltUnderflow = STATUS_FLOAT_UNDERFLOW, - GuardPageViolation = STATUS_GUARD_PAGE_VIOLATION, - IllegalInstruction = STATUS_ILLEGAL_INSTRUCTION, - InPageError = STATUS_IN_PAGE_ERROR, - IntegerDivideByZero = STATUS_INTEGER_DIVIDE_BY_ZERO, - IntegerOverflow = STATUS_INTEGER_OVERFLOW, - InvalidDisposition = STATUS_INVALID_DISPOSITION, - InvalidHandle = STATUS_INVALID_HANDLE, - NoncontinuableException = STATUS_NONCONTINUABLE_EXCEPTION, - PrivilegedInstruction = STATUS_PRIVILEGED_INSTRUCTION, - SingleStep = STATUS_SINGLE_STEP, - StackOverflow = STATUS_STACK_OVERFLOW, - UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE, - // Addition exceptions - Wait0 = STATUS_WAIT_0, - AbandonedWait0 = STATUS_ABANDONED_WAIT_0, - UserAPC = STATUS_USER_APC, - Timeout = STATUS_TIMEOUT, - Pending = STATUS_PENDING, - SegmentNotification = STATUS_SEGMENT_NOTIFICATION, - FatalAppExit = STATUS_FATAL_APP_EXIT, - Longjump = STATUS_LONGJUMP, - DLLNotFound = STATUS_DLL_NOT_FOUND, - OrdinalNotFound = STATUS_ORDINAL_NOT_FOUND, - EntryPointNotFound = STATUS_ENTRYPOINT_NOT_FOUND, - ControlCExit = STATUS_CONTROL_C_EXIT, - DllInitFailed = STATUS_DLL_INIT_FAILED, - FltMultipleFaults = STATUS_FLOAT_MULTIPLE_FAULTS, - FltMultipleTraps = STATUS_FLOAT_MULTIPLE_TRAPS, - RegNatConsumption = STATUS_REG_NAT_CONSUMPTION, - HeapCorruption = STATUS_HEAP_CORRUPTION, - StackBufferOverrun = STATUS_STACK_BUFFER_OVERRUN, - InvalidCRuntimeParameter = STATUS_INVALID_CRUNTIME_PARAMETER, - AssertionFailure = STATUS_ASSERTION_FAILURE, - SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION, - SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION, -} - -pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[ - ExceptionCode::AccessViolation, - ExceptionCode::ArrayBoundsExceeded, - ExceptionCode::FltDivideByZero, - ExceptionCode::GuardPageViolation, - ExceptionCode::IllegalInstruction, - ExceptionCode::InPageError, - ExceptionCode::IntegerDivideByZero, - ExceptionCode::InvalidHandle, - ExceptionCode::NoncontinuableException, - ExceptionCode::PrivilegedInstruction, - ExceptionCode::StackOverflow, - ExceptionCode::HeapCorruption, - ExceptionCode::StackBufferOverrun, - ExceptionCode::AssertionFailure, -]; - -impl PartialEq for ExceptionCode { - fn eq(&self, other: &Self) -> bool { - *self as u32 == *other as u32 - } -} - -impl Eq for ExceptionCode {} - -unsafe impl Sync for ExceptionCode {} - -impl Display for ExceptionCode { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - match self { - ExceptionCode::AccessViolation => write!(f, "STATUS_ACCESS_VIOLATION")?, - ExceptionCode::ArrayBoundsExceeded => write!(f, "STATUS_ARRAY_BOUNDS_EXCEEDED")?, - ExceptionCode::Breakpoint => write!(f, "STATUS_BREAKPOINT")?, - ExceptionCode::DatatypeMisalignment => write!(f, "STATUS_DATATYPE_MISALIGNMENT")?, - ExceptionCode::FltDenormalOperand => write!(f, "STATUS_FLOAT_DENORMAL_OPERAND")?, - ExceptionCode::FltDivideByZero => write!(f, "STATUS_FLOAT_DIVIDE_BY_ZERO")?, - ExceptionCode::FltInexactResult => write!(f, "STATUS_FLOAT_INEXACT_RESULT")?, - ExceptionCode::FltInvalidOperation => write!(f, "STATUS_FLOAT_INVALID_OPERATION")?, - ExceptionCode::FltOverflow => write!(f, "STATUS_FLOAT_OVERFLOW")?, - ExceptionCode::FltStackCheck => write!(f, "STATUS_FLOAT_STACK_CHECK")?, - ExceptionCode::FltUnderflow => write!(f, "STATUS_FLOAT_UNDERFLOW")?, - ExceptionCode::GuardPageViolation => write!(f, "STATUS_GUARD_PAGE_VIOLATION")?, - ExceptionCode::IllegalInstruction => write!(f, "STATUS_ILLEGAL_INSTRUCTION")?, - ExceptionCode::InPageError => write!(f, "STATUS_IN_PAGE_ERROR")?, - ExceptionCode::IntegerDivideByZero => write!(f, "STATUS_INTEGER_DIVIDE_BY_ZERO")?, - ExceptionCode::IntegerOverflow => write!(f, "STATUS_INTEGER_OVERFLOW")?, - ExceptionCode::InvalidDisposition => write!(f, "STATUS_INVALID_DISPOSITION")?, - ExceptionCode::InvalidHandle => write!(f, "STATUS_INVALID_HANDLE")?, - ExceptionCode::NoncontinuableException => write!(f, "STATUS_NONCONTINUABLE_EXCEPTION")?, - ExceptionCode::PrivilegedInstruction => write!(f, "STATUS_PRIVILEGED_INSTRUCTION")?, - ExceptionCode::SingleStep => write!(f, "STATUS_SINGLE_STEP")?, - ExceptionCode::StackOverflow => write!(f, "STATUS_STACK_OVERFLOW")?, - ExceptionCode::UnwindConsolidate => write!(f, "STATUS_UNWIND_CONSOLIDATE")?, - ExceptionCode::Wait0 => write!(f, "STATUS_WAIT_0")?, - ExceptionCode::AbandonedWait0 => write!(f, "STATUS_ABANDONED_WAIT_0")?, - ExceptionCode::UserAPC => write!(f, "STATUS_USER_APC")?, - ExceptionCode::Timeout => write!(f, "STATUS_TIMEOUT")?, - ExceptionCode::Pending => write!(f, "STATUS_PENDING")?, - ExceptionCode::SegmentNotification => write!(f, "STATUS_SEGMENT_NOTIFICATION")?, - ExceptionCode::FatalAppExit => write!(f, "STATUS_FATAL_APP_EXIT")?, - ExceptionCode::Longjump => write!(f, "STATUS_LONGJUMP")?, - ExceptionCode::DLLNotFound => write!(f, "STATUS_DLL_NOT_FOUND")?, - ExceptionCode::OrdinalNotFound => write!(f, "STATUS_ORDINAL_NOT_FOUND")?, - ExceptionCode::EntryPointNotFound => write!(f, "STATUS_ENTRYPOINT_NOT_FOUND")?, - ExceptionCode::ControlCExit => write!(f, "STATUS_CONTROL_C_EXIT")?, - ExceptionCode::DllInitFailed => write!(f, "STATUS_DLL_INIT_FAILED")?, - ExceptionCode::FltMultipleFaults => write!(f, "STATUS_FLOAT_MULTIPLE_FAULTS")?, - ExceptionCode::FltMultipleTraps => write!(f, "STATUS_FLOAT_MULTIPLE_TRAPS")?, - ExceptionCode::RegNatConsumption => write!(f, "STATUS_REG_NAT_CONSUMPTION")?, - ExceptionCode::HeapCorruption => write!(f, "STATUS_HEAP_CORRUPTION")?, - ExceptionCode::StackBufferOverrun => write!(f, "STATUS_STACK_BUFFER_OVERRUN")?, - ExceptionCode::InvalidCRuntimeParameter => { - write!(f, "STATUS_INVALID_CRUNTIME_PARAMETER")? - } - ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?, - ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?, - ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?, - }; - - Ok(()) - } -} - -pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 45] = [ - ExceptionCode::AccessViolation, - ExceptionCode::ArrayBoundsExceeded, - ExceptionCode::Breakpoint, - ExceptionCode::DatatypeMisalignment, - ExceptionCode::FltDenormalOperand, - ExceptionCode::FltDivideByZero, - ExceptionCode::FltInexactResult, - ExceptionCode::FltInvalidOperation, - ExceptionCode::FltOverflow, - ExceptionCode::FltStackCheck, - ExceptionCode::FltUnderflow, - ExceptionCode::GuardPageViolation, - ExceptionCode::IllegalInstruction, - ExceptionCode::InPageError, - ExceptionCode::IntegerDivideByZero, - ExceptionCode::IntegerOverflow, - ExceptionCode::InvalidDisposition, - ExceptionCode::InvalidHandle, - ExceptionCode::NoncontinuableException, - ExceptionCode::PrivilegedInstruction, - ExceptionCode::SingleStep, - ExceptionCode::StackOverflow, - ExceptionCode::UnwindConsolidate, - ExceptionCode::Wait0, - ExceptionCode::AbandonedWait0, - ExceptionCode::UserAPC, - ExceptionCode::Timeout, - ExceptionCode::Pending, - ExceptionCode::SegmentNotification, - ExceptionCode::FatalAppExit, - ExceptionCode::Longjump, - ExceptionCode::DLLNotFound, - ExceptionCode::OrdinalNotFound, - ExceptionCode::EntryPointNotFound, - ExceptionCode::ControlCExit, - ExceptionCode::DllInitFailed, - ExceptionCode::FltMultipleFaults, - ExceptionCode::FltMultipleTraps, - ExceptionCode::RegNatConsumption, - ExceptionCode::HeapCorruption, - ExceptionCode::StackBufferOverrun, - ExceptionCode::InvalidCRuntimeParameter, - ExceptionCode::AssertionFailure, - ExceptionCode::SXSEarlyDeactivation, - ExceptionCode::SXSInvalidDeactivation, -]; - -pub trait Handler { - /// Handle an exception - fn handle( - &mut self, - exception_code: ExceptionCode, - exception_pointers: *mut EXCEPTION_POINTERS, - ); - /// Return a list of exceptions to handle - fn exceptions(&self) -> Vec; -} - -struct HandlerHolder { - handler: UnsafeCell<*mut dyn Handler>, -} - -unsafe impl Send for HandlerHolder {} - -/// Keep track of which handler is registered for which exception -static mut EXCEPTION_HANDLERS: [Option; 64] = [ - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, -]; - -type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long; -static mut PREVIOUS_HANDLER: Option = None; - -/// Internal function that is being called whenever an exception arrives (stdcall). -unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long { - let code = exception_pointers - .as_mut() - .unwrap() - .exception_record - .as_mut() - .unwrap() - .exception_code; - let exception_code = ExceptionCode::try_from(code).unwrap(); - let index = EXCEPTION_CODES_MAPPING - .iter() - .position(|x| *x == exception_code) - .unwrap(); - let ret = match &EXCEPTION_HANDLERS[index] { - Some(handler_holder) => { - let handler = &mut **handler_holder.handler.get(); - handler.handle(exception_code, exception_pointers); - EXCEPTION_EXECUTE_HANDLER - } - None => EXCEPTION_EXECUTE_HANDLER, - }; - if let Some(prev_handler) = PREVIOUS_HANDLER { - prev_handler(exception_pointers) - } else { - ret - } -} - -/// Setup Win32 exception handlers in a somewhat rusty way. -/// # Safety -/// Exception handlers are usually ugly, handle with care! -pub unsafe fn setup_exception_handler(handler: &mut T) -> Result<(), Error> { - let exceptions = handler.exceptions(); - for exception_code in exceptions { - let index = EXCEPTION_CODES_MAPPING - .iter() - .position(|x| *x == exception_code) - .unwrap(); - write_volatile( - &mut EXCEPTION_HANDLERS[index], - Some(HandlerHolder { - handler: UnsafeCell::new(handler as *mut dyn Handler), - }), - ); - } - compiler_fence(Ordering::SeqCst); - - if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute( - handle_exception as *const c_void, - ))) { - PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void)); - } - Ok(()) -} +pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS; + +use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error}; + +use alloc::vec::Vec; +use core::{ + cell::UnsafeCell, + convert::TryFrom, + fmt::{self, Display, Formatter}, + ptr, + ptr::write_volatile, + sync::atomic::{compiler_fence, Ordering}, +}; +use std::os::raw::{c_long, c_void}; + +use num_enum::{IntoPrimitive, TryFromPrimitive}; + +//const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; +//const EXCEPTION_CONTINUE_SEARCH: c_long = 0; +const EXCEPTION_EXECUTE_HANDLER: c_long = 1; + +// From https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt/signal.h +pub const SIGINT: i32 = 2; +pub const SIGILL: i32 = 4; +pub const SIGABRT_COMPAT: i32 = 6; +pub const SIGFPE: i32 = 8; +pub const SIGSEGV: i32 = 11; +pub const SIGTERM: i32 = 15; +pub const SIGBREAK: i32 = 21; +pub const SIGABRT: i32 = 22; +pub const SIGABRT2: i32 = 22; + +// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611 +pub const STATUS_WAIT_0: u32 = 0x00000000; +pub const STATUS_ABANDONED_WAIT_0: u32 = 0x00000080; +pub const STATUS_USER_APC: u32 = 0x000000C0; +pub const STATUS_TIMEOUT: u32 = 0x00000102; +pub const STATUS_PENDING: u32 = 0x00000103; +pub const STATUS_SEGMENT_NOTIFICATION: u32 = 0x40000005; +pub const STATUS_FATAL_APP_EXIT: u32 = 0x40000015; +pub const STATUS_GUARD_PAGE_VIOLATION: u32 = 0x80000001; +pub const STATUS_DATATYPE_MISALIGNMENT: u32 = 0x80000002; +pub const STATUS_BREAKPOINT: u32 = 0x80000003; +pub const STATUS_SINGLE_STEP: u32 = 0x80000004; +pub const STATUS_LONGJUMP: u32 = 0x80000026; +pub const STATUS_UNWIND_CONSOLIDATE: u32 = 0x80000029; +pub const STATUS_ACCESS_VIOLATION: u32 = 0xC0000005; +pub const STATUS_IN_PAGE_ERROR: u32 = 0xC0000006; +pub const STATUS_INVALID_HANDLE: u32 = 0xC0000008; +pub const STATUS_NO_MEMORY: u32 = 0xC0000017; +pub const STATUS_ILLEGAL_INSTRUCTION: u32 = 0xC000001D; +pub const STATUS_NONCONTINUABLE_EXCEPTION: u32 = 0xC0000025; +pub const STATUS_INVALID_DISPOSITION: u32 = 0xC0000026; +pub const STATUS_ARRAY_BOUNDS_EXCEEDED: u32 = 0xC000008C; +pub const STATUS_FLOAT_DENORMAL_OPERAND: u32 = 0xC000008D; +pub const STATUS_FLOAT_DIVIDE_BY_ZERO: u32 = 0xC000008E; +pub const STATUS_FLOAT_INEXACT_RESULT: u32 = 0xC000008F; +pub const STATUS_FLOAT_INVALID_OPERATION: u32 = 0xC0000090; +pub const STATUS_FLOAT_OVERFLOW: u32 = 0xC0000091; +pub const STATUS_FLOAT_STACK_CHECK: u32 = 0xC0000092; +pub const STATUS_FLOAT_UNDERFLOW: u32 = 0xC0000093; +pub const STATUS_INTEGER_DIVIDE_BY_ZERO: u32 = 0xC0000094; +pub const STATUS_INTEGER_OVERFLOW: u32 = 0xC0000095; +pub const STATUS_PRIVILEGED_INSTRUCTION: u32 = 0xC0000096; +pub const STATUS_STACK_OVERFLOW: u32 = 0xC00000FD; +pub const STATUS_DLL_NOT_FOUND: u32 = 0xC0000135; +pub const STATUS_ORDINAL_NOT_FOUND: u32 = 0xC0000138; +pub const STATUS_ENTRYPOINT_NOT_FOUND: u32 = 0xC0000139; +pub const STATUS_CONTROL_C_EXIT: u32 = 0xC000013A; +pub const STATUS_DLL_INIT_FAILED: u32 = 0xC0000142; +pub const STATUS_FLOAT_MULTIPLE_FAULTS: u32 = 0xC00002B4; +pub const STATUS_FLOAT_MULTIPLE_TRAPS: u32 = 0xC00002B5; +pub const STATUS_REG_NAT_CONSUMPTION: u32 = 0xC00002C9; +pub const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374; +pub const STATUS_STACK_BUFFER_OVERRUN: u32 = 0xC0000409; +pub const STATUS_INVALID_CRUNTIME_PARAMETER: u32 = 0xC0000417; +pub const STATUS_ASSERTION_FAILURE: u32 = 0xC0000420; +pub const STATUS_SXS_EARLY_DEACTIVATION: u32 = 0xC015000F; +pub const STATUS_SXS_INVALID_DEACTIVATION: u32 = 0xC0150010; + +#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)] +#[repr(u32)] +pub enum ExceptionCode { + // From https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode + AccessViolation = STATUS_ACCESS_VIOLATION, + ArrayBoundsExceeded = STATUS_ARRAY_BOUNDS_EXCEEDED, + Breakpoint = STATUS_BREAKPOINT, + DatatypeMisalignment = STATUS_DATATYPE_MISALIGNMENT, + FltDenormalOperand = STATUS_FLOAT_DENORMAL_OPERAND, + FltDivideByZero = STATUS_FLOAT_DIVIDE_BY_ZERO, + FltInexactResult = STATUS_FLOAT_INEXACT_RESULT, + FltInvalidOperation = STATUS_FLOAT_INVALID_OPERATION, + FltOverflow = STATUS_FLOAT_OVERFLOW, + FltStackCheck = STATUS_FLOAT_STACK_CHECK, + FltUnderflow = STATUS_FLOAT_UNDERFLOW, + GuardPageViolation = STATUS_GUARD_PAGE_VIOLATION, + IllegalInstruction = STATUS_ILLEGAL_INSTRUCTION, + InPageError = STATUS_IN_PAGE_ERROR, + IntegerDivideByZero = STATUS_INTEGER_DIVIDE_BY_ZERO, + IntegerOverflow = STATUS_INTEGER_OVERFLOW, + InvalidDisposition = STATUS_INVALID_DISPOSITION, + InvalidHandle = STATUS_INVALID_HANDLE, + NoncontinuableException = STATUS_NONCONTINUABLE_EXCEPTION, + PrivilegedInstruction = STATUS_PRIVILEGED_INSTRUCTION, + SingleStep = STATUS_SINGLE_STEP, + StackOverflow = STATUS_STACK_OVERFLOW, + UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE, + // Addition exceptions + Wait0 = STATUS_WAIT_0, + AbandonedWait0 = STATUS_ABANDONED_WAIT_0, + UserAPC = STATUS_USER_APC, + Timeout = STATUS_TIMEOUT, + Pending = STATUS_PENDING, + SegmentNotification = STATUS_SEGMENT_NOTIFICATION, + FatalAppExit = STATUS_FATAL_APP_EXIT, + Longjump = STATUS_LONGJUMP, + DLLNotFound = STATUS_DLL_NOT_FOUND, + OrdinalNotFound = STATUS_ORDINAL_NOT_FOUND, + EntryPointNotFound = STATUS_ENTRYPOINT_NOT_FOUND, + ControlCExit = STATUS_CONTROL_C_EXIT, + DllInitFailed = STATUS_DLL_INIT_FAILED, + FltMultipleFaults = STATUS_FLOAT_MULTIPLE_FAULTS, + FltMultipleTraps = STATUS_FLOAT_MULTIPLE_TRAPS, + RegNatConsumption = STATUS_REG_NAT_CONSUMPTION, + HeapCorruption = STATUS_HEAP_CORRUPTION, + StackBufferOverrun = STATUS_STACK_BUFFER_OVERRUN, + InvalidCRuntimeParameter = STATUS_INVALID_CRUNTIME_PARAMETER, + AssertionFailure = STATUS_ASSERTION_FAILURE, + SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION, + SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION, +} + +pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[ + ExceptionCode::AccessViolation, + ExceptionCode::ArrayBoundsExceeded, + ExceptionCode::FltDivideByZero, + ExceptionCode::GuardPageViolation, + ExceptionCode::IllegalInstruction, + ExceptionCode::InPageError, + ExceptionCode::IntegerDivideByZero, + ExceptionCode::InvalidHandle, + ExceptionCode::NoncontinuableException, + ExceptionCode::PrivilegedInstruction, + ExceptionCode::StackOverflow, + ExceptionCode::HeapCorruption, + ExceptionCode::StackBufferOverrun, + ExceptionCode::AssertionFailure, +]; + +impl PartialEq for ExceptionCode { + fn eq(&self, other: &Self) -> bool { + *self as u32 == *other as u32 + } +} + +impl Eq for ExceptionCode {} + +unsafe impl Sync for ExceptionCode {} + +impl Display for ExceptionCode { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + match self { + ExceptionCode::AccessViolation => write!(f, "STATUS_ACCESS_VIOLATION")?, + ExceptionCode::ArrayBoundsExceeded => write!(f, "STATUS_ARRAY_BOUNDS_EXCEEDED")?, + ExceptionCode::Breakpoint => write!(f, "STATUS_BREAKPOINT")?, + ExceptionCode::DatatypeMisalignment => write!(f, "STATUS_DATATYPE_MISALIGNMENT")?, + ExceptionCode::FltDenormalOperand => write!(f, "STATUS_FLOAT_DENORMAL_OPERAND")?, + ExceptionCode::FltDivideByZero => write!(f, "STATUS_FLOAT_DIVIDE_BY_ZERO")?, + ExceptionCode::FltInexactResult => write!(f, "STATUS_FLOAT_INEXACT_RESULT")?, + ExceptionCode::FltInvalidOperation => write!(f, "STATUS_FLOAT_INVALID_OPERATION")?, + ExceptionCode::FltOverflow => write!(f, "STATUS_FLOAT_OVERFLOW")?, + ExceptionCode::FltStackCheck => write!(f, "STATUS_FLOAT_STACK_CHECK")?, + ExceptionCode::FltUnderflow => write!(f, "STATUS_FLOAT_UNDERFLOW")?, + ExceptionCode::GuardPageViolation => write!(f, "STATUS_GUARD_PAGE_VIOLATION")?, + ExceptionCode::IllegalInstruction => write!(f, "STATUS_ILLEGAL_INSTRUCTION")?, + ExceptionCode::InPageError => write!(f, "STATUS_IN_PAGE_ERROR")?, + ExceptionCode::IntegerDivideByZero => write!(f, "STATUS_INTEGER_DIVIDE_BY_ZERO")?, + ExceptionCode::IntegerOverflow => write!(f, "STATUS_INTEGER_OVERFLOW")?, + ExceptionCode::InvalidDisposition => write!(f, "STATUS_INVALID_DISPOSITION")?, + ExceptionCode::InvalidHandle => write!(f, "STATUS_INVALID_HANDLE")?, + ExceptionCode::NoncontinuableException => write!(f, "STATUS_NONCONTINUABLE_EXCEPTION")?, + ExceptionCode::PrivilegedInstruction => write!(f, "STATUS_PRIVILEGED_INSTRUCTION")?, + ExceptionCode::SingleStep => write!(f, "STATUS_SINGLE_STEP")?, + ExceptionCode::StackOverflow => write!(f, "STATUS_STACK_OVERFLOW")?, + ExceptionCode::UnwindConsolidate => write!(f, "STATUS_UNWIND_CONSOLIDATE")?, + ExceptionCode::Wait0 => write!(f, "STATUS_WAIT_0")?, + ExceptionCode::AbandonedWait0 => write!(f, "STATUS_ABANDONED_WAIT_0")?, + ExceptionCode::UserAPC => write!(f, "STATUS_USER_APC")?, + ExceptionCode::Timeout => write!(f, "STATUS_TIMEOUT")?, + ExceptionCode::Pending => write!(f, "STATUS_PENDING")?, + ExceptionCode::SegmentNotification => write!(f, "STATUS_SEGMENT_NOTIFICATION")?, + ExceptionCode::FatalAppExit => write!(f, "STATUS_FATAL_APP_EXIT")?, + ExceptionCode::Longjump => write!(f, "STATUS_LONGJUMP")?, + ExceptionCode::DLLNotFound => write!(f, "STATUS_DLL_NOT_FOUND")?, + ExceptionCode::OrdinalNotFound => write!(f, "STATUS_ORDINAL_NOT_FOUND")?, + ExceptionCode::EntryPointNotFound => write!(f, "STATUS_ENTRYPOINT_NOT_FOUND")?, + ExceptionCode::ControlCExit => write!(f, "STATUS_CONTROL_C_EXIT")?, + ExceptionCode::DllInitFailed => write!(f, "STATUS_DLL_INIT_FAILED")?, + ExceptionCode::FltMultipleFaults => write!(f, "STATUS_FLOAT_MULTIPLE_FAULTS")?, + ExceptionCode::FltMultipleTraps => write!(f, "STATUS_FLOAT_MULTIPLE_TRAPS")?, + ExceptionCode::RegNatConsumption => write!(f, "STATUS_REG_NAT_CONSUMPTION")?, + ExceptionCode::HeapCorruption => write!(f, "STATUS_HEAP_CORRUPTION")?, + ExceptionCode::StackBufferOverrun => write!(f, "STATUS_STACK_BUFFER_OVERRUN")?, + ExceptionCode::InvalidCRuntimeParameter => { + write!(f, "STATUS_INVALID_CRUNTIME_PARAMETER")? + } + ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?, + ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?, + ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?, + }; + + Ok(()) + } +} + +pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 45] = [ + ExceptionCode::AccessViolation, + ExceptionCode::ArrayBoundsExceeded, + ExceptionCode::Breakpoint, + ExceptionCode::DatatypeMisalignment, + ExceptionCode::FltDenormalOperand, + ExceptionCode::FltDivideByZero, + ExceptionCode::FltInexactResult, + ExceptionCode::FltInvalidOperation, + ExceptionCode::FltOverflow, + ExceptionCode::FltStackCheck, + ExceptionCode::FltUnderflow, + ExceptionCode::GuardPageViolation, + ExceptionCode::IllegalInstruction, + ExceptionCode::InPageError, + ExceptionCode::IntegerDivideByZero, + ExceptionCode::IntegerOverflow, + ExceptionCode::InvalidDisposition, + ExceptionCode::InvalidHandle, + ExceptionCode::NoncontinuableException, + ExceptionCode::PrivilegedInstruction, + ExceptionCode::SingleStep, + ExceptionCode::StackOverflow, + ExceptionCode::UnwindConsolidate, + ExceptionCode::Wait0, + ExceptionCode::AbandonedWait0, + ExceptionCode::UserAPC, + ExceptionCode::Timeout, + ExceptionCode::Pending, + ExceptionCode::SegmentNotification, + ExceptionCode::FatalAppExit, + ExceptionCode::Longjump, + ExceptionCode::DLLNotFound, + ExceptionCode::OrdinalNotFound, + ExceptionCode::EntryPointNotFound, + ExceptionCode::ControlCExit, + ExceptionCode::DllInitFailed, + ExceptionCode::FltMultipleFaults, + ExceptionCode::FltMultipleTraps, + ExceptionCode::RegNatConsumption, + ExceptionCode::HeapCorruption, + ExceptionCode::StackBufferOverrun, + ExceptionCode::InvalidCRuntimeParameter, + ExceptionCode::AssertionFailure, + ExceptionCode::SXSEarlyDeactivation, + ExceptionCode::SXSInvalidDeactivation, +]; + +pub trait Handler { + /// Handle an exception + fn handle( + &mut self, + exception_code: ExceptionCode, + exception_pointers: *mut EXCEPTION_POINTERS, + ); + /// Return a list of exceptions to handle + fn exceptions(&self) -> Vec; +} + +struct HandlerHolder { + handler: UnsafeCell<*mut dyn Handler>, +} + +unsafe impl Send for HandlerHolder {} + +/// Keep track of which handler is registered for which exception +static mut EXCEPTION_HANDLERS: [Option; 64] = [ + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, +]; + +unsafe fn internal_handle_exception( + exception_code: ExceptionCode, + exception_pointers: *mut EXCEPTION_POINTERS, +) -> i32 { + let index = EXCEPTION_CODES_MAPPING + .iter() + .position(|x| *x == exception_code) + .unwrap(); + match &EXCEPTION_HANDLERS[index] { + Some(handler_holder) => { + let handler = &mut **handler_holder.handler.get(); + handler.handle(exception_code, exception_pointers); + EXCEPTION_EXECUTE_HANDLER + } + None => EXCEPTION_EXECUTE_HANDLER, + } +} + +type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long; +static mut PREVIOUS_HANDLER: Option = None; + +/// Internal function that is being called whenever an exception arrives (stdcall). +unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long { + let code = exception_pointers + .as_mut() + .unwrap() + .exception_record + .as_mut() + .unwrap() + .exception_code; + let exception_code = ExceptionCode::try_from(code).unwrap(); + // println!("Received {}", exception_code); + let ret = internal_handle_exception(exception_code, exception_pointers); + if let Some(prev_handler) = PREVIOUS_HANDLER { + prev_handler(exception_pointers) + } else { + ret + } +} + +type NativeSignalHandlerType = unsafe extern "C" fn(i32); +extern "C" { + fn signal(signum: i32, func: NativeSignalHandlerType) -> *const c_void; +} + +unsafe extern "C" fn handle_signal(_signum: i32) { + // println!("Received signal {}", _signum); + internal_handle_exception(ExceptionCode::AssertionFailure, ptr::null_mut()); +} + +/// Setup Win32 exception handlers in a somewhat rusty way. +/// # Safety +/// Exception handlers are usually ugly, handle with care! +pub unsafe fn setup_exception_handler(handler: &mut T) -> Result<(), Error> { + let exceptions = handler.exceptions(); + let mut catch_assertions = false; + for exception_code in exceptions { + if exception_code == ExceptionCode::AssertionFailure { + catch_assertions = true; + } + let index = EXCEPTION_CODES_MAPPING + .iter() + .position(|x| *x == exception_code) + .unwrap(); + write_volatile( + &mut EXCEPTION_HANDLERS[index], + Some(HandlerHolder { + handler: UnsafeCell::new(handler as *mut dyn Handler), + }), + ); + } + compiler_fence(Ordering::SeqCst); + if catch_assertions { + signal(SIGABRT, handle_signal); + } + if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute( + handle_exception as *const c_void, + ))) { + PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void)); + } + Ok(()) +} From 3b625981fa079b70337b79a6d85fe196bd2cc033 Mon Sep 17 00:00:00 2001 From: andreafioraldi Date: Fri, 26 Mar 2021 11:46:50 +0100 Subject: [PATCH 029/104] do not enforce clang in libafl_targets --- libafl_targets/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index a8d3dbce4e..fb5a37f045 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -7,8 +7,8 @@ fn main() { let out_dir = out_dir.to_string_lossy().to_string(); //let out_dir_path = Path::new(&out_dir); - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); + //std::env::set_var("CC", "clang"); + //std::env::set_var("CXX", "clang++"); #[cfg(feature = "libfuzzer")] { From 1f7be63031c12dbffde88ec3b13a736364fa57fd Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 26 Mar 2021 17:53:47 +0100 Subject: [PATCH 030/104] value profile in libafl_targets --- Cargo.toml | 5 +- fuzzers/frida_libpng/Cargo.toml | 20 +- fuzzers/libfuzzer_libpng_old/.gitignore | 1 - fuzzers/libfuzzer_libpng_old/Cargo.toml | 31 --- fuzzers/libfuzzer_libpng_old/README.md | 25 --- fuzzers/libfuzzer_libpng_old/build.rs | 113 ---------- .../libfuzzer_libpng_old/corpus/not_kitty.png | Bin 218 -> 0 bytes .../corpus/not_kitty_alpha.png | Bin 376 -> 0 bytes .../corpus/not_kitty_gamma.png | Bin 228 -> 0 bytes .../corpus/not_kitty_icc.png | Bin 427 -> 0 bytes fuzzers/libfuzzer_libpng_old/harness.cc | 197 ------------------ fuzzers/libfuzzer_libpng_old/src/fuzzer.rs | 174 ---------------- fuzzers/libfuzzer_libpng_old/test.sh | 18 -- fuzzers/libfuzzer_stb_image/Cargo.toml | 4 +- fuzzers/libfuzzer_stb_image/build.rs | 2 +- libafl_targets/Cargo.toml | 1 + libafl_targets/build.rs | 15 +- libafl_targets/src/lib.rs | 5 + .../{ => src}/libfuzzer_compatibility.c | 0 libafl_targets/src/value_profile.c | 97 +++++++++ libafl_targets/src/value_profile.rs | 24 +++ 21 files changed, 151 insertions(+), 581 deletions(-) delete mode 100644 fuzzers/libfuzzer_libpng_old/.gitignore delete mode 100644 fuzzers/libfuzzer_libpng_old/Cargo.toml delete mode 100644 fuzzers/libfuzzer_libpng_old/README.md delete mode 100644 fuzzers/libfuzzer_libpng_old/build.rs delete mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png delete mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty_alpha.png delete mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png delete mode 100644 fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png delete mode 100644 fuzzers/libfuzzer_libpng_old/harness.cc delete mode 100644 fuzzers/libfuzzer_libpng_old/src/fuzzer.rs delete mode 100755 fuzzers/libfuzzer_libpng_old/test.sh rename libafl_targets/{ => src}/libfuzzer_compatibility.c (100%) create mode 100644 libafl_targets/src/value_profile.c create mode 100644 libafl_targets/src/value_profile.rs diff --git a/Cargo.toml b/Cargo.toml index 519d09a76f..cc9355c666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,13 @@ members = [ "libafl_targets", #example fuzzers - "fuzzers/frida_libpng", "fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libpng_cmpalloc", "fuzzers/libfuzzer_windows", ] exclude = [ "fuzzers/libfuzzer_libpng", - "fuzzers/libfuzzer_stb_image", + "fuzzers/libfuzzer_stb_image", + "fuzzers/frida_libpng", + "fuzzers/qemu_user", ] diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 2fd07e7c74..4fe870bfdc 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -5,18 +5,16 @@ authors = ["Andrea Fioraldi ", "Dominik Maier ", "Dominik Maier "] -edition = "2018" -build = "build.rs" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -default = ["std"] -std = [] - -#[profile.release] -#lto = true -#codegen-units = 1 -#opt-level = 3 -#debug = true - -[build-dependencies] -cc = { version = "1.0", features = ["parallel"] } -num_cpus = "1.0" - -[dependencies] -libafl = { path = "../../libafl/" } - -[[example]] -name = "libfuzzer_libpng" -path = "./src/fuzzer.rs" -test = false -bench = false diff --git a/fuzzers/libfuzzer_libpng_old/README.md b/fuzzers/libfuzzer_libpng_old/README.md deleted file mode 100644 index f56138c2b5..0000000000 --- a/fuzzers/libfuzzer_libpng_old/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Libfuzzer for libpng - -This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. -To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. -It has been tested on Linux. - -## Build - -To build this example, run `cargo build --example libfuzzer_libpng --release`. -This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. -Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. -Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. - -## Run - -The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. - -Each following execution will run a fuzzer client. -As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). -This means each client will start itself again to listen for crashes and timeouts. -By restarting the actual fuzzer, it can recover from these exit conditions. - -In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). - -For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/libfuzzer_libpng_old/build.rs b/fuzzers/libfuzzer_libpng_old/build.rs deleted file mode 100644 index 5e68b3bc46..0000000000 --- a/fuzzers/libfuzzer_libpng_old/build.rs +++ /dev/null @@ -1,113 +0,0 @@ -// build.rs - -use std::{ - env, - path::Path, - process::{exit, Command}, -}; - -const LIBPNG_URL: &str = - "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; - -fn main() { - if cfg!(windows) { - println!("cargo:warning=Skipping libpng example on Windows"); - exit(0); - } - - let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); - let out_dir = out_dir.to_string_lossy().to_string(); - let out_dir_path = Path::new(&out_dir); - - println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); - println!("cargo:rerun-if-changed=harness.cc"); - - let libpng = format!("{}/libpng-1.6.37", &out_dir); - let libpng_path = Path::new(&libpng); - let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); - - // Enforce clang for its -fsanitize-coverage support. - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); - let ldflags = match env::var("LDFLAGS") { - Ok(val) => val, - Err(_) => "".to_string(), - }; - - if !libpng_path.is_dir() { - if !Path::new(&libpng_tar).is_file() { - println!("cargo:warning=Libpng not found, downloading..."); - // Download libpng - Command::new("wget") - .arg("-c") - .arg(LIBPNG_URL) - .arg("-O") - .arg(&libpng_tar) - .status() - .unwrap(); - } - Command::new("tar") - .current_dir(&out_dir_path) - .arg("-xvf") - .arg(&libpng_tar) - .status() - .unwrap(); - Command::new(format!("{}/configure", &libpng)) - .current_dir(&libpng_path) - .args(&[ - "--disable-shared", - &format!("--host={}", env::var("TARGET").unwrap())[..], - ]) - .env("CC", "clang") - .env("CXX", "clang++") - .env( - "CFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "CXXFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "LDFLAGS", - format!("-g -fPIE -fsanitize-coverage=trace-pc-guard {}", ldflags), - ) - .status() - .unwrap(); - Command::new("make") - .current_dir(&libpng_path) - .status() - .unwrap(); - } - - cc::Build::new() - .file("../libfuzzer_runtime/rt.c") - .compile("libfuzzer-sys"); - - cc::Build::new() - .include(&libpng_path) - .cpp(true) - .flag("-fsanitize-coverage=trace-pc-guard") - .flag("-Wno-void-pointer-to-int-cast") - .flag("-Wno-int-to-pointer-cast") - .flag("-Wno-sign-compare") - .flag("-Wno-format") - // .define("HAS_DUMMY_CRASH", "1") - .file("./harness.cc") - .compile("libfuzzer-harness"); - - println!("cargo:rustc-link-search=native={}", &out_dir); - println!("cargo:rustc-link-search=native={}/.libs", &libpng); - println!("cargo:rustc-link-lib=static=png16"); - - //Deps for libpng: -pthread -lz -lm - println!("cargo:rustc-link-lib=dylib=m"); - println!("cargo:rustc-link-lib=dylib=z"); - - //For the C++ harness - //must by dylib for android - println!("cargo:rustc-link-lib=dylib=stdc++"); - - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png deleted file mode 100644 index eff7c1707b936a8f8df725814f604d454b78b5c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X_yc@GT+_~+`TzevkY_wIZRYx+5&y#hyq+?%!C8<`)MX5lF!N|bSRM)^r*U&J;z}U*bz{;0L z1Vuw`eoAIqC5i?kD`P_|6GMoGiCWXn12ss3YzWRzD=AMbN@Z|N$xljE@XSq2PYp^< WOsOn9nQ8-6#Ng@b=d#Wzp$PyV*n0l} diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png deleted file mode 100644 index 939d9d29a9b9f95bac5e9a72854361ee85469921..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmTQ929t;oCfmw1AIbU z)6Sgv|NlRbXFM})=KnKxKI=t+9LW;bh?3y^w370~qErUQl>DSr1<%~X^wgl##FWay zlc_d9MbVxvjv*GO?@o5)YH;9THa`3B|5>?^8?LvjJ}xLe>!7e@k)r^sLedir0mCVe z=5sMjEm$*~tHD+}{NS_$nMdb|ABqg-@UGMMsZ=uY-X%Cq@&3vmZ%&@H{P?6&+U!yq VvuXWlo?M_c44$rjF6*2UngF4cP+$N6 diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png deleted file mode 100644 index f0c7804d99829cc6307c1c6ae9915cf42d555414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 427 zcmV;c0aX5pP)9xSWu9|B*4Isn^#g47m^r~thH)GiR<@yX0fO)OF<2Kt#qCldyUF#H?{4jV?XGw9)psxE&K1B1m^ z1_tH{2(hG@3=G>_85ksPA;eS`Ffj19FfeR8pIlm01~rBeWCZ{dbvfq;rA3DT000kA zOjJc?%*_A){{R30GnreSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM003J_L_t(I%iWVf3V=Wi12fJ3|IHp$*hSlV@t||fKp?cDK@bHXV&o_g zF_hw;3ILUGteXmeJsVfSmcVJno)^MdQwU3bFHCtNG)uY>mLcD%`0UBaIq~Fq8#dBr V12uok3~c}a002ovPDHLkV1nKBo!S5Z diff --git a/fuzzers/libfuzzer_libpng_old/harness.cc b/fuzzers/libfuzzer_libpng_old/harness.cc deleted file mode 100644 index 65faff685d..0000000000 --- a/fuzzers/libfuzzer_libpng_old/harness.cc +++ /dev/null @@ -1,197 +0,0 @@ -// libpng_read_fuzzer.cc -// Copyright 2017-2018 Glenn Randers-Pehrson -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that may -// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE - -// Last changed in libpng 1.6.35 [July 15, 2018] - -// The modifications in 2017 by Glenn Randers-Pehrson include -// 1. addition of a PNG_CLEANUP macro, -// 2. setting the option to ignore ADLER32 checksums, -// 3. adding "#include " which is needed on some platforms -// to provide memcpy(). -// 4. adding read_end_info() and creating an end_info structure. -// 5. adding calls to png_set_*() transforms commonly used by browsers. - -#include -#include -#include - -#include - -#define PNG_INTERNAL -#include "png.h" - -#define PNG_CLEANUP \ - if(png_handler.png_ptr) \ - { \ - if (png_handler.row_ptr) \ - png_free(png_handler.png_ptr, png_handler.row_ptr); \ - if (png_handler.end_info_ptr) \ - png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ - &png_handler.end_info_ptr); \ - else if (png_handler.info_ptr) \ - png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ - nullptr); \ - else \ - png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ - png_handler.png_ptr = nullptr; \ - png_handler.row_ptr = nullptr; \ - png_handler.info_ptr = nullptr; \ - png_handler.end_info_ptr = nullptr; \ - } - -struct BufState { - const uint8_t* data; - size_t bytes_left; -}; - -struct PngObjectHandler { - png_infop info_ptr = nullptr; - png_structp png_ptr = nullptr; - png_infop end_info_ptr = nullptr; - png_voidp row_ptr = nullptr; - BufState* buf_state = nullptr; - - ~PngObjectHandler() { - if (row_ptr) - png_free(png_ptr, row_ptr); - if (end_info_ptr) - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); - else if (info_ptr) - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - else - png_destroy_read_struct(&png_ptr, nullptr, nullptr); - delete buf_state; - } -}; - -void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { - BufState* buf_state = static_cast(png_get_io_ptr(png_ptr)); - if (length > buf_state->bytes_left) { - png_error(png_ptr, "read error"); - } - memcpy(data, buf_state->data, length); - buf_state->bytes_left -= length; - buf_state->data += length; -} - -static const int kPngHeaderSize = 8; - -// Entry point for LibFuzzer. -// Roughly follows the libpng book example: -// http://www.libpng.org/pub/png/book/chapter13.html -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < kPngHeaderSize) { - return 0; - } - - std::vector v(data, data + size); - if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { - // not a PNG. - return 0; - } - - PngObjectHandler png_handler; - png_handler.png_ptr = nullptr; - png_handler.row_ptr = nullptr; - png_handler.info_ptr = nullptr; - png_handler.end_info_ptr = nullptr; - - png_handler.png_ptr = png_create_read_struct - (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (!png_handler.png_ptr) { - return 0; - } - - png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); - if (!png_handler.info_ptr) { - PNG_CLEANUP - return 0; - } - - png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); - if (!png_handler.end_info_ptr) { - PNG_CLEANUP - return 0; - } - - png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); -#ifdef PNG_IGNORE_ADLER32 - png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); -#endif - - // Setting up reading from buffer. - png_handler.buf_state = new BufState(); - png_handler.buf_state->data = data + kPngHeaderSize; - png_handler.buf_state->bytes_left = size - kPngHeaderSize; - png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); - png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); - - if (setjmp(png_jmpbuf(png_handler.png_ptr))) { - PNG_CLEANUP - return 0; - } - - // Reading. - png_read_info(png_handler.png_ptr, png_handler.info_ptr); - - // reset error handler to put png_deleter into scope. - if (setjmp(png_jmpbuf(png_handler.png_ptr))) { - PNG_CLEANUP - return 0; - } - - png_uint_32 width, height; - int bit_depth, color_type, interlace_type, compression_type; - int filter_type; - - if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, - &height, &bit_depth, &color_type, &interlace_type, - &compression_type, &filter_type)) { - PNG_CLEANUP - return 0; - } - - // This is going to be too slow. - if (width && height > 100000000 / width) { - PNG_CLEANUP -#ifdef HAS_DUMMY_CRASH - #ifdef __aarch64__ - asm volatile (".word 0xf7f0a000\n"); - #else - asm("ud2"); - #endif -#endif - return 0; - } - - // Set several transforms that browsers typically use: - png_set_gray_to_rgb(png_handler.png_ptr); - png_set_expand(png_handler.png_ptr); - png_set_packing(png_handler.png_ptr); - png_set_scale_16(png_handler.png_ptr); - png_set_tRNS_to_alpha(png_handler.png_ptr); - - int passes = png_set_interlace_handling(png_handler.png_ptr); - - png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); - - png_handler.row_ptr = png_malloc( - png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, - png_handler.info_ptr)); - - for (int pass = 0; pass < passes; ++pass) { - for (png_uint_32 y = 0; y < height; ++y) { - png_read_row(png_handler.png_ptr, - static_cast(png_handler.row_ptr), nullptr); - } - } - - png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); - - PNG_CLEANUP - return 0; -} - diff --git a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs deleted file mode 100644 index 8653a61586..0000000000 --- a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts -//! The example harness is built for libpng. - -#[cfg(unix)] -use core::time::Duration; -use std::{env, path::PathBuf}; - -#[cfg(unix)] -use libafl::{ - bolts::{shmem::UnixShMem, tuples::tuple_list}, - corpus::{ - Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, - QueueCorpusScheduler, - }, - events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, - fuzzer::{Fuzzer, StdFuzzer}, - mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, - observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, - stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, - stats::SimpleStats, - utils::{current_nanos, StdRand}, - Error, -}; - -/// We will interact with a C++ target, so use external c functionality -#[cfg(unix)] -extern "C" { - /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; - - // afl_libfuzzer_init calls LLVMFUzzerInitialize() - fn afl_libfuzzer_init() -> i32; - - static __lafl_edges_map: *mut u8; - static __lafl_cmp_map: *mut u8; - static __lafl_max_edges_size: u32; -} - -/// The main fn, usually parsing parameters, and starting the fuzzer -pub fn main() { - // Registry the metadata types used in this fuzzer - // Needed only on no_std - //RegistryBuilder::register::(); - - println!( - "Workdir: {:?}", - env::current_dir().unwrap().to_string_lossy().to_string() - ); - fuzz( - vec![PathBuf::from("./corpus")], - PathBuf::from("./crashes"), - 1337, - ) - .expect("An error occurred while fuzzing"); -} - -/// Not supported on windows right now -#[cfg(windows)] -fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { - todo!("Example not supported on Windows"); -} - -/// The actual fuzzer -#[cfg(unix)] -fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { - // 'While the stats are state, they are usually used in the broker - which is likely never restarted - let stats = SimpleStats::new(|s| println!("{}", s)); - - // The restarting state will spawn the same process again as child, then restarted it each time it crashes. - let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { - Ok(res) => res, - Err(err) => match err { - Error::ShuttingDown => { - return Ok(()); - } - _ => { - panic!("Failed to setup the restarter: {}", err); - } - }, - }; - - // Create an observation channel using the coverage map - let edges_observer = HitcountsMapObserver::new(unsafe { - StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) - }); - - // If not restarting, create a State from scratch - let mut state = state.unwrap_or_else(|| { - State::new( - // RNG - StdRand::with_seed(current_nanos()), - // Corpus that will be evolved, we keep it in memory for performance - InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - tuple_list!( - MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), - TimeFeedback::new() - ), - // Corpus in which we store solutions (crashes in this example), - // on disk so the user can get them after stopping the fuzzer - OnDiskCorpus::new(objective_dir).unwrap(), - // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()), - ) - }); - - println!("We're a client, let's fuzz :)"); - - // Create a PNG dictionary if not existing - if state.metadata().get::().is_none() { - state.add_metadata(Tokens::new(vec![ - vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header - "IHDR".as_bytes().to_vec(), - "IDAT".as_bytes().to_vec(), - "PLTE".as_bytes().to_vec(), - "IEND".as_bytes().to_vec(), - ])); - } - - // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); - - // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); - - // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |buf: &[u8]| { - unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; - ExitKind::Ok - }; - - // Create the executor for an in-process function with just one observer for edge coverage - let mut executor = TimeoutExecutor::new( - InProcessExecutor::new( - "in-process(edges)", - &mut harness, - tuple_list!(edges_observer, TimeObserver::new("time")), - &mut state, - &mut restarting_mgr, - )?, - // 10 seconds timeout - Duration::new(10, 0), - ); - - // The actual target run starts here. - // Call LLVMFUzzerInitialize() if present. - unsafe { - if afl_libfuzzer_init() == -1 { - println!("Warning: LLVMFuzzerInitialize failed with -1") - } - } - - // In case the corpus is empty (on first run), reset - if state.corpus().count() < 1 { - state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) - .expect(&format!( - "Failed to load initial corpus at {:?}", - &corpus_dirs - )); - println!("We imported {} inputs from disk.", state.corpus().count()); - } - - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; - - // Never reached - Ok(()) -} diff --git a/fuzzers/libfuzzer_libpng_old/test.sh b/fuzzers/libfuzzer_libpng_old/test.sh deleted file mode 100755 index 156cf1de04..0000000000 --- a/fuzzers/libfuzzer_libpng_old/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -mkdir -p ./crashes -rm -rf ./.libfuzzer_test.elf - -cargo build --example libfuzzer_libpng --release || exit 1 -cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf - -# The broker -RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf & -# Give the broker time to spawn -sleep 2 -echo "Spawning client" -# The 1st fuzzer client, pin to cpu 0x1 -RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf 2>/dev/null - -killall .libfuzzer_test.elf -rm -rf ./.libfuzzer_test.elf diff --git a/fuzzers/libfuzzer_stb_image/Cargo.toml b/fuzzers/libfuzzer_stb_image/Cargo.toml index 6328090379..1ec1ab28ba 100644 --- a/fuzzers/libfuzzer_stb_image/Cargo.toml +++ b/fuzzers/libfuzzer_stb_image/Cargo.toml @@ -17,8 +17,8 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "value_profile", "libfuzzer"] } [build-dependencies] cc = { version = "1.0", features = ["parallel"] } -num_cpus = "1.0" \ No newline at end of file +num_cpus = "1.0" diff --git a/fuzzers/libfuzzer_stb_image/build.rs b/fuzzers/libfuzzer_stb_image/build.rs index 50cb1582bc..27c140f6df 100644 --- a/fuzzers/libfuzzer_stb_image/build.rs +++ b/fuzzers/libfuzzer_stb_image/build.rs @@ -14,7 +14,7 @@ fn main() { cc::Build::new() // Use sanitizer coverage to track the edges in the PUT - .flag("-fsanitize-coverage=trace-pc-guard") + .flag("-fsanitize-coverage=trace-pc-guard,trace-cmp") // Take advantage of LTO (needs lld-link set in your cargo config) //.flag("-flto=thin") .flag("-Wno-sign-compare") diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index cbf30ef790..67d4e88e64 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -9,6 +9,7 @@ default = [] pcguard_edges = [] pcguard_hitcounts = [] libfuzzer = [] +value_profile = [] pcguard = ["pcguard_hitcounts"] [build-dependencies] diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index fb5a37f045..8062540719 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -1,10 +1,12 @@ // build.rs use std::env; +use std::path::Path; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = out_dir.to_string_lossy().to_string(); + let src_dir = Path::new("src"); //let out_dir_path = Path::new(&out_dir); //std::env::set_var("CC", "clang"); @@ -12,12 +14,21 @@ fn main() { #[cfg(feature = "libfuzzer")] { - println!("cargo:rerun-if-changed=libfuzzer_compatibility.c"); + println!("cargo:rerun-if-changed=src/libfuzzer_compatibility.c"); cc::Build::new() - .file("libfuzzer_compatibility.c") + .file(src_dir.join("libfuzzer_compatibility.c")) .compile("libfuzzer_compatibility"); } + + #[cfg(feature = "value_profile")] + { + println!("cargo:rerun-if-changed=src/value_profile.c"); + + cc::Build::new() + .file(src_dir.join("value_profile.c")) + .compile("value_profile"); + } println!("cargo:rustc-link-search=native={}", &out_dir); diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index d1ccae18c4..e817b8a57c 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -3,6 +3,11 @@ pub mod pcguard; #[cfg(any(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] pub use pcguard::*; +#[cfg(feature = "value_profile")] +pub mod value_profile; +#[cfg(feature = "value_profile")] +pub use value_profile::*; + #[cfg(feature = "libfuzzer")] pub mod libfuzzer; #[cfg(feature = "libfuzzer")] diff --git a/libafl_targets/libfuzzer_compatibility.c b/libafl_targets/src/libfuzzer_compatibility.c similarity index 100% rename from libafl_targets/libfuzzer_compatibility.c rename to libafl_targets/src/libfuzzer_compatibility.c diff --git a/libafl_targets/src/value_profile.c b/libafl_targets/src/value_profile.c new file mode 100644 index 0000000000..5f093f5420 --- /dev/null +++ b/libafl_targets/src/value_profile.c @@ -0,0 +1,97 @@ +#include +#include +#include + +// TODO compile time flag +#define MAP_SIZE 65536 + +extern uint8_t libafl_cmp_map[MAP_SIZE]; + +#define MAX(a, b) \ + ({ \ + \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + \ + }) + +#if defined(__APPLE__) + #pragma weak __sanitizer_cov_trace_const_cmp1 = __sanitizer_cov_trace_cmp1 + #pragma weak __sanitizer_cov_trace_const_cmp2 = __sanitizer_cov_trace_cmp2 + #pragma weak __sanitizer_cov_trace_const_cmp4 = __sanitizer_cov_trace_cmp4 + #pragma weak __sanitizer_cov_trace_const_cmp8 = __sanitizer_cov_trace_cmp8 +#else +void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__sanitizer_cov_trace_cmp1"))); +void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) + __attribute__((alias("__sanitizer_cov_trace_cmp2"))); +void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) + __attribute__((alias("__sanitizer_cov_trace_cmp4"))); +void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) + __attribute__((alias("__sanitizer_cov_trace_cmp8"))); +#endif + +void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + +} + +void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + +} + +void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + +} + +void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcountll(~(arg1 ^ arg2)))); + +} + +void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { + + uintptr_t rt = (uintptr_t)__builtin_return_address(0); + if (cases[1] == 64) { + + for (uint64_t i = 0; i < cases[0]; i++) { + + uintptr_t k = rt + i; + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcountll(~(val ^ cases[i + 2])))); + + } + + } else { + + for (uint64_t i = 0; i < cases[0]; i++) { + + uintptr_t k = rt + i; + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(val ^ cases[i + 2])))); + + } + + } + +} diff --git a/libafl_targets/src/value_profile.rs b/libafl_targets/src/value_profile.rs new file mode 100644 index 0000000000..d5aabd8872 --- /dev/null +++ b/libafl_targets/src/value_profile.rs @@ -0,0 +1,24 @@ +// TODO compile time flag +pub const MAP_SIZE: usize = 65536; + +#[no_mangle] +pub static mut libafl_cmp_map: [u8; MAP_SIZE] = [0; MAP_SIZE]; + +pub use libafl_cmp_map as CMP_MAP; + +/* +extern { + #[link_name = "llvm.returnaddress"] + fn return_address() -> usize; +} + +#[no_mangle] +pub unsafe extern "C" fn __sanitizer_cov_trace_cmp1(arg1: u8, arg2: u8) { + let mut pos = return_address(); + pos = (pos >> 4) ^ (pos << 8); + pos &= MAP_SIZE - 1; + *CMP_MAP.get_unchecked_mut(pos) = core::cmp::max(*CMP_MAP.get_unchecked(pos), (!(arg1 ^ arg2)).count_ones() as u8); +} +*/ + +// TODO complete when linking to LLVM intrinsic will land to stable Rust From 4b77ea9975ed3784090facc4707c860bf1d6a791 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Sat, 27 Mar 2021 00:36:13 +0100 Subject: [PATCH 031/104] book intro --- docs/README.md | 4 ++-- docs/book.toml | 4 ++-- docs/src/SUMMARY.md | 14 +++++++++++++- docs/src/baby_fuzzer.md | 1 + docs/src/chapter_1.md | 1 - docs/src/core_concepts/core_concepts.md | 1 + docs/src/core_concepts/executor.md | 1 + docs/src/core_concepts/observer.md | 1 + docs/src/getting_started/crates.md | 1 + docs/src/getting_started/getting_started.md | 1 + docs/src/getting_started/installation.md | 1 + docs/src/introduction.md | 14 ++++++++++++++ docs/src/libafl.md | 9 +++++++++ 13 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 docs/src/baby_fuzzer.md delete mode 100644 docs/src/chapter_1.md create mode 100644 docs/src/core_concepts/core_concepts.md create mode 100644 docs/src/core_concepts/executor.md create mode 100644 docs/src/core_concepts/observer.md create mode 100644 docs/src/getting_started/crates.md create mode 100644 docs/src/getting_started/getting_started.md create mode 100644 docs/src/getting_started/installation.md create mode 100644 docs/src/introduction.md create mode 100644 docs/src/libafl.md diff --git a/docs/README.md b/docs/README.md index 8e379b7b28..57f0f059b8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ -# LibAFL Documentation +# LibAFL Documentation Book -This project contains the out-of-source LibAFL documentation. +This project contains the out-of-source LibAFL documentation as a book. Here you can find tutorials, examples and detailed explanations. diff --git a/docs/book.toml b/docs/book.toml index 68f4a19788..8901056073 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -1,6 +1,6 @@ [book] -authors = ["Andrea Fioraldi"] +authors = ["Andrea Fioraldi", "Dominik Maier"] language = "en" multilingual = false src = "src" -title = "LibAFL Documentation" +title = "The LibAFL Fuzzing Library" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 7390c82896..2aed5e4096 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -1,3 +1,15 @@ # Summary -- [Chapter 1](./chapter_1.md) +[The LibAFL Fuzzing Library](./libafl.md) + +[Introduction](./introduction.md) + +- [Getting Started](./getting_started/getting_started.md) + - [Installation](./getting_started/installation.md) + - [Crates](./getting_started/crates.md) + +- [Baby Fuzzer](./baby_fuzzer.md) + +- [Core Concepts](./core_concepts/core_concepts.md) + - [Executor](./core_concepts/executor.md) + - [Observer](./core_concepts/observer.md) diff --git a/docs/src/baby_fuzzer.md b/docs/src/baby_fuzzer.md new file mode 100644 index 0000000000..4f1e4ff234 --- /dev/null +++ b/docs/src/baby_fuzzer.md @@ -0,0 +1 @@ +# Baby Fuzzer diff --git a/docs/src/chapter_1.md b/docs/src/chapter_1.md deleted file mode 100644 index b743fda354..0000000000 --- a/docs/src/chapter_1.md +++ /dev/null @@ -1 +0,0 @@ -# Chapter 1 diff --git a/docs/src/core_concepts/core_concepts.md b/docs/src/core_concepts/core_concepts.md new file mode 100644 index 0000000000..c587b44432 --- /dev/null +++ b/docs/src/core_concepts/core_concepts.md @@ -0,0 +1 @@ +# Core Concepts diff --git a/docs/src/core_concepts/executor.md b/docs/src/core_concepts/executor.md new file mode 100644 index 0000000000..3fddc32644 --- /dev/null +++ b/docs/src/core_concepts/executor.md @@ -0,0 +1 @@ +# Executor diff --git a/docs/src/core_concepts/observer.md b/docs/src/core_concepts/observer.md new file mode 100644 index 0000000000..94e0f8b987 --- /dev/null +++ b/docs/src/core_concepts/observer.md @@ -0,0 +1 @@ +# Observer diff --git a/docs/src/getting_started/crates.md b/docs/src/getting_started/crates.md new file mode 100644 index 0000000000..5008dca83a --- /dev/null +++ b/docs/src/getting_started/crates.md @@ -0,0 +1 @@ +# Crates diff --git a/docs/src/getting_started/getting_started.md b/docs/src/getting_started/getting_started.md new file mode 100644 index 0000000000..bad55622f9 --- /dev/null +++ b/docs/src/getting_started/getting_started.md @@ -0,0 +1 @@ +# Getting Started diff --git a/docs/src/getting_started/installation.md b/docs/src/getting_started/installation.md new file mode 100644 index 0000000000..25267fe2b7 --- /dev/null +++ b/docs/src/getting_started/installation.md @@ -0,0 +1 @@ +# Installation diff --git a/docs/src/introduction.md b/docs/src/introduction.md new file mode 100644 index 0000000000..94fb00b27e --- /dev/null +++ b/docs/src/introduction.md @@ -0,0 +1,14 @@ +# Introduction + +Fuzzers are important assets in the pockets of security researchers and even developers nowadays. +A wide range of cool state-of-the-art tools like [AFL++](https://github.com/AFLplusplus/AFLplusplus), [libFuzzer](https://llvm.org/docs/LibFuzzer.html) or [honggfuzz](https://github.com/google/honggfuzz) are avaiable to users and they do their job in a very effective way. + +From the power user perspective, however, these tools are limited because not designed with the extensibility as first-class citizen. +Usually, a fuzzer developer has to choose if fork one of these existing tools with the result of having a tons of fuzzers derived from others which are in any case incompatible with each other, or creating a new fuzzer from scratch, reinventing the wheel and usually giving up on features that are complex to reimplement. + +Here comes LibAFL, a library that IS NOT a fuzzer, but a collection of reusable pieces of fuzzers written in Rust. +LibAFL helps you writing your own custom fuzzer, tailored for a specific target or for a particular instrumentation backend, without reinventing the wheel or forking an existing fuzzer. + +## Why you should use LibAFL + +TODO list here killer features (no_std, multi platform, scalability, ...) diff --git a/docs/src/libafl.md b/docs/src/libafl.md new file mode 100644 index 0000000000..1f9b512823 --- /dev/null +++ b/docs/src/libafl.md @@ -0,0 +1,9 @@ +# The LibAFL Fuzzing Library + +*by Andrea Fioraldi and Dominik Maier* + +This version of the LibAFL book is coupled with the release 1.0 beta of the library. + +This document is still incomplete, and extremely work-in-progress. The structure and the concepts explained here will likely change a lot in the future, as the structure of LibAFL itself will change. + +The HTML version of this book is available online at PLACEHOLDER and offline from the LibAFL repository in the docs/ folder. From d231966dbe2c0fb746607902b26d426cf93d0e8e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Sat, 27 Mar 2021 09:42:51 +0100 Subject: [PATCH 032/104] setup page in book --- docs/src/SUMMARY.md | 3 +- docs/src/getting_started/build.md | 3 ++ docs/src/getting_started/getting_started.md | 3 ++ docs/src/getting_started/installation.md | 1 - docs/src/getting_started/setup.md | 58 +++++++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 docs/src/getting_started/build.md delete mode 100644 docs/src/getting_started/installation.md create mode 100644 docs/src/getting_started/setup.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 2aed5e4096..bcd4ffea37 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -5,7 +5,8 @@ [Introduction](./introduction.md) - [Getting Started](./getting_started/getting_started.md) - - [Installation](./getting_started/installation.md) + - [Setup](./getting_started/setup.md) + - [Build](./getting_started/build.md) - [Crates](./getting_started/crates.md) - [Baby Fuzzer](./baby_fuzzer.md) diff --git a/docs/src/getting_started/build.md b/docs/src/getting_started/build.md new file mode 100644 index 0000000000..174deb9738 --- /dev/null +++ b/docs/src/getting_started/build.md @@ -0,0 +1,3 @@ +# Build + + diff --git a/docs/src/getting_started/getting_started.md b/docs/src/getting_started/getting_started.md index bad55622f9..6becb923c8 100644 --- a/docs/src/getting_started/getting_started.md +++ b/docs/src/getting_started/getting_started.md @@ -1 +1,4 @@ # Getting Started + +To start using LibAFL, there are some first steps to do. In this chapter, we will +discuss how to install LibAFL with `cargo`, how are structured its crates and the purpose of each crate. diff --git a/docs/src/getting_started/installation.md b/docs/src/getting_started/installation.md deleted file mode 100644 index 25267fe2b7..0000000000 --- a/docs/src/getting_started/installation.md +++ /dev/null @@ -1 +0,0 @@ -# Installation diff --git a/docs/src/getting_started/setup.md b/docs/src/getting_started/setup.md new file mode 100644 index 0000000000..e57aca1f14 --- /dev/null +++ b/docs/src/getting_started/setup.md @@ -0,0 +1,58 @@ +# Setup + +The first step is to download LibAFL and all its dependencies that are not automatically installed with `cargo`. + +> ### Command Line Notation +> +> In this chapter and throughout the book, we’ll show some commands used in the +> terminal. Lines that you should enter in a terminal all start with `$`. You +> don’t need to type in the `$` character; it indicates the start of each +> command. Lines that don’t start with `$` typically show the output of the +> previous command. Additionally, PowerShell-specific examples will use `>` +> rather than `$`. + +The easiest way to download LibAFL is using `git`. + +```sh +$ git clone git@github.com:AFLplusplus/LibAFL.git +``` + +You can alternatively, on a UNIX-like machine, download a compressed archive and extract with: + +```sh +$ wget https://github.com/AFLplusplus/LibAFL/archive/main.tar.gz +$ tar xvf LibAFL-main.tar.gz +$ rm LibAFL-main.tar.gz +$ ls LibAFL-main # this is the extracted folder +``` + +## Clang installation + +One of the external dependencies of LibAFL is the Clang C/C++ compiler. +While most of the code is in pure Rust, we still need a C compiler because Rust stable +still does not support features that we need such as weak linking and LLVM builtins linking, +and so we use C to expose the missing functionalities to our Rust codebase. + +In addition, if you want to perform source-level fuzz testing of C/C++ applications, +you will likely need Clang with its instrumentation options to compile the programs +under test. + +You can download and build the LLVM source tree, Clang included, following the steps +explained [here](https://clang.llvm.org/get_started.html). + +Alternatively, on Linux, you can use your distro's package manager to get Clang, +but these packages are not always updated, so we suggest you to use the +Debian/Ubuntu prebuilt packages from LLVM that are available using their [official repository](https://apt.llvm.org/). + +For Miscrosoft Windows, you can download the [installer package](https://llvm.org/builds/) that LLVM generates periodically. + +Despite that Clang is the default C compiler on macOS, we discourage the use of the build shipped by Apple and encourage +the installation from `brew` or direclty a fresh build from the source code. + +## Rust installation + +If you don't have Rust installed, you can easily follow the steps described [here](https://www.rust-lang.org/tools/install) +to install it on any supported system. + +We suggest to install Clang and LLVM first. + From cbfe17b95c97f9d300add3dcfa7f80c4b098d6ae Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Sat, 27 Mar 2021 10:10:50 +0100 Subject: [PATCH 033/104] build page in book --- docs/src/getting_started/build.md | 21 +++++++++++++++++++++ docs/src/getting_started/crates.md | 12 ++++++++++++ docs/src/getting_started/getting_started.md | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/src/getting_started/build.md b/docs/src/getting_started/build.md index 174deb9738..7aad411b76 100644 --- a/docs/src/getting_started/build.md +++ b/docs/src/getting_started/build.md @@ -1,3 +1,24 @@ # Build +LibAFL, as most of the Rust projects, can be built using `cargo` from the root directory of the project with: +```sh +$ cargo build --release +``` + +Note that the `--release` flag is optional for development, but it is needed for fuzzing at a decent speed, +otherwise you will experience a slowdown of even more than 10x. + +The LibAFL repository is composed by multiple crates, and the top-level Cargo.toml is just an orchestrator that groups these crates +in a workspace. Building from the root directory will build all the crates in the workspace. + +## Build example fuzzers + +You can notice that in the repository there is a `fuzzers/` folder. +This folder contains a set of crates that are not part of the workspace, so that are not built issuing `cargo build` from the top-level directory. + +These crates are examples of fuzzers using particular features of LibAFL combined sometimes with instrumentation backends (e.g. [SanitizerCoverage](https://clang.llvm.org/docs/SanitizerCoverage.html), [Frida](https://frida.re/), ...). + +The user can use these crates as examples and as skeleton for its custom fuzzer using a similar set of features. + +To build an example fuzzer you have to invoke cargo from its folder (`fuzzers/[FUZZER_NAME]). diff --git a/docs/src/getting_started/crates.md b/docs/src/getting_started/crates.md index 5008dca83a..c6b907532e 100644 --- a/docs/src/getting_started/crates.md +++ b/docs/src/getting_started/crates.md @@ -1 +1,13 @@ # Crates + +LibAFL is composed by different crates. +Each one has its self-contained purpose, and the user may not need to use all of them in its project. + +Following the naming convention of the folders in the project's root, they are: + +- libafl +- libafl_derive +- libafl_targets +- libafl_cc + + diff --git a/docs/src/getting_started/getting_started.md b/docs/src/getting_started/getting_started.md index 6becb923c8..ef05e80138 100644 --- a/docs/src/getting_started/getting_started.md +++ b/docs/src/getting_started/getting_started.md @@ -1,4 +1,4 @@ # Getting Started To start using LibAFL, there are some first steps to do. In this chapter, we will -discuss how to install LibAFL with `cargo`, how are structured its crates and the purpose of each crate. +discuss how to download LibAFL and build with `cargo`, how are structured its crates and the purpose of each crate. From 0c2a267075354dd0c4aa7830b33a9082d89f4797 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 29 Mar 2021 15:56:54 +0200 Subject: [PATCH 034/104] remove libfuzzer_runtime and use cc wrapper for mozjpeg --- fuzzers/libfuzzer_libmozjpeg/Cargo.toml | 30 +- fuzzers/libfuzzer_libmozjpeg/README.md | 2 + fuzzers/libfuzzer_libmozjpeg/broker.sh | 3 - fuzzers/libfuzzer_libmozjpeg/build.rs | 113 +- fuzzers/libfuzzer_libmozjpeg/hook_allocs.c | 52 + .../{jpeg.tkns => jpeg.dict} | 0 fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs | 33 + fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs | 34 + .../src/{fuzzer.rs => lib.rs} | 49 +- fuzzers/libfuzzer_libmozjpeg/start.sh | 9 - fuzzers/libfuzzer_libmozjpeg/stop.sh | 2 - fuzzers/libfuzzer_libmozjpeg/test.sh | 17 - fuzzers/libfuzzer_libpng/Cargo.toml | 2 - fuzzers/libfuzzer_libpng/src/bin/cc.rs | 2 +- fuzzers/libfuzzer_libpng/src/bin/cxx.rs | 2 +- fuzzers/libfuzzer_libpng_cmpalloc/.gitignore | 1 - fuzzers/libfuzzer_libpng_cmpalloc/Cargo.toml | 31 - fuzzers/libfuzzer_libpng_cmpalloc/README.md | 28 - fuzzers/libfuzzer_libpng_cmpalloc/build.rs | 108 - .../corpus/not_kitty.png | Bin 218 -> 0 bytes .../corpus/not_kitty_alpha.png | Bin 376 -> 0 bytes .../corpus/not_kitty_gamma.png | Bin 228 -> 0 bytes .../corpus/not_kitty_icc.png | Bin 427 -> 0 bytes fuzzers/libfuzzer_libpng_cmpalloc/harness.cc | 197 - .../libfuzzer_libpng_cmpalloc/src/fuzzer.rs | 178 - fuzzers/libfuzzer_libpng_cmpalloc/test.sh | 17 - fuzzers/libfuzzer_runtime/rt.c | 189 - fuzzers/libfuzzer_runtime/rt.rs | 61 - fuzzers/libfuzzer_stb_image/build.rs | 2 +- fuzzers/libfuzzer_windows/.gitignore | 1 - fuzzers/libfuzzer_windows/Cargo.toml | 31 - fuzzers/libfuzzer_windows/README.md | 25 - fuzzers/libfuzzer_windows/build.rs | 58 - .../libfuzzer_windows/corpus/not_kitty.png | Bin 218 -> 0 bytes .../corpus/not_kitty_alpha.png | Bin 376 -> 0 bytes .../corpus/not_kitty_gamma.png | Bin 228 -> 0 bytes .../corpus/not_kitty_icc.png | Bin 427 -> 0 bytes fuzzers/libfuzzer_windows/harness.cc | 231 - fuzzers/libfuzzer_windows/src/fuzzer.rs | 176 - fuzzers/libfuzzer_windows/stb_image.h | 7762 ----------------- fuzzers/libfuzzer_windows/test.bat | 20 - 41 files changed, 164 insertions(+), 9302 deletions(-) delete mode 100755 fuzzers/libfuzzer_libmozjpeg/broker.sh create mode 100644 fuzzers/libfuzzer_libmozjpeg/hook_allocs.c rename fuzzers/libfuzzer_libmozjpeg/{jpeg.tkns => jpeg.dict} (100%) create mode 100644 fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs create mode 100644 fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs rename fuzzers/libfuzzer_libmozjpeg/src/{fuzzer.rs => lib.rs} (78%) delete mode 100755 fuzzers/libfuzzer_libmozjpeg/start.sh delete mode 100755 fuzzers/libfuzzer_libmozjpeg/stop.sh delete mode 100755 fuzzers/libfuzzer_libmozjpeg/test.sh delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/.gitignore delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/Cargo.toml delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/README.md delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/build.rs delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty.png delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_alpha.png delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_gamma.png delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_icc.png delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/harness.cc delete mode 100644 fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs delete mode 100755 fuzzers/libfuzzer_libpng_cmpalloc/test.sh delete mode 100644 fuzzers/libfuzzer_runtime/rt.c delete mode 100644 fuzzers/libfuzzer_runtime/rt.rs delete mode 100644 fuzzers/libfuzzer_windows/.gitignore delete mode 100644 fuzzers/libfuzzer_windows/Cargo.toml delete mode 100644 fuzzers/libfuzzer_windows/README.md delete mode 100644 fuzzers/libfuzzer_windows/build.rs delete mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty.png delete mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty_alpha.png delete mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty_gamma.png delete mode 100644 fuzzers/libfuzzer_windows/corpus/not_kitty_icc.png delete mode 100644 fuzzers/libfuzzer_windows/harness.cc delete mode 100644 fuzzers/libfuzzer_windows/src/fuzzer.rs delete mode 100644 fuzzers/libfuzzer_windows/stb_image.h delete mode 100644 fuzzers/libfuzzer_windows/test.bat diff --git a/fuzzers/libfuzzer_libmozjpeg/Cargo.toml b/fuzzers/libfuzzer_libmozjpeg/Cargo.toml index ede0d941fc..76658ebb41 100644 --- a/fuzzers/libfuzzer_libmozjpeg/Cargo.toml +++ b/fuzzers/libfuzzer_libmozjpeg/Cargo.toml @@ -1,31 +1,29 @@ [package] name = "libfuzzer_libmozjpeg" version = "0.1.0" -authors = ["Marcin Kozlowski "] +authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" -build = "build.rs" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["std"] std = [] -#[profile.release] -#lto = true -#codegen-units = 1 -#opt-level = 3 -#debug = true +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[dependencies] +libafl = { path = "../../libafl/" } +libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "value_profile", "libfuzzer"] } +# TODO Include it only when building cc +libafl_cc = { path = "../../libafl_cc/" } [build-dependencies] cc = { version = "1.0", features = ["parallel"] } num_cpus = "1.0" -[dependencies] -libafl = { path = "../../libafl/" } - -[[example]] +[lib] name = "libfuzzer_libmozjpeg" -path = "./src/fuzzer.rs" -test = false -bench = false +crate-type = ["staticlib"] diff --git a/fuzzers/libfuzzer_libmozjpeg/README.md b/fuzzers/libfuzzer_libmozjpeg/README.md index 931138306c..9a823669c6 100644 --- a/fuzzers/libfuzzer_libmozjpeg/README.md +++ b/fuzzers/libfuzzer_libmozjpeg/README.md @@ -3,6 +3,8 @@ This folder contains an example fuzzer for libmozjpeg, using LLMP for fast multi-process fuzzing and crash detection. It has been tested on Linux. +https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz + ## Build To build this example, run `cargo build --example libfuzzer_libmozjpeg --release`. diff --git a/fuzzers/libfuzzer_libmozjpeg/broker.sh b/fuzzers/libfuzzer_libmozjpeg/broker.sh deleted file mode 100755 index bbe5b82ae6..0000000000 --- a/fuzzers/libfuzzer_libmozjpeg/broker.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -taskset -c 0 ./.libfuzzer_test.elf - diff --git a/fuzzers/libfuzzer_libmozjpeg/build.rs b/fuzzers/libfuzzer_libmozjpeg/build.rs index e71dafcc01..e3961e1e56 100644 --- a/fuzzers/libfuzzer_libmozjpeg/build.rs +++ b/fuzzers/libfuzzer_libmozjpeg/build.rs @@ -1,120 +1,21 @@ // build.rs -use std::{ - env, - path::Path, - process::{exit, Command}, -}; - -const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz"; +use std::env; fn main() { - if cfg!(windows) { - println!("cargo:warning=Skipping libmozjpeg example on Windows"); - exit(0); - } - let out_dir = env::var_os("OUT_DIR").unwrap(); - //let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string(); - let out_dir_path = Path::new(&out_dir); - println!("cargo:rerun-if-changed=./runtime/rt.c",); - println!("cargo:rerun-if-changed=harness.cc"); - - let libmozjpeg = format!("{}/mozjpeg-4.0.3", &out_dir); - let libmozjpeg_path = Path::new(&libmozjpeg); - let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &out_dir); - - // Enforce clang for its -fsanitize-coverage support. - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); - - if !libmozjpeg_path.is_dir() { - if !Path::new(&libmozjpeg_tar).is_file() { - println!("cargo:warning=Libmozjpeg not found, downloading..."); - // Download libmozjpeg - Command::new("wget") - .arg("-c") - .arg(LIBMOZJPEG_URL) - .arg("-O") - .arg(&libmozjpeg_tar) - .status() - .unwrap(); - } - Command::new("tar") - .current_dir(&out_dir_path) - .arg("-xvf") - .arg(&libmozjpeg_tar) - .status() - .unwrap(); - //println!("cargo:warning=Running cmake on {}", &libmozjpeg); - - Command::new("cmake") - .current_dir(&libmozjpeg_path) - .args(&["-G", "Unix Makefiles", "--disable-shared"]) - .arg(&libmozjpeg) - .env("OPT_LEVEL", "3") - .env("CC", "clang") - .env("CXX", "clang++") - .env( - "CFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "CXXFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard") - .status() - .unwrap(); - - Command::new("make") - .current_dir(&libmozjpeg_path) - //.arg(&format!("-j{}", num_cpus::get())) - .args(&[ - "CC=clang", - "CXX=clang++", - "OPT_LEVEL=3", - "CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - "LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard", - "CXXFLAGS=-D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ]) - .env("CC", "clang") - .env("CXX", "clang++") - .env( - "CFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "CXXFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard") - .status() - .unwrap(); - } + println!("cargo:rerun-if-changed=harness.c"); cc::Build::new() - .file("../libfuzzer_runtime/rt.c") - .compile("libfuzzer-sys"); - - cc::Build::new() - .include(&libmozjpeg_path) - .flag("-fsanitize-coverage=trace-pc-guard") - .file("./harness.cc") - .compile("libfuzzer-harness"); + // Use sanitizer coverage to track the edges in the PUT + // Take advantage of LTO (needs lld-link set in your cargo config) + //.flag("-flto=thin") + .file("./hook_allocs.c") + .compile("hook_allocs"); println!("cargo:rustc-link-search=native={}", &out_dir); - println!("cargo:rustc-link-search=native={}/", &libmozjpeg); - println!("cargo:rustc-link-lib=static=jpeg"); - - //Deps for libmozjpeg: -pthread -lz -lm - println!("cargo:rustc-link-lib=dylib=m"); - println!("cargo:rustc-link-lib=dylib=z"); - - //For the C++ harness - println!("cargo:rustc-link-lib=static=stdc++"); println!("cargo:rerun-if-changed=build.rs"); } diff --git a/fuzzers/libfuzzer_libmozjpeg/hook_allocs.c b/fuzzers/libfuzzer_libmozjpeg/hook_allocs.c new file mode 100644 index 0000000000..c0e3224fc4 --- /dev/null +++ b/fuzzers/libfuzzer_libmozjpeg/hook_allocs.c @@ -0,0 +1,52 @@ +#include +#include +#include + +#define MAP_SIZE (16*1024) + +#ifdef _WIN32 +#define posix_memalign(p, a, s) (((*(p)) = _aligned_malloc((s), (a))), *(p) ?0 :errno) +#endif + +#define MAX(a, b) \ + ({ \ + \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + \ + }) + +size_t libafl_alloc_map[MAP_SIZE]; + +void *malloc(size_t size) { + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_alloc_map[k] = MAX(libafl_alloc_map[k], size); + + // We cannot malloc in malloc. + // Hence, even realloc(NULL, size) would loop in an optimized build. + // We fall back to a stricter allocation function. Fingers crossed. + void *ret = NULL; + posix_memalign(&ret, 1<<6, size); + return ret; + +} + +void *calloc(size_t nmemb, size_t size) { + + size *= nmemb; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= MAP_SIZE - 1; + libafl_alloc_map[k] = MAX(libafl_alloc_map[k], size); + + void *ret = NULL; + posix_memalign(&ret, 1<<6, size); + memset(ret, 0, size); + return ret; + +} diff --git a/fuzzers/libfuzzer_libmozjpeg/jpeg.tkns b/fuzzers/libfuzzer_libmozjpeg/jpeg.dict similarity index 100% rename from fuzzers/libfuzzer_libmozjpeg/jpeg.tkns rename to fuzzers/libfuzzer_libmozjpeg/jpeg.dict diff --git a/fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs b/fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs new file mode 100644 index 0000000000..8bc7675165 --- /dev/null +++ b/fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs @@ -0,0 +1,33 @@ +use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX}; +use std::env; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + let mut dir = env::current_exe().unwrap(); + dir.pop(); + + let mut cc = ClangWrapper::new("clang", "clang++"); + cc.from_args(&args) + .unwrap() + .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into()) + .unwrap() + .add_arg("-fPIC".into()) + .unwrap() + .add_link_arg( + dir.join(format!("{}libfuzzer_libmozjpeg.{}", LIB_PREFIX, LIB_EXT)) + .display() + .to_string(), + ) + .unwrap(); + // Libraries needed by libafl on Windows + #[cfg(windows)] + cc.add_link_arg("-lws2_32".into()) + .unwrap() + .add_link_arg("-lBcrypt".into()) + .unwrap() + .add_link_arg("-lAdvapi32".into()) + .unwrap(); + cc.run().unwrap(); + } +} diff --git a/fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs b/fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs new file mode 100644 index 0000000000..164de2fb7a --- /dev/null +++ b/fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs @@ -0,0 +1,34 @@ +use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX}; +use std::env; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + let mut dir = env::current_exe().unwrap(); + dir.pop(); + + let mut cc = ClangWrapper::new("clang", "clang++"); + cc.is_cpp() + .from_args(&args) + .unwrap() + .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into()) + .unwrap() + .add_arg("-fPIC".into()) + .unwrap() + .add_link_arg( + dir.join(format!("{}libfuzzer_libmozjpeg.{}", LIB_PREFIX, LIB_EXT)) + .display() + .to_string(), + ) + .unwrap(); + // Libraries needed by libafl on Windows + #[cfg(windows)] + cc.add_link_arg("-lws2_32".into()) + .unwrap() + .add_link_arg("-lBcrypt".into()) + .unwrap() + .add_link_arg("-lAdvapi32".into()) + .unwrap(); + cc.run().unwrap(); + } +} diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs similarity index 78% rename from fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs rename to fuzzers/libfuzzer_libmozjpeg/src/lib.rs index 0d31d983e2..14d8556eb5 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs @@ -3,7 +3,6 @@ use std::{env, path::PathBuf}; -#[cfg(unix)] use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, @@ -21,21 +20,15 @@ use libafl::{ Error, }; -/// We will interact with a C++ target, so use external c functionality -#[cfg(unix)] +use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM, CMP_MAP, CMP_MAP_SIZE}; + +const ALLOC_MAP_SIZE: usize = 16*1024; extern "C" { - /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; - - // afl_libfuzzer_init calls LLVMFUzzerInitialize() - fn afl_libfuzzer_init() -> i32; - - static __lafl_edges_map: *mut u8; - static __lafl_cmp_map: *mut u8; - static __lafl_max_edges_size: u32; + static mut libafl_alloc_map: [usize; ALLOC_MAP_SIZE]; } /// The main fn, usually parsing parameters, and starting the fuzzer +#[no_mangle] pub fn main() { // Registry the metadata types used in this fuzzer // Needed only on no_std @@ -53,14 +46,7 @@ pub fn main() { .expect("An error occurred while fuzzing"); } -/// Not supported on windows right now -#[cfg(windows)] -fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { - todo!("Example not supported on Windows"); -} - /// The actual fuzzer -#[cfg(unix)] fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { // 'While the stats are state, they are usually used in the broker - which is likely never restarted let stats = SimpleStats::new(|s| println!("{}", s)); @@ -71,9 +57,13 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> .expect("Failed to setup the restarter".into()); // Create an observation channel using the coverage map - let edges_observer = unsafe { - StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) - }; + let edges_observer = StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM }); + + // Create an observation channel using the cmp map + let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP }, CMP_MAP_SIZE); + + // Create an observation channel using the allocations map + let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }, ALLOC_MAP_SIZE); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { @@ -96,7 +86,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Add the JPEG tokens if not existing if state.metadata().get::().is_none() { - state.add_metadata(Tokens::from_tokens_file("./jpeg.tkns")?); + state.add_metadata(Tokens::from_tokens_file("./jpeg.dict")?); } // Setup a basic mutator with a mutational stage @@ -109,25 +99,24 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { - unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + libfuzzer_test_one_input(buf); ExitKind::Ok }; // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( - "in-process(edges)", + "in-process(edges,cmp,alloc)", &mut harness, - tuple_list!(edges_observer), + tuple_list!(edges_observer, cmps_observer, allocs_observer), &mut state, &mut restarting_mgr, )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. - unsafe { - if afl_libfuzzer_init() == -1 { - println!("Warning: LLVMFuzzerInitialize failed with -1") - } + let args: Vec = env::args().collect(); + if libfuzzer_initialize(&args) == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") } // In case the corpus is empty (on first run), reset diff --git a/fuzzers/libfuzzer_libmozjpeg/start.sh b/fuzzers/libfuzzer_libmozjpeg/start.sh deleted file mode 100755 index 9e0b74d5de..0000000000 --- a/fuzzers/libfuzzer_libmozjpeg/start.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -cores=$(grep -c ^processor /proc/cpuinfo) -for (( c=1;c<$cores;c++)) -do - echo $c - taskset -c $c ./.libfuzzer_test.elf 2>/dev/null & - sleep 0.1 -done - diff --git a/fuzzers/libfuzzer_libmozjpeg/stop.sh b/fuzzers/libfuzzer_libmozjpeg/stop.sh deleted file mode 100755 index a97094121b..0000000000 --- a/fuzzers/libfuzzer_libmozjpeg/stop.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -killall -9 .libfuzzer_test.elf diff --git a/fuzzers/libfuzzer_libmozjpeg/test.sh b/fuzzers/libfuzzer_libmozjpeg/test.sh deleted file mode 100755 index 55df5cee43..0000000000 --- a/fuzzers/libfuzzer_libmozjpeg/test.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -mkdir -p ./crashes - -cargo build --example libfuzzer_libmozjpeg --release || exit 1 -cp ../../target/release/examples/libfuzzer_libmozjpeg ./.libfuzzer_test.elf - -# The broker -RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf & -# Give the broker time to spawn -sleep 2 -echo "Spawning client" -# The 1st fuzzer client, pin to cpu 0x1 -RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf 2>/dev/null - -killall .libfuzzer_test.elf -rm -rf ./.libfuzzer_test.elf diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index 5d1c03c221..1f125edadc 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -4,8 +4,6 @@ version = "0.1.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [features] default = ["std"] std = [] diff --git a/fuzzers/libfuzzer_libpng/src/bin/cc.rs b/fuzzers/libfuzzer_libpng/src/bin/cc.rs index 3100d34c99..ad5b05c084 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cc.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/cc.rs @@ -13,7 +13,7 @@ fn main() { .add_arg("-fsanitize-coverage=trace-pc-guard".into()) .unwrap() .add_link_arg( - dir.join(format!("{}libfuzzer_stb_image.{}", LIB_PREFIX, LIB_EXT)) + dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT)) .display() .to_string(), ) diff --git a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs index 3a7701d555..842c915ba2 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/cxx.rs @@ -14,7 +14,7 @@ fn main() { .add_arg("-fsanitize-coverage=trace-pc-guard".into()) .unwrap() .add_link_arg( - dir.join(format!("{}libfuzzer_stb_image.{}", LIB_PREFIX, LIB_EXT)) + dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT)) .display() .to_string(), ) diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/.gitignore b/fuzzers/libfuzzer_libpng_cmpalloc/.gitignore deleted file mode 100644 index a977a2ca5b..0000000000 --- a/fuzzers/libfuzzer_libpng_cmpalloc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libpng-* \ No newline at end of file diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/Cargo.toml b/fuzzers/libfuzzer_libpng_cmpalloc/Cargo.toml deleted file mode 100644 index 16e498bb93..0000000000 --- a/fuzzers/libfuzzer_libpng_cmpalloc/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "libfuzzer_libpng_cmpalloc" -version = "0.1.0" -authors = ["Andrea Fioraldi ", "Dominik Maier "] -edition = "2018" -build = "build.rs" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -default = ["std"] -std = [] - -#[profile.release] -#lto = true -#codegen-units = 1 -#opt-level = 3 -#debug = true - -[build-dependencies] -cc = { version = "1.0", features = ["parallel"] } -num_cpus = "1.0" - -[dependencies] -libafl = { path = "../../libafl/" } - -[[example]] -name = "libfuzzer_libpng_cmpalloc" -path = "./src/fuzzer.rs" -test = false -bench = false diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/README.md b/fuzzers/libfuzzer_libpng_cmpalloc/README.md deleted file mode 100644 index bfd858fbcb..0000000000 --- a/fuzzers/libfuzzer_libpng_cmpalloc/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Libfuzzer for libpng (cmp+alloc) - -This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. -To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. -It has been tested on Linux. - -The difference between the normal Libfuzzer for libpng example here is that this fuzzer is not just using edge coverage as feedback but also comparisons values (-value-profile like) and allocations sizes. -This is an example how multiple feedbacks can be combined in a fuzzer. - -## Build - -To build this example, run `cargo build --example libfuzzer_libpng_cmpalloc --release`. -This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. -Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. -Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng_cmpalloc`. - -## Run - -The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. - -Each following execution will run a fuzzer client. -As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). -This means each client will start itself again to listen for crashes and timeouts. -By restarting the actual fuzzer, it can recover from these exit conditions. - -In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). - -For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/build.rs b/fuzzers/libfuzzer_libpng_cmpalloc/build.rs deleted file mode 100644 index 45dcf2ce58..0000000000 --- a/fuzzers/libfuzzer_libpng_cmpalloc/build.rs +++ /dev/null @@ -1,108 +0,0 @@ -// build.rs - -use std::{ - env, - path::Path, - process::{exit, Command}, -}; - -const LIBPNG_URL: &str = - "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; - -fn main() { - if cfg!(windows) { - println!("cargo:warning=Skipping libpng example on Windows"); - exit(0); - } - - let out_dir = env::var_os("OUT_DIR").unwrap(); - let out_dir = out_dir.to_string_lossy().to_string(); - let out_dir_path = Path::new(&out_dir); - - println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); - println!("cargo:rerun-if-changed=harness.cc"); - - let libpng = format!("{}/libpng-1.6.37", &out_dir); - let libpng_path = Path::new(&libpng); - let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &out_dir); - - // Enforce clang for its -fsanitize-coverage support. - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); - let ldflags = match env::var("LDFLAGS") { - Ok(val) => val, - Err(_) => "".to_string(), - }; - - if !libpng_path.is_dir() { - if !Path::new(&libpng_tar).is_file() { - println!("cargo:warning=Libpng not found, downloading..."); - // Download libpng - Command::new("wget") - .arg("-c") - .arg(LIBPNG_URL) - .arg("-O") - .arg(&libpng_tar) - .status() - .unwrap(); - } - Command::new("tar") - .current_dir(&out_dir_path) - .arg("-xvf") - .arg(&libpng_tar) - .status() - .unwrap(); - Command::new(format!("{}/configure", &libpng)) - .current_dir(&libpng_path) - .args(&[ - "--disable-shared", - &format!("--host={}", env::var("TARGET").unwrap())[..], - ]) - .env("CC", "clang") - .env("CXX", "clang++") - .env( - "CFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "CXXFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - ) - .env( - "LDFLAGS", - format!("-g -fPIE -fsanitize-coverage=trace-pc-guard {}", ldflags), - ) - .status() - .unwrap(); - Command::new("make") - .current_dir(&libpng_path) - .status() - .unwrap(); - } - - cc::Build::new() - .file("../libfuzzer_runtime/rt.c") - .compile("libfuzzer-sys"); - - cc::Build::new() - .include(&libpng_path) - .cpp(true) - .flag("-fsanitize-coverage=trace-pc-guard") - // .define("HAS_DUMMY_CRASH", "1") - .file("./harness.cc") - .compile("libfuzzer-harness"); - - println!("cargo:rustc-link-search=native={}", &out_dir); - println!("cargo:rustc-link-search=native={}/.libs", &libpng); - println!("cargo:rustc-link-lib=static=png16"); - - //Deps for libpng: -pthread -lz -lm - println!("cargo:rustc-link-lib=dylib=m"); - println!("cargo:rustc-link-lib=dylib=z"); - - //For the C++ harness - //must by dylib for android - println!("cargo:rustc-link-lib=dylib=stdc++"); - - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty.png b/fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty.png deleted file mode 100644 index eff7c1707b936a8f8df725814f604d454b78b5c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X_yc@GT+_~+`TzevkY_wIZRYx+5&y#hyq+?%!C8<`)MX5lF!N|bSRM)^r*U&J;z}U*bz{;0L z1Vuw`eoAIqC5i?kD`P_|6GMoGiCWXn12ss3YzWRzD=AMbN@Z|N$xljE@XSq2PYp^< WOsOn9nQ8-6#Ng@b=d#Wzp$PyV*n0l} diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_gamma.png deleted file mode 100644 index 939d9d29a9b9f95bac5e9a72854361ee85469921..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmTQ929t;oCfmw1AIbU z)6Sgv|NlRbXFM})=KnKxKI=t+9LW;bh?3y^w370~qErUQl>DSr1<%~X^wgl##FWay zlc_d9MbVxvjv*GO?@o5)YH;9THa`3B|5>?^8?LvjJ}xLe>!7e@k)r^sLedir0mCVe z=5sMjEm$*~tHD+}{NS_$nMdb|ABqg-@UGMMsZ=uY-X%Cq@&3vmZ%&@H{P?6&+U!yq VvuXWlo?M_c44$rjF6*2UngF4cP+$N6 diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_icc.png deleted file mode 100644 index f0c7804d99829cc6307c1c6ae9915cf42d555414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 427 zcmV;c0aX5pP)9xSWu9|B*4Isn^#g47m^r~thH)GiR<@yX0fO)OF<2Kt#qCldyUF#H?{4jV?XGw9)psxE&K1B1m^ z1_tH{2(hG@3=G>_85ksPA;eS`Ffj19FfeR8pIlm01~rBeWCZ{dbvfq;rA3DT000kA zOjJc?%*_A){{R30GnreSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM003J_L_t(I%iWVf3V=Wi12fJ3|IHp$*hSlV@t||fKp?cDK@bHXV&o_g zF_hw;3ILUGteXmeJsVfSmcVJno)^MdQwU3bFHCtNG)uY>mLcD%`0UBaIq~Fq8#dBr V12uok3~c}a002ovPDHLkV1nKBo!S5Z diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/harness.cc b/fuzzers/libfuzzer_libpng_cmpalloc/harness.cc deleted file mode 100644 index 65faff685d..0000000000 --- a/fuzzers/libfuzzer_libpng_cmpalloc/harness.cc +++ /dev/null @@ -1,197 +0,0 @@ -// libpng_read_fuzzer.cc -// Copyright 2017-2018 Glenn Randers-Pehrson -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that may -// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE - -// Last changed in libpng 1.6.35 [July 15, 2018] - -// The modifications in 2017 by Glenn Randers-Pehrson include -// 1. addition of a PNG_CLEANUP macro, -// 2. setting the option to ignore ADLER32 checksums, -// 3. adding "#include " which is needed on some platforms -// to provide memcpy(). -// 4. adding read_end_info() and creating an end_info structure. -// 5. adding calls to png_set_*() transforms commonly used by browsers. - -#include -#include -#include - -#include - -#define PNG_INTERNAL -#include "png.h" - -#define PNG_CLEANUP \ - if(png_handler.png_ptr) \ - { \ - if (png_handler.row_ptr) \ - png_free(png_handler.png_ptr, png_handler.row_ptr); \ - if (png_handler.end_info_ptr) \ - png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ - &png_handler.end_info_ptr); \ - else if (png_handler.info_ptr) \ - png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ - nullptr); \ - else \ - png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ - png_handler.png_ptr = nullptr; \ - png_handler.row_ptr = nullptr; \ - png_handler.info_ptr = nullptr; \ - png_handler.end_info_ptr = nullptr; \ - } - -struct BufState { - const uint8_t* data; - size_t bytes_left; -}; - -struct PngObjectHandler { - png_infop info_ptr = nullptr; - png_structp png_ptr = nullptr; - png_infop end_info_ptr = nullptr; - png_voidp row_ptr = nullptr; - BufState* buf_state = nullptr; - - ~PngObjectHandler() { - if (row_ptr) - png_free(png_ptr, row_ptr); - if (end_info_ptr) - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); - else if (info_ptr) - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - else - png_destroy_read_struct(&png_ptr, nullptr, nullptr); - delete buf_state; - } -}; - -void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { - BufState* buf_state = static_cast(png_get_io_ptr(png_ptr)); - if (length > buf_state->bytes_left) { - png_error(png_ptr, "read error"); - } - memcpy(data, buf_state->data, length); - buf_state->bytes_left -= length; - buf_state->data += length; -} - -static const int kPngHeaderSize = 8; - -// Entry point for LibFuzzer. -// Roughly follows the libpng book example: -// http://www.libpng.org/pub/png/book/chapter13.html -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < kPngHeaderSize) { - return 0; - } - - std::vector v(data, data + size); - if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { - // not a PNG. - return 0; - } - - PngObjectHandler png_handler; - png_handler.png_ptr = nullptr; - png_handler.row_ptr = nullptr; - png_handler.info_ptr = nullptr; - png_handler.end_info_ptr = nullptr; - - png_handler.png_ptr = png_create_read_struct - (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (!png_handler.png_ptr) { - return 0; - } - - png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); - if (!png_handler.info_ptr) { - PNG_CLEANUP - return 0; - } - - png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); - if (!png_handler.end_info_ptr) { - PNG_CLEANUP - return 0; - } - - png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); -#ifdef PNG_IGNORE_ADLER32 - png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); -#endif - - // Setting up reading from buffer. - png_handler.buf_state = new BufState(); - png_handler.buf_state->data = data + kPngHeaderSize; - png_handler.buf_state->bytes_left = size - kPngHeaderSize; - png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); - png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); - - if (setjmp(png_jmpbuf(png_handler.png_ptr))) { - PNG_CLEANUP - return 0; - } - - // Reading. - png_read_info(png_handler.png_ptr, png_handler.info_ptr); - - // reset error handler to put png_deleter into scope. - if (setjmp(png_jmpbuf(png_handler.png_ptr))) { - PNG_CLEANUP - return 0; - } - - png_uint_32 width, height; - int bit_depth, color_type, interlace_type, compression_type; - int filter_type; - - if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, - &height, &bit_depth, &color_type, &interlace_type, - &compression_type, &filter_type)) { - PNG_CLEANUP - return 0; - } - - // This is going to be too slow. - if (width && height > 100000000 / width) { - PNG_CLEANUP -#ifdef HAS_DUMMY_CRASH - #ifdef __aarch64__ - asm volatile (".word 0xf7f0a000\n"); - #else - asm("ud2"); - #endif -#endif - return 0; - } - - // Set several transforms that browsers typically use: - png_set_gray_to_rgb(png_handler.png_ptr); - png_set_expand(png_handler.png_ptr); - png_set_packing(png_handler.png_ptr); - png_set_scale_16(png_handler.png_ptr); - png_set_tRNS_to_alpha(png_handler.png_ptr); - - int passes = png_set_interlace_handling(png_handler.png_ptr); - - png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); - - png_handler.row_ptr = png_malloc( - png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, - png_handler.info_ptr)); - - for (int pass = 0; pass < passes; ++pass) { - for (png_uint_32 y = 0; y < height; ++y) { - png_read_row(png_handler.png_ptr, - static_cast(png_handler.row_ptr), nullptr); - } - } - - png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); - - PNG_CLEANUP - return 0; -} - diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs deleted file mode 100644 index 5d53ac67a9..0000000000 --- a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs +++ /dev/null @@ -1,178 +0,0 @@ -//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts -//! The example harness is built for libpng. - -use std::{env, path::PathBuf}; - -#[cfg(unix)] -use libafl::{ - bolts::{shmem::UnixShMem, tuples::tuple_list}, - corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, - events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, ExitKind}, - feedbacks::{CrashFeedback, MaxMapFeedback}, - fuzzer::{Fuzzer, StdFuzzer}, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - mutators::token_mutations::Tokens, - observers::{HitcountsMapObserver, StdMapObserver}, - stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, - stats::SimpleStats, - utils::{current_nanos, StdRand}, - Error, -}; - -#[cfg(unix)] -const MAP_SIZE: usize = 16 * 1024; - -/// We will interact with a C++ target, so use external c functionality -#[cfg(unix)] -extern "C" { - /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; - - // afl_libfuzzer_init calls LLVMFUzzerInitialize() - fn afl_libfuzzer_init() -> i32; - - static __lafl_edges_map: *mut u8; - static __lafl_cmp_map: *mut u8; - static __lafl_alloc_map: *mut usize; - static __lafl_max_edges_size: u32; -} - -/// The main fn, usually parsing parameters, and starting the fuzzer -pub fn main() { - // Registry the metadata types used in this fuzzer - // Needed only on no_std - //RegistryBuilder::register::(); - - println!( - "Workdir: {:?}", - env::current_dir().unwrap().to_string_lossy().to_string() - ); - fuzz( - vec![PathBuf::from("./corpus")], - PathBuf::from("./crashes"), - 1337, - ) - .expect("An error occurred while fuzzing"); -} - -/// Not supported on windows right now -#[cfg(windows)] -fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { - todo!("Example not supported on Windows"); -} - -/// The actual fuzzer -#[cfg(unix)] -fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { - // 'While the stats are state, they are usually used in the broker - which is likely never restarted - let stats = SimpleStats::new(|s| println!("{}", s)); - - // The restarting state will spawn the same process again as child, then restarted it each time it crashes. - let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { - Ok(res) => res, - Err(err) => match err { - Error::ShuttingDown => { - return Ok(()); - } - _ => { - panic!("Failed to setup the restarter: {}", err); - } - }, - }; - - // Create an observation channel using the coverage map - let edges_observer = HitcountsMapObserver::new(unsafe { - StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) - }); - - // Create an observation channel using the cmp map - let cmps_observer = unsafe { StdMapObserver::new_from_ptr("cmps", __lafl_cmp_map, MAP_SIZE) }; - - // Create an observation channel using the allocations map - let allocs_observer = - unsafe { StdMapObserver::new_from_ptr("allocs", __lafl_alloc_map, MAP_SIZE) }; - - // If not restarting, create a State from scratch - let mut state = state.unwrap_or_else(|| { - State::new( - // RNG - StdRand::with_seed(current_nanos()), - // Corpus that will be evolved, we keep it in memory for performance - InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - tuple_list!( - MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), - MaxMapFeedback::new_with_observer(&cmps_observer), - MaxMapFeedback::new_with_observer(&allocs_observer), - ), - // Corpus in which we store solutions (crashes in this example), - // on disk so the user can get them after stopping the fuzzer - OnDiskCorpus::new(objective_dir).unwrap(), - // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new()), - ) - }); - - println!("We're a client, let's fuzz :)"); - - // Create a PNG dictionary if not existing - if state.metadata().get::().is_none() { - state.add_metadata(Tokens::new(vec![ - vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header - "IHDR".as_bytes().to_vec(), - "IDAT".as_bytes().to_vec(), - "PLTE".as_bytes().to_vec(), - "IEND".as_bytes().to_vec(), - ])); - } - - // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); - - let scheduler = RandCorpusScheduler::new(); - // A fuzzer with just one stage and a random policy to get testcasess from the corpus - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); - - // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |buf: &[u8]| { - unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; - ExitKind::Ok - }; - - // Create the executor for an in-process function with just one observer for edge coverage - let mut executor = InProcessExecutor::new( - "in-process(edges)", - &mut harness, - tuple_list!(edges_observer, cmps_observer, allocs_observer), - &mut state, - &mut restarting_mgr, - )?; - - // The actual target run starts here. - // Call LLVMFUzzerInitialize() if present. - unsafe { - if afl_libfuzzer_init() == -1 { - println!("Warning: LLVMFuzzerInitialize failed with -1") - } - } - - // In case the corpus is empty (on first run), reset - if state.corpus().count() < 1 { - state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) - .expect(&format!( - "Failed to load initial corpus at {:?}", - &corpus_dirs - )); - println!("We imported {} inputs from disk.", state.corpus().count()); - } - - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; - - // Never reached - Ok(()) -} diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/test.sh b/fuzzers/libfuzzer_libpng_cmpalloc/test.sh deleted file mode 100755 index 47e79afb3f..0000000000 --- a/fuzzers/libfuzzer_libpng_cmpalloc/test.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -mkdir -p ./crashes - -cargo build --example libfuzzer_libpng_cmpalloc --release || exit 1 -cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf - -# The broker -RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf & -# Give the broker time to spawn -sleep 2 -echo "Spawning client" -# The 1st fuzzer client, pin to cpu 0x1 -RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf 2>/dev/null - -killall .libfuzzer_test.elf -rm -rf ./.libfuzzer_test.elf diff --git a/fuzzers/libfuzzer_runtime/rt.c b/fuzzers/libfuzzer_runtime/rt.c deleted file mode 100644 index eed26db24f..0000000000 --- a/fuzzers/libfuzzer_runtime/rt.c +++ /dev/null @@ -1,189 +0,0 @@ -#include -#include -#include -#include - -#define MAP_SIZE (16*1024) - -int orig_argc; -char **orig_argv; -char **orig_envp; - -uint8_t __lafl_dummy_map[MAP_SIZE]; -size_t __lafl_dummy_map_usize[MAP_SIZE]; - -uint8_t *__lafl_edges_map = __lafl_dummy_map; -uint8_t *__lafl_cmp_map = __lafl_dummy_map; -size_t *__lafl_alloc_map = __lafl_dummy_map_usize; - -uint32_t __lafl_max_edges_size = 0; - -void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { - - uint32_t pos = *guard; - uint16_t val = __lafl_edges_map[pos] + 1; - __lafl_edges_map[pos] = ((uint8_t) val) + (uint8_t) (val >> 8); - //__lafl_edges_map[pos] = 1; - -} - -void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { - - if (start == stop || *start) { return; } - - *(start++) = (++__lafl_max_edges_size) & (MAP_SIZE -1); - - while (start < stop) { - - *start = (++__lafl_max_edges_size) & (MAP_SIZE -1); - start++; - - } - -} - -#define MAX(a, b) \ - ({ \ - \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ - \ - }) - -#if defined(__APPLE__) - #pragma weak __sanitizer_cov_trace_const_cmp1 = __sanitizer_cov_trace_cmp1 - #pragma weak __sanitizer_cov_trace_const_cmp2 = __sanitizer_cov_trace_cmp2 - #pragma weak __sanitizer_cov_trace_const_cmp4 = __sanitizer_cov_trace_cmp4 - #pragma weak __sanitizer_cov_trace_const_cmp8 = __sanitizer_cov_trace_cmp8 -#else -void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__sanitizer_cov_trace_cmp1"))); -void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) - __attribute__((alias("__sanitizer_cov_trace_cmp2"))); -void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) - __attribute__((alias("__sanitizer_cov_trace_cmp4"))); -void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) - __attribute__((alias("__sanitizer_cov_trace_cmp8"))); -#endif - -void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); - -} - -void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); - -} - -void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); - -} - -void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcountll(~(arg1 ^ arg2)))); - -} - -void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { - - uintptr_t rt = (uintptr_t)__builtin_return_address(0); - if (cases[1] == 64) { - - for (uint64_t i = 0; i < cases[0]; i++) { - - uintptr_t k = rt + i; - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcountll(~(val ^ cases[i + 2])))); - - } - - } else { - - for (uint64_t i = 0; i < cases[0]; i++) { - - uintptr_t k = rt + i; - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(val ^ cases[i + 2])))); - - } - - } - -} - -#ifdef _WIN32 -#define posix_memalign(p, a, s) (((*(p)) = _aligned_malloc((s), (a))), *(p) ?0 :errno) -#endif - -void *malloc(size_t size) { - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size); - - // We cannot malloc in malloc. - // Hence, even realloc(NULL, size) would loop in an optimized build. - // We fall back to a stricter allocation function. Fingers crossed. - void *ret = NULL; - posix_memalign(&ret, 1<<6, size); - return ret; - -} - -void *calloc(size_t nmemb, size_t size) { - - size *= nmemb; - - uintptr_t k = (uintptr_t)__builtin_return_address(0); - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - __lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size); - - void *ret = NULL; - posix_memalign(&ret, 1<<6, size); - memset(ret, 0, size); - return ret; - -} - -static void afl_libfuzzer_copy_args(int argc, char** argv, char** envp) { - orig_argc = argc; - orig_argv = argv; - orig_envp = envp; -} - -__attribute__((section(".init_array"))) void (* p_afl_libfuzzer_copy_args)(int,char*[],char*[]) = &afl_libfuzzer_copy_args; - -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); -void afl_libfuzzer_main(); - -int afl_libfuzzer_init() { - - if (LLVMFuzzerInitialize) { - return LLVMFuzzerInitialize(&orig_argc, &orig_argv); - } else { - return 0; - } - -} diff --git a/fuzzers/libfuzzer_runtime/rt.rs b/fuzzers/libfuzzer_runtime/rt.rs deleted file mode 100644 index 13c4b7ea12..0000000000 --- a/fuzzers/libfuzzer_runtime/rt.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![allow(dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, - non_upper_case_globals, unused_assignments, unused_mut)] - -use std::ptr; - -pub const MAP_SIZE: usize = 65536; - -extern "C" { - /// __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); - fn LLVMFuzzerInitialize(argc: *mut libc::c_int, - argv: *mut *mut *mut libc::c_char) -> libc::c_int; - - /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - pub fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; -} - -static mut orig_argc: libc::c_int = 0; -static mut orig_argv: *mut *mut libc::c_char = ptr::null_mut(); -static mut orig_envp: *mut *mut libc::c_char = ptr::null_mut(); - -pub static mut edges_map: [u8; MAP_SIZE] = [0; MAP_SIZE]; -pub static mut cmp_map: [u8; MAP_SIZE] = [0; MAP_SIZE]; -pub static mut max_edges_size: usize = 0; - -#[no_mangle] -pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(mut guard: *mut u32) { - let mut pos: u32 = *guard; - //uint16_t val = __lafl_edges_map[pos] + 1; - //__lafl_edges_map[pos] = ((uint8_t) val) + (uint8_t) (val >> 8); - edges_map[pos as usize] = 1 as u8; -} - -#[no_mangle] -pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, mut stop: *mut u32) { - if start == stop || *start != 0 { return } - - while start < stop { - max_edges_size += 1; - *start = (max_edges_size & (MAP_SIZE -1)) as u32; - start = start.offset(1); - } -} - -unsafe extern "C" fn copy_args_init(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char, mut envp: *mut *mut libc::c_char) { - orig_argc = argc; - orig_argv = argv; - orig_envp = envp; -} - -#[no_mangle] -#[link_section = ".init_array"] -static mut p_copy_args_init: Option ()> = Some(copy_args_init); - -#[no_mangle] -pub unsafe extern "C" fn afl_libfuzzer_init() -> libc::c_int { - if Some(LLVMFuzzerInitialize).is_some() { - LLVMFuzzerInitialize(&mut orig_argc, &mut orig_argv) - } else { - 0 as libc::c_int - } -} diff --git a/fuzzers/libfuzzer_stb_image/build.rs b/fuzzers/libfuzzer_stb_image/build.rs index 27c140f6df..50cb1582bc 100644 --- a/fuzzers/libfuzzer_stb_image/build.rs +++ b/fuzzers/libfuzzer_stb_image/build.rs @@ -14,7 +14,7 @@ fn main() { cc::Build::new() // Use sanitizer coverage to track the edges in the PUT - .flag("-fsanitize-coverage=trace-pc-guard,trace-cmp") + .flag("-fsanitize-coverage=trace-pc-guard") // Take advantage of LTO (needs lld-link set in your cargo config) //.flag("-flto=thin") .flag("-Wno-sign-compare") diff --git a/fuzzers/libfuzzer_windows/.gitignore b/fuzzers/libfuzzer_windows/.gitignore deleted file mode 100644 index a977a2ca5b..0000000000 --- a/fuzzers/libfuzzer_windows/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libpng-* \ No newline at end of file diff --git a/fuzzers/libfuzzer_windows/Cargo.toml b/fuzzers/libfuzzer_windows/Cargo.toml deleted file mode 100644 index 2f212a1e6a..0000000000 --- a/fuzzers/libfuzzer_windows/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "libfuzzer_windows" -version = "0.1.0" -authors = ["Andrea Fioraldi ", "Dominik Maier "] -edition = "2018" -build = "build.rs" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -default = ["std"] -std = [] - -#[profile.release] -#lto = true -#codegen-units = 1 -#opt-level = 3 -#debug = true - -[build-dependencies] -cc = { version = "1.0", features = ["parallel"] } -num_cpus = "1.0" - -[dependencies] -libafl = { path = "../../libafl/" } - -[[example]] -name = "libfuzzer_windows" -path = "./src/fuzzer.rs" -test = false -bench = false diff --git a/fuzzers/libfuzzer_windows/README.md b/fuzzers/libfuzzer_windows/README.md deleted file mode 100644 index f56138c2b5..0000000000 --- a/fuzzers/libfuzzer_windows/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Libfuzzer for libpng - -This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. -To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. -It has been tested on Linux. - -## Build - -To build this example, run `cargo build --example libfuzzer_libpng --release`. -This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. -Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. -Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. - -## Run - -The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. - -Each following execution will run a fuzzer client. -As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). -This means each client will start itself again to listen for crashes and timeouts. -By restarting the actual fuzzer, it can recover from these exit conditions. - -In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). - -For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/libfuzzer_windows/build.rs b/fuzzers/libfuzzer_windows/build.rs deleted file mode 100644 index bb08b22443..0000000000 --- a/fuzzers/libfuzzer_windows/build.rs +++ /dev/null @@ -1,58 +0,0 @@ -// build.rs - -#[cfg(windows)] -use std::env; - -#[cfg(not(windows))] -fn main() { - println!("cargo:warning=Skipping libpng windows example on non-Windows"); - return; -} - -#[cfg(windows)] -fn main() { - let out_dir = env::var_os("OUT_DIR").unwrap(); - let out_dir = out_dir.to_string_lossy().to_string(); - - println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); - println!("cargo:rerun-if-changed=harness.cc"); - - // Enforce clang for its -fsanitize-coverage support. - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); - /*let ldflags = match env::var("LDFLAGS") { - Ok(val) => val, - Err(_) => "".to_string(), - };*/ - - cc::Build::new() - .file("../libfuzzer_runtime/rt.c") - .compile("libfuzzer-sys"); - - cc::Build::new() - .cpp(true) - .flag("-fsanitize-coverage=trace-pc-guard") - // .define("HAS_DUMMY_CRASH", "1") - .flag("-Wno-void-pointer-to-int-cast") - .flag("-Wno-pointer-to-int-cast") - .flag("-Wno-int-to-pointer-cast") - .flag("-Wno-sign-compare") - .flag("-Wno-format") - .flag("-Wno-unused-variable") - .file("./harness.cc") - .compile("windows-harness"); - - println!("cargo:rustc-link-search=native={}", &out_dir); - //println!("cargo:rustc-link-search=native={}/.libs", &libpng); - //println!("cargo:rustc-link-lib=static=png16"); - - //Deps for libpng: -pthread -lz -lm - //println!("cargo:rustc-link-lib=dylib=m"); - //println!("cargo:rustc-link-lib=dylib=z"); - - //For the C++ harness - //must by dylib for android - //println!("cargo:rustc-link-lib=dylib=stdc++"); - - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/fuzzers/libfuzzer_windows/corpus/not_kitty.png b/fuzzers/libfuzzer_windows/corpus/not_kitty.png deleted file mode 100644 index eff7c1707b936a8f8df725814f604d454b78b5c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X_yc@GT+_~+`TzevkY_wIZRYx+5&y#hyq+?%!C8<`)MX5lF!N|bSRM)^r*U&J;z}U*bz{;0L z1Vuw`eoAIqC5i?kD`P_|6GMoGiCWXn12ss3YzWRzD=AMbN@Z|N$xljE@XSq2PYp^< WOsOn9nQ8-6#Ng@b=d#Wzp$PyV*n0l} diff --git a/fuzzers/libfuzzer_windows/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_windows/corpus/not_kitty_gamma.png deleted file mode 100644 index 939d9d29a9b9f95bac5e9a72854361ee85469921..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmTQ929t;oCfmw1AIbU z)6Sgv|NlRbXFM})=KnKxKI=t+9LW;bh?3y^w370~qErUQl>DSr1<%~X^wgl##FWay zlc_d9MbVxvjv*GO?@o5)YH;9THa`3B|5>?^8?LvjJ}xLe>!7e@k)r^sLedir0mCVe z=5sMjEm$*~tHD+}{NS_$nMdb|ABqg-@UGMMsZ=uY-X%Cq@&3vmZ%&@H{P?6&+U!yq VvuXWlo?M_c44$rjF6*2UngF4cP+$N6 diff --git a/fuzzers/libfuzzer_windows/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_windows/corpus/not_kitty_icc.png deleted file mode 100644 index f0c7804d99829cc6307c1c6ae9915cf42d555414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 427 zcmV;c0aX5pP)9xSWu9|B*4Isn^#g47m^r~thH)GiR<@yX0fO)OF<2Kt#qCldyUF#H?{4jV?XGw9)psxE&K1B1m^ z1_tH{2(hG@3=G>_85ksPA;eS`Ffj19FfeR8pIlm01~rBeWCZ{dbvfq;rA3DT000kA zOjJc?%*_A){{R30GnreSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM003J_L_t(I%iWVf3V=Wi12fJ3|IHp$*hSlV@t||fKp?cDK@bHXV&o_g zF_hw;3ILUGteXmeJsVfSmcVJno)^MdQwU3bFHCtNG)uY>mLcD%`0UBaIq~Fq8#dBr V12uok3~c}a002ovPDHLkV1nKBo!S5Z diff --git a/fuzzers/libfuzzer_windows/harness.cc b/fuzzers/libfuzzer_windows/harness.cc deleted file mode 100644 index 65365fdc8c..0000000000 --- a/fuzzers/libfuzzer_windows/harness.cc +++ /dev/null @@ -1,231 +0,0 @@ -#include -#include -#include - -#define STBI_ASSERT(x) -#define STBI_NO_SIMD -#define STBI_NO_LINEAR -#define STBI_NO_STDIO -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -int target_func(const uint8_t *buf, size_t size) { - - /*printf("BUF (%ld): ", size); - for (int i = 0; i < size; i++) { - printf("%02X", buf[i]); - } - printf("\n");*/ - - if (size == 0) return 0; - - switch (buf[0]) { - - case 1: - if (buf[1] == 0x44) { - //__builtin_trap(); - return 8; - } - - break; - case 0xff: - if (buf[2] == 0xff) { - if (buf[1] == 0x44) { - //*(char *)(0xdeadbeef) = 1; - return 9; - } - } - - break; - default: - break; - - } - - return 1; - -} - -int parse_pe(const uint8_t *data, int size) -{ - HANDLE file = NULL; - DWORD fileSize = NULL; - DWORD bytesRead = NULL; - LPVOID fileData = NULL; - PIMAGE_DOS_HEADER dosHeader = {}; - PIMAGE_NT_HEADERS imageNTHeaders = {}; - PIMAGE_SECTION_HEADER sectionHeader = {}; - PIMAGE_SECTION_HEADER importSection = {}; - IMAGE_IMPORT_DESCRIPTOR* importDescriptor = {}; - PIMAGE_THUNK_DATA thunkData = {}; - DWORD thunk = NULL; - DWORD rawOffset = NULL; - - // allocate heap - fileSize = size; - fileData = (void *)data; - - // IMAGE_DOS_HEADER - dosHeader = (PIMAGE_DOS_HEADER)fileData; - - printf("******* DOS HEADER *******\n"); - printf("\t0x%x\t\tMagic number\n", dosHeader->e_magic); - /* - printf("\t0x%x\t\tBytes on last page of file\n", dosHeader->e_cblp); - printf("\t0x%x\t\tPages in file\n", dosHeader->e_cp); - printf("\t0x%x\t\tRelocations\n", dosHeader->e_crlc); - printf("\t0x%x\t\tSize of header in paragraphs\n", dosHeader->e_cparhdr); - printf("\t0x%x\t\tMinimum extra paragraphs needed\n", dosHeader->e_minalloc); - printf("\t0x%x\t\tMaximum extra paragraphs needed\n", dosHeader->e_maxalloc); - printf("\t0x%x\t\tInitial (relative) SS value\n", dosHeader->e_ss); - printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp); - printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp); - printf("\t0x%x\t\tChecksum\n", dosHeader->e_csum); - printf("\t0x%x\t\tInitial IP value\n", dosHeader->e_ip); - printf("\t0x%x\t\tInitial (relative) CS value\n", dosHeader->e_cs); - printf("\t0x%x\t\tFile address of relocation table\n", dosHeader->e_lfarlc); - printf("\t0x%x\t\tOverlay number\n", dosHeader->e_ovno); - printf("\t0x%x\t\tOEM identifier (for e_oeminfo)\n", dosHeader->e_oemid); - printf("\t0x%x\t\tOEM information; e_oemid specific\n", dosHeader->e_oeminfo); - printf("\t0x%x\t\tFile address of new exe header\n", dosHeader->e_lfanew); -*/ - // IMAGE_NT_HEADERS - imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)fileData + dosHeader->e_lfanew); - /* - printf("\n******* NT HEADERS *******\n"); - printf("\t%x\t\tSignature\n", imageNTHeaders->Signature); - - // FILE_HEADER - printf("\n******* FILE HEADER *******\n"); - printf("\t0x%x\t\tMachine\n", imageNTHeaders->FileHeader.Machine); - printf("\t0x%x\t\tNumber of Sections\n", imageNTHeaders->FileHeader.NumberOfSections); - printf("\t0x%x\tTime Stamp\n", imageNTHeaders->FileHeader.TimeDateStamp); - printf("\t0x%x\t\tPointer to Symbol Table\n", imageNTHeaders->FileHeader.PointerToSymbolTable); - printf("\t0x%x\t\tNumber of Symbols\n", imageNTHeaders->FileHeader.NumberOfSymbols); - printf("\t0x%x\t\tSize of Optional Header\n", imageNTHeaders->FileHeader.SizeOfOptionalHeader); - printf("\t0x%x\t\tCharacteristics\n", imageNTHeaders->FileHeader.Characteristics); - - // OPTIONAL_HEADER - printf("\n******* OPTIONAL HEADER *******\n"); - printf("\t0x%x\t\tMagic\n", imageNTHeaders->OptionalHeader.Magic); - printf("\t0x%x\t\tMajor Linker Version\n", imageNTHeaders->OptionalHeader.MajorLinkerVersion); - printf("\t0x%x\t\tMinor Linker Version\n", imageNTHeaders->OptionalHeader.MinorLinkerVersion); - printf("\t0x%x\t\tSize Of Code\n", imageNTHeaders->OptionalHeader.SizeOfCode); - printf("\t0x%x\t\tSize Of Initialized Data\n", imageNTHeaders->OptionalHeader.SizeOfInitializedData); - printf("\t0x%x\t\tSize Of UnInitialized Data\n", imageNTHeaders->OptionalHeader.SizeOfUninitializedData); - printf("\t0x%x\t\tAddress Of Entry Point (.text)\n", imageNTHeaders->OptionalHeader.AddressOfEntryPoint); - printf("\t0x%x\t\tBase Of Code\n", imageNTHeaders->OptionalHeader.BaseOfCode); - //printf("\t0x%x\t\tBase Of Data\n", imageNTHeaders->OptionalHeader.BaseOfData); - printf("\t0x%x\t\tImage Base\n", imageNTHeaders->OptionalHeader.ImageBase); - printf("\t0x%x\t\tSection Alignment\n", imageNTHeaders->OptionalHeader.SectionAlignment); - printf("\t0x%x\t\tFile Alignment\n", imageNTHeaders->OptionalHeader.FileAlignment); - printf("\t0x%x\t\tMajor Operating System Version\n", imageNTHeaders->OptionalHeader.MajorOperatingSystemVersion); - printf("\t0x%x\t\tMinor Operating System Version\n", imageNTHeaders->OptionalHeader.MinorOperatingSystemVersion); - printf("\t0x%x\t\tMajor Image Version\n", imageNTHeaders->OptionalHeader.MajorImageVersion); - printf("\t0x%x\t\tMinor Image Version\n", imageNTHeaders->OptionalHeader.MinorImageVersion); - printf("\t0x%x\t\tMajor Subsystem Version\n", imageNTHeaders->OptionalHeader.MajorSubsystemVersion); - printf("\t0x%x\t\tMinor Subsystem Version\n", imageNTHeaders->OptionalHeader.MinorSubsystemVersion); - printf("\t0x%x\t\tWin32 Version Value\n", imageNTHeaders->OptionalHeader.Win32VersionValue); - printf("\t0x%x\t\tSize Of Image\n", imageNTHeaders->OptionalHeader.SizeOfImage); - printf("\t0x%x\t\tSize Of Headers\n", imageNTHeaders->OptionalHeader.SizeOfHeaders); - printf("\t0x%x\t\tCheckSum\n", imageNTHeaders->OptionalHeader.CheckSum); - printf("\t0x%x\t\tSubsystem\n", imageNTHeaders->OptionalHeader.Subsystem); - printf("\t0x%x\t\tDllCharacteristics\n", imageNTHeaders->OptionalHeader.DllCharacteristics); - printf("\t0x%x\t\tSize Of Stack Reserve\n", imageNTHeaders->OptionalHeader.SizeOfStackReserve); - printf("\t0x%x\t\tSize Of Stack Commit\n", imageNTHeaders->OptionalHeader.SizeOfStackCommit); - printf("\t0x%x\t\tSize Of Heap Reserve\n", imageNTHeaders->OptionalHeader.SizeOfHeapReserve); - printf("\t0x%x\t\tSize Of Heap Commit\n", imageNTHeaders->OptionalHeader.SizeOfHeapCommit); - printf("\t0x%x\t\tLoader Flags\n", imageNTHeaders->OptionalHeader.LoaderFlags); - printf("\t0x%x\t\tNumber Of Rva And Sizes\n", imageNTHeaders->OptionalHeader.NumberOfRvaAndSizes); - - // DATA_DIRECTORIES - printf("\n******* DATA DIRECTORIES *******\n"); - printf("\tExport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[0].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[0].Size); - printf("\tImport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[1].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[1].Size); - */ -return 0; - // SECTION_HEADERS - printf("\n******* SECTION HEADERS *******\n"); - // get offset to first section headeer - DWORD sectionLocation = (DWORD)imageNTHeaders + sizeof(DWORD) + (DWORD)(sizeof(IMAGE_FILE_HEADER)) + (DWORD)imageNTHeaders->FileHeader.SizeOfOptionalHeader; - DWORD sectionSize = (DWORD)sizeof(IMAGE_SECTION_HEADER); - - // get offset to the import directory RVA - DWORD importDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; - // print section data - for (int i = 0; i < imageNTHeaders->FileHeader.NumberOfSections; i++) { - sectionHeader = (PIMAGE_SECTION_HEADER)sectionLocation; - printf("\t%s\n", sectionHeader->Name); - printf("\t\t0x%x\t\tVirtual Size\n", sectionHeader->Misc.VirtualSize); - printf("\t\t0x%x\t\tVirtual Address\n", sectionHeader->VirtualAddress); - /* - printf("\t\t0x%x\t\tSize Of Raw Data\n", sectionHeader->SizeOfRawData); - printf("\t\t0x%x\t\tPointer To Raw Data\n", sectionHeader->PointerToRawData); - printf("\t\t0x%x\t\tPointer To Relocations\n", sectionHeader->PointerToRelocations); - printf("\t\t0x%x\t\tPointer To Line Numbers\n", sectionHeader->PointerToLinenumbers); - printf("\t\t0x%x\t\tNumber Of Relocations\n", sectionHeader->NumberOfRelocations); - printf("\t\t0x%x\t\tNumber Of Line Numbers\n", sectionHeader->NumberOfLinenumbers); - printf("\t\t0x%x\tCharacteristics\n", sectionHeader->Characteristics); - */ - // save section that contains import directory table - if (importDirectoryRVA >= sectionHeader->VirtualAddress && importDirectoryRVA < sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize) { - importSection = sectionHeader; - } - sectionLocation += sectionSize; - } - - // get file offset to import table - rawOffset = (DWORD)fileData + importSection->PointerToRawData; - - // get pointer to import descriptor's file offset. Note that the formula for calculating file offset is: imageBaseAddress + pointerToRawDataOfTheSectionContainingRVAofInterest + (RVAofInterest - SectionContainingRVAofInterest.VirtualAddress) - importDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)(rawOffset + (imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress - importSection->VirtualAddress)); - - printf("\n******* DLL IMPORTS *******\n"); - for (; importDescriptor->Name != 0; importDescriptor++) { - // imported dll modules - printf("\t%s\n", rawOffset + (importDescriptor->Name - importSection->VirtualAddress)); - thunk = importDescriptor->OriginalFirstThunk == 0 ? importDescriptor->FirstThunk : importDescriptor->OriginalFirstThunk; - thunkData = (PIMAGE_THUNK_DATA)(rawOffset + (thunk - importSection->VirtualAddress)); - - // dll exported functions - for (; thunkData->u1.AddressOfData != 0; thunkData++) { - //a cheap and probably non-reliable way of checking if the function is imported via its ordinal number ¯\_(ツ)_/¯ - if (thunkData->u1.AddressOfData > 0x80000000) { - //show lower bits of the value to get the ordinal ¯\_(ツ)_/¯ - printf("\t\tOrdinal: %x\n", (WORD)thunkData->u1.AddressOfData); - } else { - printf("\t\t%s\n", (rawOffset + (thunkData->u1.AddressOfData - importSection->VirtualAddress + 2))); - } - } - } - - return 0; -} - - - -int load_stbi(const uint8_t *data, int size) -{ - int w; - int h; - int channels; - - const unsigned char * img = stbi_load_from_memory(data, size, &w, &h, &channels, 0); - if (img) { stbi_image_free((void *)img); } - // STBI_FREE((void *)img); } - - return 0; -} - -extern "C" -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - return target_func(Data, Size); - - if(Size > 0x4000) return 0; - - int size = Size; - const unsigned char * data = Data; - return load_stbi(data, size); - //return parse_pe(data, size); -} - diff --git a/fuzzers/libfuzzer_windows/src/fuzzer.rs b/fuzzers/libfuzzer_windows/src/fuzzer.rs deleted file mode 100644 index 0366b8486e..0000000000 --- a/fuzzers/libfuzzer_windows/src/fuzzer.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts -//! The example harness is built for libpng. - -#[cfg(windows)] -use std::{env, path::PathBuf}; - -#[cfg(windows)] -use libafl::{ - bolts::{shmem::Win32ShMem, tuples::tuple_list}, - corpus::{ - Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, - QueueCorpusScheduler, - }, - events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, ExitKind}, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, - fuzzer::{Fuzzer, StdFuzzer}, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - mutators::token_mutations::Tokens, - observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, - stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, State}, - stats::SimpleStats, - utils::{current_nanos, StdRand}, - Error, -}; - -/// We will interact with a C++ target, so use external c functionality -#[cfg(windows)] -extern "C" { - /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; - - // afl_libfuzzer_init calls LLVMFUzzerInitialize() - fn afl_libfuzzer_init() -> i32; - - static __lafl_edges_map: *mut u8; - static __lafl_cmp_map: *mut u8; - static __lafl_max_edges_size: u32; -} - -/// The main fn, usually parsing parameters, and starting the fuzzer -pub fn main() { - // Registry the metadata types used in this fuzzer - // Needed only on no_std - //RegistryBuilder::register::(); - - #[cfg(windows)] - println!( - "Workdir: {:?}", - env::current_dir().unwrap().to_string_lossy().to_string() - ); - - #[cfg(not(windows))] - todo!("Example currently only supports Windows."); - - #[cfg(windows)] - fuzz( - vec![PathBuf::from("./corpus")], - PathBuf::from("./crashes"), - 1337, - ) - .expect("An error occurred while fuzzing"); -} - -/// Not supported on unix right now -//#[cfg(cfg)] -//fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { -// todo!("Example not supported on Unix"); -//} - -/// The actual fuzzer -#[cfg(windows)] -fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { - // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |buf: &[u8]| { - unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; - ExitKind::Ok - }; - - // 'While the stats are state, they are usually used in the broker - which is likely never restarted - let stats = SimpleStats::new(|s| println!("{}", s)); - - // The restarting state will spawn the same process again as child, then restarted it each time it crashes. - let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, Win32ShMem, _>(stats, broker_port) { - Ok(res) => res, - Err(err) => match err { - Error::ShuttingDown => { - return Ok(()); - } - _ => { - panic!("Failed to setup the restarter: {}", err); - } - }, - }; - - // Create an observation channel using the coverage map - let edges_observer = HitcountsMapObserver::new(unsafe { - StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) - }); - - // If not restarting, create a State from scratch - let mut state = state.unwrap_or_else(|| { - State::new( - // RNG - StdRand::with_seed(current_nanos()), - // Corpus that will be evolved, we keep it in memory for performance - InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - tuple_list!( - MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), - TimeFeedback::new() - ), - // Corpus in which we store solutions (crashes in this example), - // on disk so the user can get them after stopping the fuzzer - OnDiskCorpus::new(objective_dir).unwrap(), - // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()), - ) - }); - - println!("We're a client, let's fuzz :)"); - - // Create a PNG dictionary if not existing - if state.metadata().get::().is_none() { - state.add_metadata(Tokens::new(vec![ - vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header - "IHDR".as_bytes().to_vec(), - "IDAT".as_bytes().to_vec(), - "PLTE".as_bytes().to_vec(), - "IEND".as_bytes().to_vec(), - ])); - } - - // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); - let stage = StdMutationalStage::new(mutator); - - // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); - let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); - - // Create the executor for an in-process function with just one observer for edge coverage - let mut executor = InProcessExecutor::new( - "in-process(edges)", - &mut harness, - tuple_list!(edges_observer, TimeObserver::new("time")), - &mut state, - &mut restarting_mgr, - )?; - - // The actual target run starts here. - // Call LLVMFUzzerInitialize() if present. - unsafe { - if afl_libfuzzer_init() == -1 { - println!("Warning: LLVMFuzzerInitialize failed with -1") - } - } - - // In case the corpus is empty (on first run), reset - if state.corpus().count() < 1 { - state - .load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs) - .expect(&format!( - "Failed to load initial corpus at {:?}", - &corpus_dirs - )); - println!("We imported {} inputs from disk.", state.corpus().count()); - } - - fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?; - - // Never reached - Ok(()) -} diff --git a/fuzzers/libfuzzer_windows/stb_image.h b/fuzzers/libfuzzer_windows/stb_image.h deleted file mode 100644 index 6542ede682..0000000000 --- a/fuzzers/libfuzzer_windows/stb_image.h +++ /dev/null @@ -1,7762 +0,0 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif - - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif -#endif - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) - return 0; - -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - int out_size = 0; - int delays_size = 0; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } - else { - out = (stbi_uc*) tmp; - out_size = layers * stride; - } - - if (delays) { - *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - delays_size = layers * sizeof(int); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - out_size = layers * stride; - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - delays_size = layers * sizeof(int); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ \ No newline at end of file diff --git a/fuzzers/libfuzzer_windows/test.bat b/fuzzers/libfuzzer_windows/test.bat deleted file mode 100644 index eb4fb3e64b..0000000000 --- a/fuzzers/libfuzzer_windows/test.bat +++ /dev/null @@ -1,20 +0,0 @@ -mkdir crashes -del .\.libfuzzer_test.elf - -cargo build --example libfuzzer_windows --release -timeout /T 1 -cp ..\..\target\release\examples\libfuzzer_windows.exe .\.libfuzzer_test.exe -timeout /T 1 - -# The broker -start .\.libfuzzer_test.exe -# Give the broker time to spawn -timeout /T 1 -echo "Spawning client" -start .\.libfuzzer_test.exe -# .\.libfuzzer_test.exe > nul - -timeout /T 10 -echo "Finished fuzzing for a bit" -TASKKILL /IM .libfuzzer_test.exe -del .libfuzzer_test.exe From b4e6115d4f05b8b6af61e9bf55d07552f7d2c405 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 29 Mar 2021 15:57:27 +0200 Subject: [PATCH 035/104] fixes for pcguard and value profile --- Cargo.toml | 6 +----- libafl_targets/src/pcguard.rs | 8 ++++---- libafl_targets/src/value_profile.rs | 6 +++--- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cc9355c666..6710352a0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,15 +10,11 @@ members = [ "libafl_derive", "libafl_cc", "libafl_targets", - - #example fuzzers - "fuzzers/libfuzzer_libmozjpeg", - "fuzzers/libfuzzer_libpng_cmpalloc", - "fuzzers/libfuzzer_windows", ] exclude = [ "fuzzers/libfuzzer_libpng", "fuzzers/libfuzzer_stb_image", + "fuzzers/libfuzzer_libmozjpeg", "fuzzers/frida_libpng", "fuzzers/qemu_user", ] diff --git a/libafl_targets/src/pcguard.rs b/libafl_targets/src/pcguard.rs index a90b3a659d..b7fff8bd5e 100644 --- a/libafl_targets/src/pcguard.rs +++ b/libafl_targets/src/pcguard.rs @@ -4,10 +4,10 @@ compile_error!( ); // TODO compile time flag -pub const MAP_SIZE: usize = 65536; +pub const EDGES_MAP_SIZE: usize = 65536; -pub static mut EDGES_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; -//pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut EDGES_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; +//pub static mut CMP_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; pub static mut MAX_EDGES_NUM: usize = 0; #[no_mangle] @@ -32,7 +32,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32 while start < stop { MAX_EDGES_NUM += 1; - *start = (MAX_EDGES_NUM & (MAP_SIZE - 1)) as u32; + *start = (MAX_EDGES_NUM & (EDGES_MAP_SIZE - 1)) as u32; start = start.offset(1); } } diff --git a/libafl_targets/src/value_profile.rs b/libafl_targets/src/value_profile.rs index d5aabd8872..b5f0d2981c 100644 --- a/libafl_targets/src/value_profile.rs +++ b/libafl_targets/src/value_profile.rs @@ -1,8 +1,8 @@ // TODO compile time flag -pub const MAP_SIZE: usize = 65536; +pub const CMP_MAP_SIZE: usize = 65536; #[no_mangle] -pub static mut libafl_cmp_map: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut libafl_cmp_map: [u8; CMP_MAP_SIZE] = [0; CMP_MAP_SIZE]; pub use libafl_cmp_map as CMP_MAP; @@ -16,7 +16,7 @@ extern { pub unsafe extern "C" fn __sanitizer_cov_trace_cmp1(arg1: u8, arg2: u8) { let mut pos = return_address(); pos = (pos >> 4) ^ (pos << 8); - pos &= MAP_SIZE - 1; + pos &= CMP_MAP_SIZE - 1; *CMP_MAP.get_unchecked_mut(pos) = core::cmp::max(*CMP_MAP.get_unchecked(pos), (!(arg1 ^ arg2)).count_ones() as u8); } */ From 144d9dff9d047ff44383f11a7f49d9fc4de9854f Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 29 Mar 2021 15:57:58 +0200 Subject: [PATCH 036/104] doc --- docs/src/core_concepts/core_concepts.md | 2 ++ docs/src/getting_started/crates.md | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/src/core_concepts/core_concepts.md b/docs/src/core_concepts/core_concepts.md index c587b44432..46b3960f44 100644 --- a/docs/src/core_concepts/core_concepts.md +++ b/docs/src/core_concepts/core_concepts.md @@ -1 +1,3 @@ # Core Concepts + + diff --git a/docs/src/getting_started/crates.md b/docs/src/getting_started/crates.md index c6b907532e..1745da0494 100644 --- a/docs/src/getting_started/crates.md +++ b/docs/src/getting_started/crates.md @@ -5,9 +5,9 @@ Each one has its self-contained purpose, and the user may not need to use all of Following the naming convention of the folders in the project's root, they are: -- libafl -- libafl_derive -- libafl_targets -- libafl_cc +- libafl, the main crate that contains all the components needed to build a fuzzer +- libafl_derive, a proc-macro crate paired with the libafl crate +- libafl_targets, a crate that expose, under feature flags, pieces of code to interact with targets +- libafl_cc, a library that provide some utils to wrap compilers and create source level fuzzers. From bd9aa89753d5312c6dc983fb633274ddef3b09de Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 29 Mar 2021 16:07:56 +0200 Subject: [PATCH 037/104] libfuzzer_libmozjpeg readme --- fuzzers/libfuzzer_libmozjpeg/README.md | 36 ++++++++++++++++---------- libafl_targets/build.rs | 6 ++--- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/fuzzers/libfuzzer_libmozjpeg/README.md b/fuzzers/libfuzzer_libmozjpeg/README.md index 9a823669c6..b302a1b2bb 100644 --- a/fuzzers/libfuzzer_libmozjpeg/README.md +++ b/fuzzers/libfuzzer_libmozjpeg/README.md @@ -1,16 +1,32 @@ # Libfuzzer for libmozjpeg This folder contains an example fuzzer for libmozjpeg, using LLMP for fast multi-process fuzzing and crash detection. +Alongside the traditional edge coverage, this example shows how to use a value-profile like feedback to bypass CMPs and an allocations size maximization feedback to spot patological inputs in terms of memory usage. It has been tested on Linux. -https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz - ## Build -To build this example, run `cargo build --example libfuzzer_libmozjpeg --release`. -This will call (the build.rs)[./builld.rs], which in turn downloads a libmozjpeg archive from the web. -Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the c++ harness)[./harness.cc] and the instrumented `libmozjpeg`. -Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libmozjpeg`. +To build this example, run `cargo build --release`. +This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer. the SanitizerCoverage runtime functions for edges and value-profile feedbacks and the `hook_allocs.c` C file that hooks the allocator to report the size to the fuzzer. +In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target. + +Then download the mozjpeg source tarball from https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz and unpack the archive. + +Now compile it with: + +``` +cd mozjpeg-4.0.3 +cmake --disable-shared . -DCMAKE_C_COMPILER=/path/to/libfuzzer_mozjpeg/target/release/cc -DCMAKE_CXX_COMPILER=/path/to/libfuzzer_mozjpeg/target/release/cxx -G "Unix Makefiles" +make -j `nproc` +``` + +Now, we have to build the libfuzzer harness and link all togheter to create our fuzzer binary. + +``` +/path/to/libfuzzer_libpng/target/debug/cxx /path/to/libfuzzer_libpng/harness.cc mozjpeg-4.0.3/*.a -I mozjpeg-4.0.3/ -o fuzzer +``` + +Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`. ## Run @@ -21,10 +37,4 @@ As this example uses in-process fuzzing, we added a Restarting Event Manager (`s This means each client will start itself again to listen for crashes and timeouts. By restarting the actual fuzzer, it can recover from these exit conditions. -For convenience, you may just run `./test.sh` in this folder or: - -broker.sh - starts the broker -start.sh - starts as many clients as there are cores -stop.sh - stop everything - - +In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 8062540719..250196da36 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -6,8 +6,8 @@ use std::path::Path; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = out_dir.to_string_lossy().to_string(); - let src_dir = Path::new("src"); //let out_dir_path = Path::new(&out_dir); + let _src_dir = Path::new("src"); //std::env::set_var("CC", "clang"); //std::env::set_var("CXX", "clang++"); @@ -17,7 +17,7 @@ fn main() { println!("cargo:rerun-if-changed=src/libfuzzer_compatibility.c"); cc::Build::new() - .file(src_dir.join("libfuzzer_compatibility.c")) + .file(_src_dir.join("libfuzzer_compatibility.c")) .compile("libfuzzer_compatibility"); } @@ -26,7 +26,7 @@ fn main() { println!("cargo:rerun-if-changed=src/value_profile.c"); cc::Build::new() - .file(src_dir.join("value_profile.c")) + .file(_src_dir.join("value_profile.c")) .compile("value_profile"); } From d889c8c82621251451574e767f2078615340be57 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 29 Mar 2021 16:13:46 +0200 Subject: [PATCH 038/104] libfuzzer_stb_image readme --- fuzzers/libfuzzer_stb_image/Cargo.toml | 2 +- fuzzers/libfuzzer_stb_image/README.md | 32 +++++--------------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/fuzzers/libfuzzer_stb_image/Cargo.toml b/fuzzers/libfuzzer_stb_image/Cargo.toml index 1ec1ab28ba..3b3cd143ce 100644 --- a/fuzzers/libfuzzer_stb_image/Cargo.toml +++ b/fuzzers/libfuzzer_stb_image/Cargo.toml @@ -17,7 +17,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "value_profile", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "libfuzzer"] } [build-dependencies] cc = { version = "1.0", features = ["parallel"] } diff --git a/fuzzers/libfuzzer_stb_image/README.md b/fuzzers/libfuzzer_stb_image/README.md index acb7426f8a..1daf0a4c47 100644 --- a/fuzzers/libfuzzer_stb_image/README.md +++ b/fuzzers/libfuzzer_stb_image/README.md @@ -1,38 +1,18 @@ -# Libfuzzer for libpng +# Libfuzzer for stb_image -This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. -To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. -It has been tested on Linux. +This folder contains an example fuzzer for stb_image, using LLMP for fast multi-process fuzzing and crash detection. +It has been tested on Linux and Windows. ## Build To build this example, run `cargo build --release`. -This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. -In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target. +This will build the the fuzzer (src/main.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback as a standalone binary. -Then download libpng from https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz and unpack the archive. - -Now compile it with: - -``` -cd libpng-1.6.37 -./configure -make CC=/path/to/libfuzzer_libpng/target/release/cc -j `nproc` -``` - -You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`. - -Now, we have to build the libfuzzer harness and link all togheter to create our fuzzer binary. - -``` -/path/to/libfuzzer_libpng/target/debug/cxx /path/to/libfuzzer_libpng/harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer -lz -lm -``` - -Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`. +Unlike the libpng example, in this example the harness (that entirely includes the program under test) is compiled in the `build.rs` file while building the crate, and linked with the fuzzer by cargo when producing the final binary, `target/release/libfuzzer_stb_image`. ## Run -The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. +The first time you run the binary (`target/release/libfuzzer_stb_image`), the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Each following execution will run a fuzzer client. As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). From 1c9ea4138e2e8ab052d9bad5d7a1580aca262202 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 29 Mar 2021 19:49:24 +0200 Subject: [PATCH 039/104] format --- libafl_targets/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 250196da36..5e95ab0cf2 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -20,7 +20,7 @@ fn main() { .file(_src_dir.join("libfuzzer_compatibility.c")) .compile("libfuzzer_compatibility"); } - + #[cfg(feature = "value_profile")] { println!("cargo:rerun-if-changed=src/value_profile.c"); From 0f17fa3fc92f2ff890b7168851048d0a1f619a68 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 31 Mar 2021 11:24:52 +0200 Subject: [PATCH 040/104] docs --- docs/src/SUMMARY.md | 5 +++- docs/src/core_concepts/core_concepts.md | 4 +++ docs/src/core_concepts/corpus.md | 1 + docs/src/core_concepts/executor.md | 9 +++++++ docs/src/core_concepts/feedback.md | 3 +++ docs/src/core_concepts/input.md | 1 + docs/src/core_concepts/observer.md | 4 +++ docs/src/getting_started/crates.md | 35 ++++++++++++++++++++++--- libafl/Cargo.toml | 1 - 9 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 docs/src/core_concepts/corpus.md create mode 100644 docs/src/core_concepts/feedback.md create mode 100644 docs/src/core_concepts/input.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index bcd4ffea37..9cc0cbf517 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -12,5 +12,8 @@ - [Baby Fuzzer](./baby_fuzzer.md) - [Core Concepts](./core_concepts/core_concepts.md) - - [Executor](./core_concepts/executor.md) - [Observer](./core_concepts/observer.md) + - [Executor](./core_concepts/executor.md) + - [Feedback](./core_concepts/feedback.md) + - [Input](./core_concepts/input.md) + - [Corpus](./core_concepts/corpus.md) diff --git a/docs/src/core_concepts/core_concepts.md b/docs/src/core_concepts/core_concepts.md index 46b3960f44..2da1afbb02 100644 --- a/docs/src/core_concepts/core_concepts.md +++ b/docs/src/core_concepts/core_concepts.md @@ -1,3 +1,7 @@ # Core Concepts +LibAFL is designed around some core concepts that we think can effectively abstract most of the other fuzzers designs. + +In this chapter, we discuss these concepts, provide some examples related to other fuzzers and some details how the defined entities maps to the code in Rust. + diff --git a/docs/src/core_concepts/corpus.md b/docs/src/core_concepts/corpus.md new file mode 100644 index 0000000000..361eb33c64 --- /dev/null +++ b/docs/src/core_concepts/corpus.md @@ -0,0 +1 @@ +# Corpus diff --git a/docs/src/core_concepts/executor.md b/docs/src/core_concepts/executor.md index 3fddc32644..965280e07e 100644 --- a/docs/src/core_concepts/executor.md +++ b/docs/src/core_concepts/executor.md @@ -1 +1,10 @@ # Executor + +In different fuzzers, the concept of executing the program under test each run is now always the same. +For instance, for in-memory fuzzers like libFuzzer an execution is a call to an harness function, for hypervisor-based fuzzers like [kAFL](https://github.com/IntelLabs/kAFL) instead an entire operating system is started from a snapshot each run. + +In our model, an Executor is the entity that defines not only how to execute the target, but all the volatile operations that are related to just a single run of the target. + +So the Executor is for instance reponsible to inform the program about the input that the fuzzer wants to use in the run, writing to a memory location for instance or passing it as a parameter to the harness function. + +It also holds a set of Observers, as thay are related to just a single run of the target. diff --git a/docs/src/core_concepts/feedback.md b/docs/src/core_concepts/feedback.md new file mode 100644 index 0000000000..c4a1060c53 --- /dev/null +++ b/docs/src/core_concepts/feedback.md @@ -0,0 +1,3 @@ +# Feedback + + diff --git a/docs/src/core_concepts/input.md b/docs/src/core_concepts/input.md new file mode 100644 index 0000000000..135b6af33f --- /dev/null +++ b/docs/src/core_concepts/input.md @@ -0,0 +1 @@ +# Input diff --git a/docs/src/core_concepts/observer.md b/docs/src/core_concepts/observer.md index 94e0f8b987..65ba1ec920 100644 --- a/docs/src/core_concepts/observer.md +++ b/docs/src/core_concepts/observer.md @@ -1 +1,5 @@ # Observer + +An Observer, or Observation Channel, is an entity that provide an information related to a single run of the program under test to the fuzzer. + + diff --git a/docs/src/getting_started/crates.md b/docs/src/getting_started/crates.md index 1745da0494..841094e076 100644 --- a/docs/src/getting_started/crates.md +++ b/docs/src/getting_started/crates.md @@ -5,9 +5,36 @@ Each one has its self-contained purpose, and the user may not need to use all of Following the naming convention of the folders in the project's root, they are: -- libafl, the main crate that contains all the components needed to build a fuzzer -- libafl_derive, a proc-macro crate paired with the libafl crate -- libafl_targets, a crate that expose, under feature flags, pieces of code to interact with targets -- libafl_cc, a library that provide some utils to wrap compilers and create source level fuzzers. +### libafl +This is the main crate that contains all the components needed to build a fuzzer. +This crate has the following feature flags: + +- std, that enables the parts of the code that use the Rust standard library. Without this flags, libafl is no_std. +- derive, that enables the usage of the `derive(...)` macros defined in libafl_derive from libafl. + +By default, std and derive are both set. + +### libafl_derive + +This a proc-macro crate paired with the libafl crate. + +At the moment, it just expose the `derive(SerdeAny)` macro that can be used to define metadata structs. + +### libafl_targets + +This crate that exposes, under feature flags, pieces of code to interact with targets + +Currently, the supported flags are: + +- pcguard_edges, that defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges in a map. +- pcguard_hitcounts, that defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges with the hitcounts (like AFL) in a map. +- libfuzzer, that expose a compatibility layer with libFuzzer style harnesses. +- value_profile, that defines the SanitizerCoverage trace-cmp hooks to track the matching bits of each comparison in a map. + +### libafl_cc + +This is a library that provides some utils to wrap compilers and create source level fuzzers. + +At the moment, only the Clang compiler is supported. diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 2c1506c38e..72aafb481c 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -32,7 +32,6 @@ harness = false [features] default = ["std", "anymapdbg", "derive"] std = [] # print, sharedmap, ... support -runtime = [] # a runtime for clang inmem-executor anymapdbg = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint. derive = ["libafl_derive"] # provide derive(SerdeAny) macro. llmp_small_maps = [] # reduces initial map size for llmp From c8bfe76c437f33b60b38a04bc7ef78fab78442ab Mon Sep 17 00:00:00 2001 From: s1341 Date: Thu, 1 Apr 2021 15:39:11 +0300 Subject: [PATCH 041/104] Fix android and optimize frida (#44) * fix_android_and_optimize_frida: Fix build and run for android; optimize frida maybe_log functions * fix_android_and_optimize_frida: Get rid of the HasFd trait; cleanup warnings * fix_android_and_optimize_frida: fmt --- fuzzers/frida_libpng/build.rs | 13 ++-- fuzzers/frida_libpng/src/fuzzer.rs | 107 ++++++++++++++--------------- libafl/src/bolts/llmp.rs | 38 +++------- libafl/src/bolts/shmem.rs | 15 +--- libafl/src/events/llmp.rs | 8 +-- 5 files changed, 73 insertions(+), 108 deletions(-) diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs index cfe52fdf7a..63c8ae17ee 100644 --- a/fuzzers/frida_libpng/build.rs +++ b/fuzzers/frida_libpng/build.rs @@ -29,8 +29,13 @@ fn main() { let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); // Enforce clang for its -fsanitize-coverage support. - std::env::set_var("CC", "clang"); - std::env::set_var("CXX", "clang++"); + let clang = match env::var("CLANG_PATH") { + Ok(path) => path, + Err(_) => "clang".to_string(), + }; + let clangpp = format!("{}++", &clang); + std::env::set_var("CC", &clang); + std::env::set_var("CXX", &clangpp); let ldflags = match env::var("LDFLAGS") { Ok(val) => val, Err(_) => "".to_string(), @@ -61,8 +66,8 @@ fn main() { "--disable-shared", &format!("--host={}", env::var("TARGET").unwrap())[..], ]) - .env("CC", "clang") - .env("CXX", "clang++") + .env("CC", &clang) + .env("CXX", &clangpp) .env( "CFLAGS", "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index 885a62279f..b9cc649bbc 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -77,69 +77,56 @@ pub fn get_module_size(module_name: &str) -> usize { /// every time we need a copy that is within a direct branch of the start of the transformed basic /// block. #[cfg(target_arch = "x86_64")] -const MAYBE_LOG_CODE: [u8; 69] = [ - 0x9c, // pushfq - 0x50, // push rax - 0x51, // push rcx - 0x52, // push rdx - 0x56, // push rsi - 0x89, 0xf8, // mov eax, edi - 0xc1, 0xe0, 0x08, // shl eax, 8 - 0xc1, 0xef, 0x04, // shr edi, 4 - 0x31, 0xc7, // xor edi, eax - 0x0f, 0xb7, 0xc7, // movzx eax, di - 0x48, 0x8d, 0x0d, 0x34, 0x00, 0x00, 0x00, // lea rcx, sym._afl_area_ptr_ptr - 0x48, 0x8b, 0x09, // mov rcx, qword [rcx] - 0x48, 0x8d, 0x15, 0x22, 0x00, 0x00, 0x00, // lea rdx, sym._afl_prev_loc_ptr - 0x48, 0x8b, 0x32, // mov rsi, qword [rdx] - 0x48, 0x8b, 0x36, // mov rsi, qword [rsi] - 0x48, 0x31, 0xc6, // xor rsi, rax - 0x48, 0x81, 0xe6, 0xff, 0x1f, 0x00, - 0x00, // and rsi, 0x1fff (8 * 1024 - 1) TODO: make this variable - 0xfe, 0x04, 0x31, // inc byte [rcx + rsi] - 0x48, 0xd1, 0xe8, // shr rax, 1 - 0x48, 0x8b, 0x0a, // mov rcx, qword [rdx] - 0x48, 0x89, 0x01, // mov qword [rcx], rax - 0x5e, // pop rsi - 0x5a, // pop rdx - 0x59, // pop rcx - 0x58, // pop rax - 0x9d, // popfq - 0xc3, // ret - // Read-only data goes here: - // uint64_t* afl_prev_loc_ptr - // uint8_t** afl_area_ptr_ptr - // unsigned int afl_instr_rms +const MAYBE_LOG_CODE: [u8; 47] = [ + 0x9c, /* pushfq */ + 0x50, /* push rax */ + 0x51, /* push rcx */ + 0x52, /* push rdx */ + 0x48, 0x8d, 0x05, 0x24, 0x00, 0x00, 0x00, /* lea rax, sym._afl_area_ptr_ptr */ + 0x48, 0x8b, 0x00, /* mov rax, qword [rax] */ + 0x48, 0x8d, 0x0d, 0x22, 0x00, 0x00, 0x00, /* lea rcx, sym.previous_pc */ + 0x48, 0x8b, 0x11, /* mov rdx, qword [rcx] */ + 0x48, 0x8b, 0x12, /* mov rdx, qword [rdx] */ + 0x48, 0x31, 0xfa, /* xor rdx, rdi */ + 0xfe, 0x04, 0x10, /* inc byte [rax + rdx] */ + 0x48, 0xd1, 0xef, /* shr rdi, 1 */ + 0x48, 0x8b, 0x01, /* mov rax, qword [rcx] */ + 0x48, 0x89, 0x38, /* mov qword [rax], rdi */ + 0x5a, /* pop rdx */ + 0x59, /* pop rcx */ + 0x58, /* pop rax */ + 0x9d, /* popfq */ + 0xc3, /* ret */ + + /* Read-only data goes here: */ + /* uint8_t* afl_area_ptr */ + /* uint64_t* afl_prev_loc_ptr */ ]; #[cfg(target_arch = "aarch64")] -const MAYBE_LOG_CODE: [u8; 104] = [ +const MAYBE_LOG_CODE: [u8; 56] = [ + // __afl_area_ptr[current_pc ^ previous_pc]++; + // previous_pc = current_pc >> 1; 0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]! 0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]! - 0xE1, 0x03, 0x00, 0xAA, // mov x1, x0 - 0x00, 0xDC, 0x78, 0xD3, // lsl x0, x0, #8 - 0x21, 0xFC, 0x44, 0xD3, // lsr x1, x1, #4 - 0x00, 0x00, 0x01, 0xCA, // eor x0, x0, x1 - 0x00, 0x3C, 0x00, 0x53, // uxth w0, w0 - 0xa1, 0x02, 0x00, 0x58, // ldr x1, =area_ptr - 0x42, 0x02, 0x00, 0x58, // ldr x2, =pc_ptr - 0x43, 0x00, 0x40, 0xF9, // ldr x3, [x2] - 0x63, 0x00, 0x00, 0xCA, // eor x3, x3, x0 - 0x63, 0x40, 0x40, 0x92, // and x3, x3, #0x1ffff - 0x21, 0x00, 0x03, 0x8B, // add x1, x1, x3 - 0x24, 0x00, 0x40, 0x39, // ldrb w4, [x1, #0 - 0x84, 0x04, 0x00, 0x91, // add x4, x4, #1 - 0x24, 0x00, 0x00, 0x39, // strb w4, [x1, #0] - 0x00, 0xFC, 0x41, 0xD3, // lsr x0, x0, #1 - 0x40, 0x00, 0x00, 0xF9, // str x0, [x2] + // x0 = current_pc + 0x81, 0x01, 0x00, 0x58, // ldr x1, #0x30, =__afl_area_ptr + 0xa2, 0x01, 0x00, 0x58, // ldr x2, #0x38, =&previous_pc + 0x44, 0x00, 0x40, 0xf9, // ldr x4, [x2] (=previous_pc) + // __afl_area_ptr[current_pc ^ previous_pc]++; + 0x84, 0x00, 0x00, 0xca, // eor x4, x4, x0 + 0x23, 0x68, 0x64, 0xf8, // ldr x3, [x1, x4] + 0x63, 0x04, 0x00, 0x91, // add x3, x3, #1 + 0x23, 0x68, 0x24, 0xf8, // str x3, [x1, x2] + // previous_pc = current_pc >> 1; + 0xe0, 0x07, 0x40, 0x8b, // add x0, xzr, x0, LSR #1 + 0x40, 0x00, 0x00, 0xf9, // str x0, [x2] 0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10 0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10 0xC0, 0x03, 0x5F, 0xD6, // ret - 0x1f, 0x20, 0x03, 0xD5, // nop - 0x1f, 0x20, 0x03, 0xD5, // nop - 0x1f, 0x20, 0x03, 0xD5, // nop - 0x1f, 0x20, 0x03, 0xD5, // nop - 0x1f, 0x20, 0x03, 0xD5, // nop + + // &afl_area_ptr + // &afl_prev_loc_ptr ]; /// The implementation of the FridaEdgeCoverageHelper @@ -198,7 +185,10 @@ impl<'a> FridaEdgeCoverageHelper<'a> { -(frida_gum_sys::GUM_RED_ZONE_SIZE as i32), ); writer.put_push_reg(X86Register::Rdi); - writer.put_mov_reg_address(X86Register::Rdi, address); + writer.put_mov_reg_address( + X86Register::Rdi, + ((address >> 4) ^ (address << 8)) & (MAP_SIZE - 1) as u64, + ); writer.put_call_address(helper.current_log_impl); writer.put_pop_reg(X86Register::Rdi); writer.put_lea_reg_reg_offset( @@ -216,7 +206,10 @@ impl<'a> FridaEdgeCoverageHelper<'a> { -(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64, IndexMode::PreAdjust, ); - writer.put_ldr_reg_u64(Aarch64Register::X0, address); + writer.put_ldr_reg_u64( + Aarch64Register::X0, + ((address >> 4) ^ (address << 8)) & (MAP_SIZE - 1) as u64, + ); writer.put_bl_imm(helper.current_log_impl); writer.put_ldp_reg_reg_reg_offset( Aarch64Register::Lr, diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 9d896c7b8a..479e9b943d 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -80,18 +80,12 @@ use nix::{ }; #[cfg(all(feature = "std", unix))] -use std::{ - ffi::CStr, - os::unix::{ - self, - net::{UnixListener, UnixStream}, - {io::AsRawFd, prelude::RawFd}, - }, +use std::os::unix::{ + self, + net::{UnixListener, UnixStream}, + {io::AsRawFd, prelude::RawFd}, }; -#[cfg(all(unix, feature = "std"))] -use libc::c_char; - #[cfg(all(unix, feature = "std"))] use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; @@ -102,9 +96,6 @@ use crate::{ Error, }; -#[cfg(all(unix, feature = "std"))] -use super::shmem::HasFd; - /// We'll start off with 256 megabyte maps per fuzzer client #[cfg(not(feature = "llmp_small_maps"))] const LLMP_CFG_INITIAL_MAP_SIZE: usize = 1 << 28; @@ -454,7 +445,7 @@ where #[cfg(all(unix, feature = "std"))] impl LlmpConnection where - SH: ShMem + HasFd, + SH: ShMem, { #[cfg(all(feature = "std", unix))] pub fn on_domain_socket(filename: &str) -> Result { @@ -1438,6 +1429,7 @@ where let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; let broadcast_map_description = postcard::to_allocvec(&client_out_map_mem.description())?; + let client_out_map_mem_fd: i32 = client_out_map_mem.shm_str().parse().unwrap(); let mut incoming_map_description_serialized = vec![0u8; broadcast_map_description.len()]; @@ -1504,17 +1496,7 @@ where ListenerStream::Unix(stream, addr) => unsafe { dbg!("New connection", addr); - let broadcast_fd_initial: i32 = - CStr::from_ptr(broadcast_map_description.as_ptr() as *const c_char) - .to_string_lossy() - .into_owned() - .parse() - .unwrap_or_else(|_| { - panic!( - "ShmId is not a valid int file descriptor: {:?}", - broadcast_map_description - ) - }); + let broadcast_fd_initial: i32 = client_out_map_mem_fd; match sendmsg( stream.as_raw_fd(), @@ -1893,7 +1875,7 @@ where #[cfg(all(unix, feature = "std"))] impl LlmpClient where - SH: ShMem + HasFd, + SH: ShMem, { #[cfg(all(unix, feature = "std"))] /// Create a LlmpClient, getting the ID from a given filename @@ -1936,7 +1918,9 @@ where .first() .unwrap() .shmem - .shm_id()])], + .shm_str() + .parse() + .unwrap()])], MsgFlags::empty(), None, ) { diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 3c704be519..84cf07e5b3 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -101,13 +101,6 @@ pub trait ShMem: Sized + Debug { } } -/// shared maps that have an id can use this trait -//#[cfg(all(unix, feature = "std"))] -pub trait HasFd { - /// Retrieve the id of this shared map - fn shm_id(&self) -> i32; -} - #[cfg(all(unix, feature = "std"))] pub mod unix_shmem { @@ -121,7 +114,7 @@ pub mod unix_shmem { use crate::Error; - use super::{HasFd, ShMem}; + use super::ShMem; #[cfg(unix)] extern "C" { @@ -313,12 +306,6 @@ pub mod unix_shmem { } } - impl HasFd for UnixShMem { - fn shm_id(&self) -> i32 { - self.shm_id - } - } - /// Deinit sharedmaps on drop impl Drop for UnixShMem { fn drop(&mut self) { diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index e8ec17ffc0..cbd113ef30 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -17,9 +17,6 @@ use crate::utils::{fork, ForkResult}; #[cfg(all(feature = "std", unix))] use crate::bolts::shmem::UnixShMem; -#[cfg(all(feature = "std", unix))] -use crate::bolts::shmem::HasFd; - use crate::{ bolts::{ llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag}, @@ -313,7 +310,7 @@ impl LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem + HasFd, + SH: ShMem, ST: Stats, { #[cfg(all(feature = "std", unix))] @@ -516,7 +513,7 @@ pub fn setup_restarting_mgr( where I: Input, S: DeserializeOwned + IfInteresting, - SH: ShMem, // Todo: HasFd is only needed for Android + SH: ShMem, ST: Stats, { let mut mgr; @@ -525,7 +522,6 @@ where let (sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() { #[cfg(target_os = "android")] { - let path = std::env::current_dir()?; mgr = LlmpEventManager::::new_on_domain_socket(stats, "\x00llmp_socket")?; }; #[cfg(not(target_os = "android"))] From ceea3e0c14ed9bd09095606213e1d0fdd3935ec2 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 2 Apr 2021 11:59:38 +0200 Subject: [PATCH 042/104] docs --- docs/src/SUMMARY.md | 5 +++ docs/src/core_concepts/feedback.md | 8 ++++ docs/src/core_concepts/generator.md | 1 + docs/src/core_concepts/input.md | 11 ++++++ docs/src/core_concepts/mutator.md | 1 + docs/src/core_concepts/observer.md | 6 ++- libafl/src/executors/runtime.rs | 57 ----------------------------- 7 files changed, 31 insertions(+), 58 deletions(-) create mode 100644 docs/src/core_concepts/generator.md create mode 100644 docs/src/core_concepts/mutator.md delete mode 100644 libafl/src/executors/runtime.rs diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 9cc0cbf517..1489869e70 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -17,3 +17,8 @@ - [Feedback](./core_concepts/feedback.md) - [Input](./core_concepts/input.md) - [Corpus](./core_concepts/corpus.md) + - [Generator](./core_concepts/generator.md) + - [Mutator](./core_concepts/mutator.md) + - [Stage](./core_concepts/mutator.md) + + diff --git a/docs/src/core_concepts/feedback.md b/docs/src/core_concepts/feedback.md index c4a1060c53..c066a5ab44 100644 --- a/docs/src/core_concepts/feedback.md +++ b/docs/src/core_concepts/feedback.md @@ -1,3 +1,11 @@ # Feedback +The Feedback is an entity that classify the outcome of an execution of the program under test as interesting or not. +Tipically, if an exeuction is interesting, the corresponding input used to feed the target program is added to a corpus. +Most of the times, the notion of Feedback is deeply linked to the Observer, but they are different concepts. + +The Feedback, in most of the cases, process the information reported by one or more observer to decide if the execution is interesting. +The concept of "interestingness" is abstract, but tipically it is related to a novelty search (i.e. interesting inputs are those that reach a previosly unseen edge in the control flow graph). + +As an example, given an Observer that reports all the size of memory allocations, a maximization Feedback can be used to maximize these sizes to sport patological inputs in terms of memory consumption. diff --git a/docs/src/core_concepts/generator.md b/docs/src/core_concepts/generator.md new file mode 100644 index 0000000000..b8249cff8c --- /dev/null +++ b/docs/src/core_concepts/generator.md @@ -0,0 +1 @@ +# Generator diff --git a/docs/src/core_concepts/input.md b/docs/src/core_concepts/input.md index 135b6af33f..ae64587555 100644 --- a/docs/src/core_concepts/input.md +++ b/docs/src/core_concepts/input.md @@ -1 +1,12 @@ # Input + +Formally, the input of a program is the data taken from external sources and that affect the program behaviour. + +In our model of an abstarct fuzzer, we define the Input as the internal representation of the program input (or a part of it). + +In the straightforward case, the input of the program is a byte array and in fuzzers such as AFL we store an manipulate exaclty these byte arrays. + +But it is not always the case. A program can expect inputs that are not byte arrays (e.g. a sequence of syscalls) and the fuzzer does not represent the Input in the same way that the program consume it. + +In case of a grammar fuzzer for instance, the Input is generally an Abstract Syntax Tree because it is a data structure that can be easily manipulated while maintaining the validity, but the program expects a byte array as input so, just before the execution, the tree is serialized to a sequence of bytes. + diff --git a/docs/src/core_concepts/mutator.md b/docs/src/core_concepts/mutator.md new file mode 100644 index 0000000000..a5e1255285 --- /dev/null +++ b/docs/src/core_concepts/mutator.md @@ -0,0 +1 @@ +# Mutator diff --git a/docs/src/core_concepts/observer.md b/docs/src/core_concepts/observer.md index 65ba1ec920..d44d2fd8f7 100644 --- a/docs/src/core_concepts/observer.md +++ b/docs/src/core_concepts/observer.md @@ -1,5 +1,9 @@ # Observer -An Observer, or Observation Channel, is an entity that provide an information related to a single run of the program under test to the fuzzer. +An Observer, or Observation Channel, is an entity that provides an information observed during the execution of the program under test to the fuzzer. +The information contained in the Observer is not preserved cross executions. + +As an example, the coverage shared map filled during the execution to report the executed edges used by fuzzers such as AFL and HoggFuzz can be considered an Observation Channel. +This information is not preserved accros runs and it is an observation of a dynamic property of the program. diff --git a/libafl/src/executors/runtime.rs b/libafl/src/executors/runtime.rs deleted file mode 100644 index 1b7de09d80..0000000000 --- a/libafl/src/executors/runtime.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! A sancov runtime to update a simple u8 map with coverage-information during fuzzing - -//#![feature(asm)] - -/// The map size used by this instance. -const MAP_SIZE: usize = 65536; - -#[no_mangle] -pub static mut __lafl_dummy_map: [u8; MAP_SIZE] = [0; MAP_SIZE]; -#[no_mangle] -pub static mut __lafl_edges_map: *mut u8 = unsafe { __lafl_dummy_map.as_ptr() as *mut _ }; -#[no_mangle] -pub static mut __lafl_cmp_map: *mut u8 = unsafe { __lafl_dummy_map.as_ptr() as *mut _ }; -#[no_mangle] -pub static mut __lafl_max_edges_size: u32 = 0; - -/// Called for each branch the target program takes. -#[no_mangle] -#[inline] -pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: &u32) { - let ref mut trace_byte = *__lafl_edges_map.offset(*guard as isize); - /* TODO: translate to RUST inline ASM once it's stable (neverzero) - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - asm! volatile( \ - "addb $1, (%0, %1, 1)\n" \ - "adcb $0, (%0, %1, 1)\n" \ - : /* no out */ \ - : "r"(afl_area_ptr), "r"(loc) \ - : "memory", "eax") - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - */ - - // Make sure we wrap to 0, not zero, it's empirically proven to be better for fuzzing. - let added = (*trace_byte as u16) + 1; - *trace_byte = (added as u8) + (added >> 8) as u8; - - //*trace_byte = (*trace_byte).wrapping_add(1); -} - -/// Called when the target program starts -#[no_mangle] -#[inline] -pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { - if start == stop || *start != 0 { - return; - } - __lafl_max_edges_size = __lafl_max_edges_size.wrapping_add(1); - let fresh1 = start; - start = start.offset(1); - *fresh1 = __lafl_max_edges_size & (MAP_SIZE - 1) as u32; - while start < stop { - __lafl_max_edges_size = __lafl_max_edges_size.wrapping_add(1); - *start = __lafl_max_edges_size & (MAP_SIZE - 1) as u32; - start = start.offset(1) - } -} From 0d11a41038687660a32b6a62496a860feb89a7ce Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 7 Apr 2021 09:30:54 +0200 Subject: [PATCH 043/104] docs --- docs/src/core_concepts/corpus.md | 6 ++++++ docs/src/core_concepts/generator.md | 5 +++++ docs/src/core_concepts/mutator.md | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/docs/src/core_concepts/corpus.md b/docs/src/core_concepts/corpus.md index 361eb33c64..1cd1c20ae2 100644 --- a/docs/src/core_concepts/corpus.md +++ b/docs/src/core_concepts/corpus.md @@ -1 +1,7 @@ # Corpus + +The Corpus is where testcases are stored. A Testcase is defined as an Input and a set of related metadata like execution time for instance. + +For instance, a Corpus can store testcases on disk, or in memory, or implement a cache to speedup on disk storage. + +Usually, a testcase is added to the Corpus when it is considered as interesting. diff --git a/docs/src/core_concepts/generator.md b/docs/src/core_concepts/generator.md index b8249cff8c..1dcfee61cd 100644 --- a/docs/src/core_concepts/generator.md +++ b/docs/src/core_concepts/generator.md @@ -1 +1,6 @@ # Generator + +A Generator is a component designed to generate an Input from scratch. + +Tipically, a random generator is used to generate random inputs. + diff --git a/docs/src/core_concepts/mutator.md b/docs/src/core_concepts/mutator.md index a5e1255285..f6f7f21204 100644 --- a/docs/src/core_concepts/mutator.md +++ b/docs/src/core_concepts/mutator.md @@ -1 +1,5 @@ # Mutator + +The Mutator is an entitiy that takes one or more Inputs and generates a new derived one. + + From 6d2ad10e3fb0183709572c72a1830d407c978ce9 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 7 Apr 2021 09:53:04 +0200 Subject: [PATCH 044/104] docs --- docs/src/SUMMARY.md | 11 +--- docs/src/core_concepts.md | 86 +++++++++++++++++++++++++ docs/src/core_concepts/core_concepts.md | 7 -- docs/src/core_concepts/corpus.md | 7 -- docs/src/core_concepts/executor.md | 10 --- docs/src/core_concepts/feedback.md | 11 ---- docs/src/core_concepts/generator.md | 6 -- docs/src/core_concepts/input.md | 12 ---- docs/src/core_concepts/mutator.md | 5 -- docs/src/core_concepts/observer.md | 9 --- 10 files changed, 87 insertions(+), 77 deletions(-) create mode 100644 docs/src/core_concepts.md delete mode 100644 docs/src/core_concepts/core_concepts.md delete mode 100644 docs/src/core_concepts/corpus.md delete mode 100644 docs/src/core_concepts/executor.md delete mode 100644 docs/src/core_concepts/feedback.md delete mode 100644 docs/src/core_concepts/generator.md delete mode 100644 docs/src/core_concepts/input.md delete mode 100644 docs/src/core_concepts/mutator.md delete mode 100644 docs/src/core_concepts/observer.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 1489869e70..2e0dadb941 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -11,14 +11,5 @@ - [Baby Fuzzer](./baby_fuzzer.md) -- [Core Concepts](./core_concepts/core_concepts.md) - - [Observer](./core_concepts/observer.md) - - [Executor](./core_concepts/executor.md) - - [Feedback](./core_concepts/feedback.md) - - [Input](./core_concepts/input.md) - - [Corpus](./core_concepts/corpus.md) - - [Generator](./core_concepts/generator.md) - - [Mutator](./core_concepts/mutator.md) - - [Stage](./core_concepts/mutator.md) - +- [Core Concepts](./core_concepts.md) diff --git a/docs/src/core_concepts.md b/docs/src/core_concepts.md new file mode 100644 index 0000000000..ea87125e2e --- /dev/null +++ b/docs/src/core_concepts.md @@ -0,0 +1,86 @@ +# Core Concepts + +LibAFL is designed around some core concepts that we think can effectively abstract most of the other fuzzers designs. + +Here, we discuss these concepts and provide some examples related to other fuzzers. + +TODO add links to trait definitions in docs.rs + +## Observer + +An Observer, or Observation Channel, is an entity that provides an information observed during the execution of the program under test to the fuzzer. + +The information contained in the Observer is not preserved cross executions. + +As an example, the coverage shared map filled during the execution to report the executed edges used by fuzzers such as AFL and HoggFuzz can be considered an Observation Channel. +This information is not preserved accros runs and it is an observation of a dynamic property of the program. + +## Executor + +In different fuzzers, the concept of executing the program under test each run is now always the same. +For instance, for in-memory fuzzers like libFuzzer an execution is a call to an harness function, for hypervisor-based fuzzers like [kAFL](https://github.com/IntelLabs/kAFL) instead an entire operating system is started from a snapshot each run. + +In our model, an Executor is the entity that defines not only how to execute the target, but all the volatile operations that are related to just a single run of the target. + +So the Executor is for instance reponsible to inform the program about the input that the fuzzer wants to use in the run, writing to a memory location for instance or passing it as a parameter to the harness function. + +It also holds a set of Observers, as thay are related to just a single run of the target. + +## Feedback + +The Feedback is an entity that classify the outcome of an execution of the program under test as interesting or not. +Tipically, if an exeuction is interesting, the corresponding input used to feed the target program is added to a corpus. + +Most of the times, the notion of Feedback is deeply linked to the Observer, but they are different concepts. + +The Feedback, in most of the cases, process the information reported by one or more observer to decide if the execution is interesting. +The concept of "interestingness" is abstract, but tipically it is related to a novelty search (i.e. interesting inputs are those that reach a previosly unseen edge in the control flow graph). + +As an example, given an Observer that reports all the size of memory allocations, a maximization Feedback can be used to maximize these sizes to sport patological inputs in terms of memory consumption. + +## Input + +Formally, the input of a program is the data taken from external sources and that affect the program behaviour. + +In our model of an abstarct fuzzer, we define the Input as the internal representation of the program input (or a part of it). + +In the straightforward case, the input of the program is a byte array and in fuzzers such as AFL we store an manipulate exaclty these byte arrays. + +But it is not always the case. A program can expect inputs that are not byte arrays (e.g. a sequence of syscalls) and the fuzzer does not represent the Input in the same way that the program consume it. + +In case of a grammar fuzzer for instance, the Input is generally an Abstract Syntax Tree because it is a data structure that can be easily manipulated while maintaining the validity, but the program expects a byte array as input so, just before the execution, the tree is serialized to a sequence of bytes. + +## Corpus + +The Corpus is where testcases are stored. A Testcase is defined as an Input and a set of related metadata like execution time for instance. + +For instance, a Corpus can store testcases on disk, or in memory, or implement a cache to speedup on disk storage. + +Usually, a testcase is added to the Corpus when it is considered as interesting. + +## Mutator + +The Mutator is an entitiy that takes one or more Inputs and generates a new derived one. + +Mutators can be composed and they are generally linked to a specific Input type. + +There can be, for instance, a Mutator that applies more than a single type of mutation on the input. Consider a generic Mutator for a byte stream, bit flip is just one of the possible mutations but not the single one, there is also, for instance, the random replacement of a byte of the copy of a chunk. + +This Mutator will simple schedule the application of some other Mutators. + +## Generator + +A Generator is a component designed to generate an Input from scratch. + +Tipically, a random generator is used to generate random inputs. + +Generators are traditionally less used in Feedback-driven Fuzzing, but there are exceptions, like Nautilus, that uses a Grammar generator to create the initial corpus and a sub-tree Generator as a mutation of its grammar Mutator. + +## Stage + +A Stage is an entity that operates on a single Input got from the Corpus. + +For instamce, a Mutational Stage, given an input of the corpus, applies a Mutator and executes the generated input one or more time. How many times this has to be done can be scheduled, AFL for instance use a performance score of the input to choose how many times the havoc mutator should be invoked. This can depends also on other parameters, for instance, the length of the input if we want to just apply a sequential bitflip, or be a fixed value. + +A stage can be also an analysis stage, for instance, the Colorization stage of Redqueen that aims to introduce more entropy in a testcase or the Trimming stage of AFL that aims to reduce the size of a testcase. + diff --git a/docs/src/core_concepts/core_concepts.md b/docs/src/core_concepts/core_concepts.md deleted file mode 100644 index 2da1afbb02..0000000000 --- a/docs/src/core_concepts/core_concepts.md +++ /dev/null @@ -1,7 +0,0 @@ -# Core Concepts - -LibAFL is designed around some core concepts that we think can effectively abstract most of the other fuzzers designs. - -In this chapter, we discuss these concepts, provide some examples related to other fuzzers and some details how the defined entities maps to the code in Rust. - - diff --git a/docs/src/core_concepts/corpus.md b/docs/src/core_concepts/corpus.md deleted file mode 100644 index 1cd1c20ae2..0000000000 --- a/docs/src/core_concepts/corpus.md +++ /dev/null @@ -1,7 +0,0 @@ -# Corpus - -The Corpus is where testcases are stored. A Testcase is defined as an Input and a set of related metadata like execution time for instance. - -For instance, a Corpus can store testcases on disk, or in memory, or implement a cache to speedup on disk storage. - -Usually, a testcase is added to the Corpus when it is considered as interesting. diff --git a/docs/src/core_concepts/executor.md b/docs/src/core_concepts/executor.md deleted file mode 100644 index 965280e07e..0000000000 --- a/docs/src/core_concepts/executor.md +++ /dev/null @@ -1,10 +0,0 @@ -# Executor - -In different fuzzers, the concept of executing the program under test each run is now always the same. -For instance, for in-memory fuzzers like libFuzzer an execution is a call to an harness function, for hypervisor-based fuzzers like [kAFL](https://github.com/IntelLabs/kAFL) instead an entire operating system is started from a snapshot each run. - -In our model, an Executor is the entity that defines not only how to execute the target, but all the volatile operations that are related to just a single run of the target. - -So the Executor is for instance reponsible to inform the program about the input that the fuzzer wants to use in the run, writing to a memory location for instance or passing it as a parameter to the harness function. - -It also holds a set of Observers, as thay are related to just a single run of the target. diff --git a/docs/src/core_concepts/feedback.md b/docs/src/core_concepts/feedback.md deleted file mode 100644 index c066a5ab44..0000000000 --- a/docs/src/core_concepts/feedback.md +++ /dev/null @@ -1,11 +0,0 @@ -# Feedback - -The Feedback is an entity that classify the outcome of an execution of the program under test as interesting or not. -Tipically, if an exeuction is interesting, the corresponding input used to feed the target program is added to a corpus. - -Most of the times, the notion of Feedback is deeply linked to the Observer, but they are different concepts. - -The Feedback, in most of the cases, process the information reported by one or more observer to decide if the execution is interesting. -The concept of "interestingness" is abstract, but tipically it is related to a novelty search (i.e. interesting inputs are those that reach a previosly unseen edge in the control flow graph). - -As an example, given an Observer that reports all the size of memory allocations, a maximization Feedback can be used to maximize these sizes to sport patological inputs in terms of memory consumption. diff --git a/docs/src/core_concepts/generator.md b/docs/src/core_concepts/generator.md deleted file mode 100644 index 1dcfee61cd..0000000000 --- a/docs/src/core_concepts/generator.md +++ /dev/null @@ -1,6 +0,0 @@ -# Generator - -A Generator is a component designed to generate an Input from scratch. - -Tipically, a random generator is used to generate random inputs. - diff --git a/docs/src/core_concepts/input.md b/docs/src/core_concepts/input.md deleted file mode 100644 index ae64587555..0000000000 --- a/docs/src/core_concepts/input.md +++ /dev/null @@ -1,12 +0,0 @@ -# Input - -Formally, the input of a program is the data taken from external sources and that affect the program behaviour. - -In our model of an abstarct fuzzer, we define the Input as the internal representation of the program input (or a part of it). - -In the straightforward case, the input of the program is a byte array and in fuzzers such as AFL we store an manipulate exaclty these byte arrays. - -But it is not always the case. A program can expect inputs that are not byte arrays (e.g. a sequence of syscalls) and the fuzzer does not represent the Input in the same way that the program consume it. - -In case of a grammar fuzzer for instance, the Input is generally an Abstract Syntax Tree because it is a data structure that can be easily manipulated while maintaining the validity, but the program expects a byte array as input so, just before the execution, the tree is serialized to a sequence of bytes. - diff --git a/docs/src/core_concepts/mutator.md b/docs/src/core_concepts/mutator.md deleted file mode 100644 index f6f7f21204..0000000000 --- a/docs/src/core_concepts/mutator.md +++ /dev/null @@ -1,5 +0,0 @@ -# Mutator - -The Mutator is an entitiy that takes one or more Inputs and generates a new derived one. - - diff --git a/docs/src/core_concepts/observer.md b/docs/src/core_concepts/observer.md deleted file mode 100644 index d44d2fd8f7..0000000000 --- a/docs/src/core_concepts/observer.md +++ /dev/null @@ -1,9 +0,0 @@ -# Observer - -An Observer, or Observation Channel, is an entity that provides an information observed during the execution of the program under test to the fuzzer. - -The information contained in the Observer is not preserved cross executions. - -As an example, the coverage shared map filled during the execution to report the executed edges used by fuzzers such as AFL and HoggFuzz can be considered an Observation Channel. -This information is not preserved accros runs and it is an observation of a dynamic property of the program. - From 8577d5c6a0a73b466ef5592ffe18b9fa2669dc08 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 7 Apr 2021 11:30:03 +0200 Subject: [PATCH 045/104] clippy fixes --- clippy.sh | 15 ++++++ libafl/build.rs | 2 + libafl/src/bolts/llmp.rs | 2 + libafl/src/bolts/os/unix_signals.rs | 1 + libafl/src/bolts/shmem.rs | 7 ++- libafl/src/events/llmp.rs | 64 ++++++++++++++------------ libafl/src/events/simple.rs | 1 + libafl/src/executors/mod.rs | 2 +- libafl/src/generators/mod.rs | 2 +- libafl/src/inputs/bytes.rs | 7 +-- libafl/src/mutators/scheduled.rs | 4 +- libafl/src/mutators/token_mutations.rs | 1 + libafl/src/stages/mutational.rs | 1 + libafl/src/state/mod.rs | 6 +-- libafl/src/stats/mod.rs | 8 ++-- libafl/src/utils.rs | 6 +++ libafl_cc/src/lib.rs | 50 +++++++++++++------- libafl_targets/build.rs | 2 +- libafl_targets/src/lib.rs | 2 + 19 files changed, 119 insertions(+), 64 deletions(-) create mode 100755 clippy.sh diff --git a/clippy.sh b/clippy.sh new file mode 100755 index 0000000000..88af86df4a --- /dev/null +++ b/clippy.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Clippy checks +cargo clean +RUST_BACKTRACE=full cargo clippy --all -- \ + -D clippy::pedantic \ + -W missing-docs \ + -W clippy::missing-errors-doc \ + -W clippy::similar-names \ + -A clippy::missing-docs-in-private-items \ + -A clippy::unseparated-literal-suffix \ + -A clippy::module-name-repetitions \ + -A clippy::unreadable-literal \ + -A clippy::if-not-else \ + #--allow clippy::print-with-newline \ + #--allow clippy::write-with-newline \ diff --git a/libafl/build.rs b/libafl/build.rs index ff4a614f3d..f1bf264c6b 100644 --- a/libafl/build.rs +++ b/libafl/build.rs @@ -1,3 +1,5 @@ +//! special handling to build and link libafl + fn main() { #[cfg(target_os = "windows")] windows::build!( diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 479e9b943d..f99823e5f0 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -1064,6 +1064,7 @@ where } /// Returns the next message, tag, buf, if avaliable, else None + #[allow(clippy::type_complexity)] #[inline] pub fn recv_buf(&mut self) -> Result, Error> { unsafe { @@ -1824,6 +1825,7 @@ where } /// Returns the next message, tag, buf, if avaliable, else None + #[allow(clippy::type_complexity)] #[inline] pub fn recv_buf(&mut self) -> Result, Error> { self.receiver.recv_buf() diff --git a/libafl/src/bolts/os/unix_signals.rs b/libafl/src/bolts/os/unix_signals.rs index 8248306a45..0c149afca8 100644 --- a/libafl/src/bolts/os/unix_signals.rs +++ b/libafl/src/bolts/os/unix_signals.rs @@ -24,6 +24,7 @@ pub use libc::{c_void, siginfo_t}; #[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)] #[repr(i32)] +#[allow(clippy::clippy::pub_enum_variant_names)] pub enum Signal { SigAbort = SIGABRT, SigBus = SIGBUS, diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 84cf07e5b3..94c5ed52f6 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -356,6 +356,7 @@ pub mod unix_shmem { } /// Deinitialize this shmem instance + #[allow(clippy::clippy::clippy::unnecessary_cast)] // for c_ types unsafe fn unix_shmem_deinit(shm: *mut UnixShMem) { if shm.is_null() || (*shm).map.is_null() { /* Serialized map id */ @@ -369,6 +370,7 @@ pub mod unix_shmem { /// Functions to create Shared memory region, for observation channels and /// opening inputs and stuff. + #[allow(clippy::clippy::clippy::unnecessary_cast)] // for c_ types unsafe fn unix_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar { (*shm).map_size = map_size; (*shm).map = ptr::null_mut(); @@ -394,13 +396,14 @@ pub mod unix_shmem { if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar || (*shm).map.is_null() { shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut()); (*shm).shm_id = -(1 as c_int); - (*shm).shm_str[0 as c_int as usize] = 0u8; + (*shm).shm_str[0] = 0u8; return ptr::null_mut(); } (*shm).map } /// Uses a shmap id string to open a shared map + #[allow(clippy::clippy::unnecessary_cast)] // for c_int and c_long unsafe fn unix_shmem_by_str( shm: *mut UnixShMem, shm_str: &CStr, @@ -414,7 +417,7 @@ pub mod unix_shmem { strncpy( (*shm).shm_str.as_mut_ptr() as *mut c_char, shm_str.as_ptr() as *const c_char, - (size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_int as c_ulong), + (size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_ulong), ); (*shm).shm_id = shm_str .to_str() diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index cbd113ef30..72797512aa 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -206,6 +206,7 @@ where } /// Handle arriving events in the broker + #[allow(clippy::clippy::unnecessary_wraps)] fn handle_in_broker( stats: &mut ST, sender_id: u32, @@ -398,6 +399,7 @@ where } /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` +#[allow(clippy::type_complexity)] pub fn deserialize_state_mgr( state_corpus_serialized: &[u8], ) -> Result<(S, LlmpEventManager), Error> @@ -505,6 +507,7 @@ where /// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. /// The restarter will start a new process each time the child crashes or timeouts. #[cfg(feature = "std")] +#[allow(clippy::clippy::unnecessary_operation)] // for { mgr = LlmpEventManager... } pub fn setup_restarting_mgr( //mgr: &mut LlmpEventManager, stats: ST, @@ -534,43 +537,44 @@ where println!("Doing broker things. Run this tool again to start fuzzing in a client."); mgr.broker_loop()?; return Err(Error::ShuttingDown); - } else { - // We are the fuzzer respawner in a llmp client - mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL); + } - // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. - let sender = LlmpSender::new(0, false)?; - let receiver = LlmpReceiver::on_existing_map( - SH::clone_ref(&sender.out_maps.last().unwrap().shmem)?, - None, - )?; - // Store the information to a map. - sender.to_env(_ENV_FUZZER_SENDER)?; - receiver.to_env(_ENV_FUZZER_RECEIVER)?; + // We are the fuzzer respawner in a llmp client + mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL); - let mut ctr: u64 = 0; - // Client->parent loop - loop { - dbg!("Spawning next client (id {})", ctr); + // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. + let sender = LlmpSender::new(0, false)?; + let receiver = LlmpReceiver::on_existing_map( + SH::clone_ref(&sender.out_maps.last().unwrap().shmem)?, + None, + )?; + // Store the information to a map. + sender.to_env(_ENV_FUZZER_SENDER)?; + receiver.to_env(_ENV_FUZZER_RECEIVER)?; - // On Unix, we fork (todo: measure if that is actually faster.) - #[cfg(unix)] - let _ = match unsafe { fork() }? { - ForkResult::Parent(handle) => handle.status(), - ForkResult::Child => break (sender, receiver), - }; + let mut ctr: u64 = 0; + // Client->parent loop + loop { + dbg!("Spawning next client (id {})", ctr); - // On windows, we spawn ourself again - #[cfg(windows)] - startable_self()?.status()?; + // On Unix, we fork (todo: measure if that is actually faster.) + #[cfg(unix)] + let _ = match unsafe { fork() }? { + ForkResult::Parent(handle) => handle.status(), + ForkResult::Child => break (sender, receiver), + }; - if unsafe { read_volatile(&(*receiver.current_recv_map.page()).size_used) } == 0 { - // Storing state in the last round did not work - panic!("Fuzzer-respawner: Storing state in crashed fuzzer instance did not work, no point to spawn the next client!"); - } + // On windows, we spawn ourself again + #[cfg(windows)] + startable_self()?.status()?; - ctr = ctr.wrapping_add(1); + if unsafe { read_volatile(&(*receiver.current_recv_map.page()).size_used) } == 0 { + // Storing state in the last round did not work + panic!("Fuzzer-respawner: Storing state in crashed fuzzer instance did not work, no point to spawn the next client!"); } + + ctr = ctr.wrapping_add(1); + } } else { // We are the newly started fuzzing instance, first, connect to our own restore map. diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index ab0d88cab3..82b7c401bb 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -73,6 +73,7 @@ where } // Handle arriving events in the broker + #[allow(clippy::unnecessary_wraps)] fn handle_in_broker(stats: &mut ST, event: &Event) -> Result { match event { Event::NewTestcase { diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 3c751e080e..b0ac310a55 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -21,7 +21,7 @@ use crate::{ pub enum ExitKind { Ok, Crash, - OOM, + Oom, Timeout, } diff --git a/libafl/src/generators/mod.rs b/libafl/src/generators/mod.rs index f42a6c175a..27c94a1056 100644 --- a/libafl/src/generators/mod.rs +++ b/libafl/src/generators/mod.rs @@ -93,7 +93,7 @@ where /// Generates up to DUMMY_BYTES_MAX non-random dummy bytes (0) fn generate_dummy(&self) -> BytesInput { let size = min(self.max_size, DUMMY_BYTES_MAX); - BytesInput::new(vec![0u8; size]) + BytesInput::new(vec![0_u8; size]) } } diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index 5840e783be..03bbaa538b 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -16,10 +16,11 @@ pub struct BytesInput { impl Input for BytesInput {} + /// Rc Ref-cell from Input -impl Into>> for BytesInput { - fn into(self) -> Rc> { - Rc::new(RefCell::new(self)) +impl From for Rc> { + fn from(input: BytesInput) -> Self { + Rc::new(RefCell::new(input)) } } diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index 5aefac6747..70b02fe552 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -179,7 +179,7 @@ where /// Create a new StdScheduledMutator instance specifying mutations pub fn new(mutations: MT) -> Self { StdScheduledMutator { - mutations: mutations, + mutations, phantom: PhantomData, } } @@ -377,7 +377,7 @@ where /// Create a new StdScheduledMutator instance without mutations and corpus pub fn new(scheduled: SM) -> Self { Self { - scheduled: scheduled, + scheduled, mutation_log: vec![], phantom: PhantomData, } diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 240f2afae0..6f8a6bfe9a 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -49,6 +49,7 @@ impl Tokens { /// Adds a token to a dictionary, checking it is not a duplicate /// Returns `false` if the token was already present and did not get added. + #[allow(clippy::clippy::ptr_arg)] pub fn add_token(&mut self, token: &Vec) -> bool { if self.token_vec.contains(token) { return false; diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 9c9b4d3731..f8024b1fe3 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -81,6 +81,7 @@ where R: Rand, { mutator: M, + #[allow(clippy::type_complexity)] phantom: PhantomData<(C, CS, E, EM, I, OT, R, S)>, } diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index b55cec8fc1..b82e179a9d 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -25,7 +25,7 @@ use crate::{ use crate::inputs::bytes::BytesInput; /// The maximum size of a testcase -pub const DEFAULT_MAX_SIZE: usize = 1048576; +pub const DEFAULT_MAX_SIZE: usize = 1_048_576; /// Trait for elements offering a corpus pub trait HasCorpus @@ -453,9 +453,9 @@ where where OT: ObserversTuple, { - Ok(self + self .feedbacks_mut() - .is_interesting_all(input, observers, exit_kind)?) + .is_interesting_all(input, observers, exit_kind) } /// Adds this input to the corpus, if it's intersting, and return the index diff --git a/libafl/src/stats/mod.rs b/libafl/src/stats/mod.rs index 6a0883c707..d2a2386907 100644 --- a/libafl/src/stats/mod.rs +++ b/libafl/src/stats/mod.rs @@ -97,14 +97,14 @@ pub trait Stats { fn corpus_size(&self) -> u64 { self.client_stats() .iter() - .fold(0u64, |acc, x| acc + x.corpus_size) + .fold(0_u64, |acc, x| acc + x.corpus_size) } /// Amount of elements in the objectives (combined for all children) fn objective_size(&self) -> u64 { self.client_stats() .iter() - .fold(0u64, |acc, x| acc + x.objective_size) + .fold(0_u64, |acc, x| acc + x.objective_size) } /// Total executions @@ -112,7 +112,7 @@ pub trait Stats { fn total_execs(&mut self) -> u64 { self.client_stats() .iter() - .fold(0u64, |acc, x| acc + x.executions) + .fold(0_u64, |acc, x| acc + x.executions) } /// Executions per second @@ -121,7 +121,7 @@ pub trait Stats { let cur_time = current_time(); self.client_stats_mut() .iter_mut() - .fold(0u64, |acc, x| acc + x.execs_per_sec(cur_time)) + .fold(0_u64, |acc, x| acc + x.execs_per_sec(cur_time)) } /// The client stats for a specific id, creating new if it doesn't exist diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 74734333d1..88c18840ec 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -179,6 +179,7 @@ pub struct Xoshiro256StarRand { } impl Rand for Xoshiro256StarRand { + #[allow(clippy::clippy::unreadable_literal)] fn set_seed(&mut self, seed: u64) { self.rand_seed[0] = xxh3_64_with_seed(&HASH_CONST.to_le_bytes(), seed); self.rand_seed[1] = self.rand_seed[0] ^ 0x1234567890abcdef; @@ -223,6 +224,7 @@ pub struct XorShift64Rand { } impl Rand for XorShift64Rand { + #[allow(clippy::clippy::unreadable_literal)] fn set_seed(&mut self, seed: u64) { self.rand_seed = seed ^ 0x1234567890abcdef; } @@ -254,11 +256,13 @@ pub struct Lehmer64Rand { } impl Rand for Lehmer64Rand { + #[allow(clippy::clippy::unreadable_literal)] fn set_seed(&mut self, seed: u64) { self.rand_seed = (seed as u128) ^ 0x1234567890abcdef; } #[inline] + #[allow(clippy::clippy::unreadable_literal)] fn next(&mut self) -> u64 { self.rand_seed *= 0xda942042e4dd58b5; (self.rand_seed >> 64) as u64 @@ -303,6 +307,7 @@ impl Rand for RomuTrioRand { } #[inline] + #[allow(clippy::clippy::unreadable_literal)] fn next(&mut self) -> u64 { let xp = self.x_state; let yp = self.y_state; @@ -339,6 +344,7 @@ impl Rand for RomuDuoJrRand { } #[inline] + #[allow(clippy::clippy::unreadable_literal)] fn next(&mut self) -> u64 { let xp = self.x_state; self.x_state = 15241094284759029579u64.wrapping_mul(self.y_state); diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 4898150c66..6c41039300 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -1,36 +1,46 @@ +//! Compiler Wrapper from `LibAFL` + use std::{process::Command, string::String, vec::Vec}; +/// `LibAFL` CC Error Type #[derive(Debug)] pub enum Error { + /// CC Wrapper called with invalid arguments InvalidArguments(String), - IOError(std::io::Error), + /// Io error occurred + Io(std::io::Error), + /// Something else happened Unknown(String), } // TODO macOS +/// extension for static libraries #[cfg(windows)] -pub const LIB_EXT: &'static str = "lib"; +pub const LIB_EXT: &str = "lib"; +/// extension for static libraries #[cfg(not(windows))] -pub const LIB_EXT: &'static str = "a"; +pub const LIB_EXT: &str = "a"; +/// prefix for static libraries #[cfg(windows)] -pub const LIB_PREFIX: &'static str = ""; +pub const LIB_PREFIX: &str = ""; +/// prefix for static libraries #[cfg(not(windows))] -pub const LIB_PREFIX: &'static str = "lib"; +pub const LIB_PREFIX: &str = "lib"; /// Wrap a compiler hijacking its arguments pub trait CompilerWrapper { /// Set the wrapper arguments parsing a command line set of arguments - fn from_args<'a>(&'a mut self, args: &[String]) -> Result<&'a mut Self, Error>; + fn from_args(&mut self, args: &[String]) -> Result<&'_ mut Self, Error>; /// Add a compiler argument - fn add_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error>; + fn add_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>; /// Add a compiler argument only when compiling - fn add_cc_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error>; + fn add_cc_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>; /// Add a compiler argument only when linking - fn add_link_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error>; + fn add_link_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>; /// Command to run the compiler fn command(&mut self) -> Result, Error>; @@ -42,14 +52,14 @@ pub trait CompilerWrapper { fn run(&mut self) -> Result<(), Error> { let args = self.command()?; dbg!(&args); - if args.len() < 1 { + if args.is_empty() { return Err(Error::InvalidArguments( "The number of arguments cannot be 0".into(), )); } let status = match Command::new(&args[0]).args(&args[1..]).status() { Ok(s) => s, - Err(e) => return Err(Error::IOError(e)), + Err(e) => return Err(Error::Io(e)), }; dbg!(status); Ok(()) @@ -57,6 +67,7 @@ pub trait CompilerWrapper { } /// Wrap Clang +#[allow(clippy::clippy::struct_excessive_bools)] pub struct ClangWrapper { optimize: bool, wrapped_cc: String, @@ -73,10 +84,11 @@ pub struct ClangWrapper { link_args: Vec, } +#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared" impl CompilerWrapper for ClangWrapper { fn from_args<'a>(&'a mut self, args: &[String]) -> Result<&'a mut Self, Error> { let mut new_args = vec![]; - if args.len() < 1 { + if args.is_empty() { return Err(Error::InvalidArguments( "The number of arguments cannot be 0".into(), )); @@ -122,17 +134,17 @@ impl CompilerWrapper for ClangWrapper { Ok(self) } - fn add_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error> { + fn add_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error> { self.base_args.push(arg); Ok(self) } - fn add_cc_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error> { + fn add_cc_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error> { self.cc_args.push(arg); Ok(self) } - fn add_link_arg<'a>(&'a mut self, arg: String) -> Result<&'a mut Self, Error> { + fn add_link_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error> { self.link_args.push(arg); Ok(self) } @@ -165,6 +177,8 @@ impl CompilerWrapper for ClangWrapper { } impl ClangWrapper { + /// Create a new Clang Wrapper + #[must_use] pub fn new(wrapped_cc: &str, wrapped_cxx: &str) -> Self { Self { optimize: true, @@ -181,12 +195,14 @@ impl ClangWrapper { } } - pub fn dont_optimize<'a>(&'a mut self) -> &'a mut Self { + /// Disable optimizations + pub fn dont_optimize(&mut self) -> &'_ mut Self { self.optimize = false; self } - pub fn is_cpp<'a>(&'a mut self) -> &'a mut Self { + /// set cpp mode + pub fn is_cpp(&mut self) -> &'_ mut Self { self.is_cpp = true; self } diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 5e95ab0cf2..01caad4adf 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -1,4 +1,4 @@ -// build.rs +//! build.rs for `libafl_targets` use std::env; use std::path::Path; diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index e817b8a57c..e8f1636554 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -1,3 +1,5 @@ +//! `libafl_targets` contains runtime code, injected in the target itself during compilation. + #[cfg(any(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] pub mod pcguard; #[cfg(any(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] From 56ce44ff801be5ac10773f75b895b1d3a3f4a528 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 7 Apr 2021 11:35:27 +0200 Subject: [PATCH 046/104] clippy ignore --- libafl/src/events/llmp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 72797512aa..a3acd8e6c1 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -507,7 +507,7 @@ where /// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. /// The restarter will start a new process each time the child crashes or timeouts. #[cfg(feature = "std")] -#[allow(clippy::clippy::unnecessary_operation)] // for { mgr = LlmpEventManager... } +#[allow(clippy::clippy::unnecessary_operation, clippy::clippy::type_complexity)] // for { mgr = LlmpEventManager... } pub fn setup_restarting_mgr( //mgr: &mut LlmpEventManager, stats: ST, From 711b54929a14cefbea1ae678470c3c0a91974f43 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 7 Apr 2021 11:35:35 +0200 Subject: [PATCH 047/104] fmt --- libafl/src/events/llmp.rs | 3 +-- libafl/src/inputs/bytes.rs | 1 - libafl/src/state/mod.rs | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index a3acd8e6c1..0cc25b6e10 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -537,7 +537,7 @@ where println!("Doing broker things. Run this tool again to start fuzzing in a client."); mgr.broker_loop()?; return Err(Error::ShuttingDown); - } + } // We are the fuzzer respawner in a llmp client mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL); @@ -574,7 +574,6 @@ where } ctr = ctr.wrapping_add(1); - } } else { // We are the newly started fuzzing instance, first, connect to our own restore map. diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index 03bbaa538b..b92675c709 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -16,7 +16,6 @@ pub struct BytesInput { impl Input for BytesInput {} - /// Rc Ref-cell from Input impl From for Rc> { fn from(input: BytesInput) -> Self { diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index b82e179a9d..49d22d04e5 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -453,8 +453,7 @@ where where OT: ObserversTuple, { - self - .feedbacks_mut() + self.feedbacks_mut() .is_interesting_all(input, observers, exit_kind) } From 0ac48c2e0b42f6d8d914c741d3c1a2613800a7a8 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 7 Apr 2021 13:35:29 +0200 Subject: [PATCH 048/104] llmp debug added --- libafl/Cargo.toml | 3 +++ libafl/src/bolts/llmp.rs | 41 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 72aafb481c..1da0da2f44 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -35,6 +35,7 @@ std = [] # print, sharedmap, ... support anymapdbg = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint. derive = ["libafl_derive"] # provide derive(SerdeAny) macro. llmp_small_maps = [] # reduces initial map size for llmp +llmp_debug = [] # Enables debug output for LLMP [[example]] name = "llmp_test" @@ -56,6 +57,8 @@ serde_json = { version = "1.0", optional = true, default-features = false, featu #TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression num_enum = "0.5.1" +backtrace = "0.3" # for llmp_debug + [target.'cfg(unix)'.dependencies] libc = "0.2" # For (*nix) libc nix = "0.20.0" diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index f99823e5f0..cafaa589a2 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -86,6 +86,9 @@ use std::os::unix::{ {io::AsRawFd, prelude::RawFd}, }; +#[cfg(feature = "llmp_debug")] +use backtrace::Backtrace; + #[cfg(all(unix, feature = "std"))] use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; @@ -785,6 +788,18 @@ where /// listener about it using a EOP message. unsafe fn handle_out_eop(&mut self) -> Result<(), Error> { + #[cfg(feature = "llmp_debug")] + { + let bt = Backtrace::new(); + let shm = self.out_maps.last().unwrap(); + println!( + "LLMP_DEBUG: End of page reached for map {} with len {}, sending EOP, bt: {:?}", + shm.shmem.shm_str(), + shm.shmem.map().len(), + bt + ); + } + let old_map = self.out_maps.last_mut().unwrap().page_mut(); // Create a new shard page. @@ -1024,8 +1039,12 @@ where // Mark the new page save to unmap also (it's mapped by us, the broker now) ptr::write_volatile(&mut (*page).save_to_unmap, 1); - #[cfg(feature = "std")] - dbg!("Got a new recv map", self.current_recv_map.shmem.shm_str()); + #[cfg(feature = "llmp_debug")] + println!( + "LLMP_DEBUG: Got a new recv map {} with len {:?}", + self.current_recv_map.shmem.shm_str(), + self.current_recv_map.shmem.map().len() + ); // After we mapped the new page, return the next message, if available return self.recv(); } @@ -1135,6 +1154,13 @@ where { /// Creates a new page, initializing the passed shared mem struct pub fn new(sender: u32, mut new_map: SH) -> Self { + #[cfg(feature = "llmp_debug")] + println!( + "LLMP_DEBUG: Initializing map on {} with size {}", + new_map.shm_str(), + new_map.map().len() + ); + unsafe { _llmp_page_init(&mut new_map, sender, false); } @@ -1143,6 +1169,17 @@ where /// Maps and wraps an existing pub fn existing(existing_map: SH) -> Self { + #[cfg(feature = "llmp_debug")] + { + let bt = Backtrace::new(); + println!( + "LLMP_DEBUG: Using existing map {} with size {}, bt: {:?}", + existing_map.shm_str(), + existing_map.map().len(), + bt + ); + } + let ret = Self { shmem: existing_map, }; From d72d48d6a8edc55a1529d30031cf7a9ff6c9cda0 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 7 Apr 2021 15:57:59 +0200 Subject: [PATCH 049/104] more cargo fixes --- clippy.sh | 14 ++++++++++---- libafl/src/bolts/llmp.rs | 16 +++++++++++----- libafl/src/executors/mod.rs | 2 +- libafl/src/executors/timeout.rs | 2 +- libafl/src/feedbacks/map.rs | 10 +++++----- libafl/src/feedbacks/mod.rs | 2 +- libafl/src/fuzzer.rs | 2 +- libafl/src/generators/mod.rs | 4 ++-- libafl/src/mutators/mutations.rs | 2 +- libafl/src/utils.rs | 2 +- 10 files changed, 34 insertions(+), 22 deletions(-) diff --git a/clippy.sh b/clippy.sh index 88af86df4a..17a8503766 100755 --- a/clippy.sh +++ b/clippy.sh @@ -3,13 +3,19 @@ cargo clean RUST_BACKTRACE=full cargo clippy --all -- \ -D clippy::pedantic \ - -W missing-docs \ - -W clippy::missing-errors-doc \ + -W clippy::cast_sign_loss \ -W clippy::similar-names \ + -W clippy::cast_ptr_alignment \ + -A missing-docs \ + -A clippy::doc_markdown \ + -A clippy::must-use-candidate \ + -A clippy::missing-errors-doc \ + -A clippy::cast-possible-truncation \ + -A clippy::used-underscore-binding \ + -A clippy::ptr-as-ptr \ + -A clippy::missing-panics-doc \ -A clippy::missing-docs-in-private-items \ -A clippy::unseparated-literal-suffix \ -A clippy::module-name-repetitions \ -A clippy::unreadable-literal \ -A clippy::if-not-else \ - #--allow clippy::print-with-newline \ - #--allow clippy::write-with-newline \ diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index cafaa589a2..4a2062fe42 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -86,7 +86,7 @@ use std::os::unix::{ {io::AsRawFd, prelude::RawFd}, }; -#[cfg(feature = "llmp_debug")] +#[cfg(all(feature = "llmp_debug", feature = "std"))] use backtrace::Backtrace; #[cfg(all(unix, feature = "std"))] @@ -788,9 +788,12 @@ where /// listener about it using a EOP message. unsafe fn handle_out_eop(&mut self) -> Result<(), Error> { - #[cfg(feature = "llmp_debug")] + #[cfg(all(feature = "llmp_debug", feature = "std"))] { + #[cfg(debug_assertions)] let bt = Backtrace::new(); + #[cfg(not(debug_assertions))] + let bt = ""; let shm = self.out_maps.last().unwrap(); println!( "LLMP_DEBUG: End of page reached for map {} with len {}, sending EOP, bt: {:?}", @@ -1039,7 +1042,7 @@ where // Mark the new page save to unmap also (it's mapped by us, the broker now) ptr::write_volatile(&mut (*page).save_to_unmap, 1); - #[cfg(feature = "llmp_debug")] + #[cfg(all(feature = "llmp_debug", feature = "std"))] println!( "LLMP_DEBUG: Got a new recv map {} with len {:?}", self.current_recv_map.shmem.shm_str(), @@ -1154,7 +1157,7 @@ where { /// Creates a new page, initializing the passed shared mem struct pub fn new(sender: u32, mut new_map: SH) -> Self { - #[cfg(feature = "llmp_debug")] + #[cfg(all(feature = "llmp_debug", feature = "std"))] println!( "LLMP_DEBUG: Initializing map on {} with size {}", new_map.shm_str(), @@ -1169,9 +1172,12 @@ where /// Maps and wraps an existing pub fn existing(existing_map: SH) -> Self { - #[cfg(feature = "llmp_debug")] + #[cfg(all(feature = "llmp_debug", feature = "std"))] { + #[cfg(debug_assertions)] let bt = Backtrace::new(); + #[cfg(not(debug_assertions))] + let bt = ""; println!( "LLMP_DEBUG: Using existing map {} with size {}, bt: {:?}", existing_map.shm_str(), diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index b0ac310a55..1ccccbf437 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -49,7 +49,7 @@ where } /// A simple executor that does nothing. -/// If intput len is 0, run_target will return Err +/// If intput len is 0, `run_target` will return Err struct NopExecutor { phantom: PhantomData, } diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index 6acb753032..1fbb4b84e9 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -1,4 +1,4 @@ -//! A TimeoutExecutor set a timeout before each target run +//! A `TimeoutExecutor` sets a timeout before each target run use core::{marker::PhantomData, time::Duration}; diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 2f7e71fb3c..c18d6b80e0 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -259,7 +259,7 @@ where R: Reducer, O: MapObserver + Observer, { - /// Create new MapFeedback + /// Create new `MapFeedback` pub fn new(name: &'static str, map_size: usize) -> Self { Self { history_map: vec![T::default(); map_size], @@ -270,7 +270,7 @@ where } } - /// Create new MapFeedback for the observer type. + /// Create new `MapFeedback` for the observer type. pub fn new_with_observer(map_observer: &O) -> Self { Self { history_map: vec![T::default(); map_observer.map().len()], @@ -281,7 +281,7 @@ where } } - /// Create new MapFeedback specifying if it must track indexes of novelties + /// Create new `MapFeedback` specifying if it must track indexes of novelties pub fn new_track( name: &'static str, map_size: usize, @@ -297,7 +297,7 @@ where } } - /// Create new MapFeedback for the observer type if it must track indexes of novelties + /// Create new `MapFeedback` for the observer type if it must track indexes of novelties pub fn new_with_observer_track( map_observer: &O, track_indexes: bool, @@ -319,7 +319,7 @@ where R: Reducer, O: MapObserver, { - /// Create new MapFeedback using a map observer, and a map. + /// Create new `MapFeedback` using a map observer, and a map. /// The map can be shared. pub fn with_history_map(name: &'static str, history_map: Vec) -> Self { Self { diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 53e2cf4b8e..be782e594a 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -24,7 +24,7 @@ pub trait Feedback: Named + serde::Serialize + serde::de::DeserializeOwned + where I: Input, { - /// is_interesting should return the "Interestingness" from 0 to 255 (percent times 2.55) + /// `is_interesting ` should return the "Interestingness" from 0 to 255 (percent times 2.55) fn is_interesting( &mut self, input: &I, diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 062ae077cc..bcf75cf1f3 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -96,7 +96,7 @@ pub trait Fuzzer { Ok(ret) } - /// Given the last time, if stats_timeout seconds passed, send off an info/stats/heartbeat message to the broker. + /// Given the last time, if `stats_timeout` seconds passed, send off an info/stats/heartbeat message to the broker. /// Returns the new `last` time (so the old one, unless `stats_timeout` time has passed and stats have been sent) /// Will return an Error, if the stats could not be sent. fn maybe_report_stats( diff --git a/libafl/src/generators/mod.rs b/libafl/src/generators/mod.rs index 27c94a1056..b12cb76003 100644 --- a/libafl/src/generators/mod.rs +++ b/libafl/src/generators/mod.rs @@ -48,7 +48,7 @@ where Ok(BytesInput::new(random_bytes)) } - /// Generates up to DUMMY_BYTES_MAX non-random dummy bytes (0) + /// Generates up to `DUMMY_BYTES_MAX` non-random dummy bytes (0) fn generate_dummy(&self) -> BytesInput { let size = min(self.max_size, DUMMY_BYTES_MAX); BytesInput::new(vec![0; size]) @@ -90,7 +90,7 @@ where Ok(BytesInput::new(random_bytes)) } - /// Generates up to DUMMY_BYTES_MAX non-random dummy bytes (0) + /// Generates up to `DUMMY_BYTES_MAX` non-random dummy bytes (0) fn generate_dummy(&self) -> BytesInput { let size = min(self.max_size, DUMMY_BYTES_MAX); BytesInput::new(vec![0_u8; size]) diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index d1743572b1..0ba6cc7ffb 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -44,7 +44,7 @@ pub fn buffer_copy(dst: &mut [u8], src: &[u8], from: usize, to: usize, len: usiz /// A simple buffer_set. /// The compiler does the heavy lifting. -/// see https://stackoverflow.com/a/51732799/1345238 +/// see #[inline] fn buffer_set(data: &mut [u8], from: usize, len: usize, val: u8) { debug_assert!(from + len <= data.len()); diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 88c18840ec..72b581d2ec 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -402,7 +402,7 @@ impl ChildHandle { } #[cfg(unix)] -/// The ForkResult +/// The `ForkResult` (result of a fork) pub enum ForkResult { Parent(ChildHandle), Child, From 381aa3c052a6faa1d35f5f6fe1119763b59cc478 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 7 Apr 2021 16:39:51 +0200 Subject: [PATCH 050/104] meta --- docs/src/SUMMARY.md | 9 ++++++++- docs/src/design/architecture.md | 3 +++ docs/src/{ => design}/core_concepts.md | 0 docs/src/design/design.md | 3 +++ docs/src/design/state.md | 1 + docs/src/medatata/de_serialization.md | 1 + docs/src/medatata/definition.md | 1 + docs/src/medatata/metadata.md | 1 + docs/src/medatata/usage.md | 1 + 9 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 docs/src/design/architecture.md rename docs/src/{ => design}/core_concepts.md (100%) create mode 100644 docs/src/design/design.md create mode 100644 docs/src/design/state.md create mode 100644 docs/src/medatata/de_serialization.md create mode 100644 docs/src/medatata/definition.md create mode 100644 docs/src/medatata/metadata.md create mode 100644 docs/src/medatata/usage.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 2e0dadb941..5ab55fd332 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -11,5 +11,12 @@ - [Baby Fuzzer](./baby_fuzzer.md) -- [Core Concepts](./core_concepts.md) +- [Design](./design/design.md) + - [Core Concepts](./design/core_concepts.md) + - [Architecture](./design/architecture.md) + - [The State](./design/state.md) +- [Understanding Metadata](./medatata/metadata.md) + - [Definition](./medatata/definition.md) + - [(De)Serialization](./medatata/de_serialization.md) + - [Usage](./medatata/usage.md) diff --git a/docs/src/design/architecture.md b/docs/src/design/architecture.md new file mode 100644 index 0000000000..886c187e10 --- /dev/null +++ b/docs/src/design/architecture.md @@ -0,0 +1,3 @@ +# Architecture + +The LibAFL architecture diff --git a/docs/src/core_concepts.md b/docs/src/design/core_concepts.md similarity index 100% rename from docs/src/core_concepts.md rename to docs/src/design/core_concepts.md diff --git a/docs/src/design/design.md b/docs/src/design/design.md new file mode 100644 index 0000000000..3650272973 --- /dev/null +++ b/docs/src/design/design.md @@ -0,0 +1,3 @@ +# Design + +In this chapter, we introduce the abstract Core Concepts behind LibAFL, we then discuss how we designed the library to take into account these concepts while allowing code reuse and extensibility. diff --git a/docs/src/design/state.md b/docs/src/design/state.md new file mode 100644 index 0000000000..a898f3c21a --- /dev/null +++ b/docs/src/design/state.md @@ -0,0 +1 @@ +# The State diff --git a/docs/src/medatata/de_serialization.md b/docs/src/medatata/de_serialization.md new file mode 100644 index 0000000000..fabf103865 --- /dev/null +++ b/docs/src/medatata/de_serialization.md @@ -0,0 +1 @@ +# (De)Serialization diff --git a/docs/src/medatata/definition.md b/docs/src/medatata/definition.md new file mode 100644 index 0000000000..9cf6ae572d --- /dev/null +++ b/docs/src/medatata/definition.md @@ -0,0 +1 @@ +# Definition diff --git a/docs/src/medatata/metadata.md b/docs/src/medatata/metadata.md new file mode 100644 index 0000000000..dfcaceb2b9 --- /dev/null +++ b/docs/src/medatata/metadata.md @@ -0,0 +1 @@ +# Understanding Metadata diff --git a/docs/src/medatata/usage.md b/docs/src/medatata/usage.md new file mode 100644 index 0000000000..8f04b05adb --- /dev/null +++ b/docs/src/medatata/usage.md @@ -0,0 +1 @@ +# Usage From ec55a03ec15b75c651d3b3165454fda26f096fab Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 7 Apr 2021 19:53:14 +0200 Subject: [PATCH 051/104] more clippy --- clippy.sh | 6 +++++- libafl/src/bolts/llmp.rs | 12 ++++++------ libafl/src/bolts/os/unix_signals.rs | 4 ++-- libafl/src/bolts/serdeany.rs | 4 ++-- libafl/src/bolts/shmem.rs | 9 ++++----- libafl/src/corpus/minimizer.rs | 4 ++++ libafl/src/corpus/testcase.rs | 9 +++++---- libafl/src/events/llmp.rs | 9 +++++++-- libafl/src/events/simple.rs | 1 + libafl/src/executors/inprocess.rs | 12 ++++++------ libafl/src/mutators/mutations.rs | 14 ++++++++------ libafl/src/mutators/token_mutations.rs | 8 ++++---- libafl/src/observers/map.rs | 6 ++++-- libafl/src/stats/mod.rs | 3 ++- libafl/src/utils.rs | 14 +++++++------- libafl_cc/src/lib.rs | 2 +- 16 files changed, 68 insertions(+), 49 deletions(-) diff --git a/clippy.sh b/clippy.sh index 17a8503766..7f4e6ac479 100755 --- a/clippy.sh +++ b/clippy.sh @@ -1,14 +1,18 @@ #!/bin/sh # Clippy checks -cargo clean +cargo clean -p libafl RUST_BACKTRACE=full cargo clippy --all -- \ -D clippy::pedantic \ -W clippy::cast_sign_loss \ -W clippy::similar-names \ -W clippy::cast_ptr_alignment \ + -W clippy::cast_possible_wrap \ + -W clippy::unused_self \ + -W clippy::too_many_lines \ -A missing-docs \ -A clippy::doc_markdown \ -A clippy::must-use-candidate \ + -A clippy::type_repetition_in_bounds \ -A clippy::missing-errors-doc \ -A clippy::cast-possible-truncation \ -A clippy::used-underscore-binding \ diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 4a2062fe42..ba40bd8d7e 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -1223,6 +1223,7 @@ where /// Will return IllegalArgument error if msg is not on page. /// # Safety /// This dereferences msg, make sure to pass a proper pointer to it. + #[allow(clippy::cast_sign_loss)] pub unsafe fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result { let page = self.page(); if llmp_msg_in_page(page, msg) { @@ -1266,19 +1267,17 @@ where /// Gets this message from this page, at the indicated offset. /// Will return IllegalArgument error if the offset is out of bounds. pub fn msg_from_offset(&mut self, offset: u64) -> Result<*mut LlmpMsg, Error> { + let offset = offset as usize; unsafe { let page = self.page_mut(); let page_size = self.shmem.map().len() - size_of::(); - if offset as isize > page_size as isize { + if offset > page_size { Err(Error::IllegalArgument(format!( "Msg offset out of bounds (size: {}, requested offset: {})", page_size, offset ))) } else { - Ok( - ((*page).messages.as_mut_ptr() as *mut u8).offset(offset as isize) - as *mut LlmpMsg, - ) + Ok(((*page).messages.as_mut_ptr() as *mut u8).add(offset) as *mut LlmpMsg) } } } @@ -1310,7 +1309,7 @@ pub struct LlmpBrokerSignalHandler { #[cfg(all(unix))] impl Handler for LlmpBrokerSignalHandler { - fn handle(&mut self, _signal: Signal, _info: siginfo_t, _void: c_void) { + fn handle(&mut self, _signal: Signal, _info: siginfo_t, _void: *const c_void) { unsafe { ptr::write_volatile(&mut self.shutting_down, true) }; } @@ -1400,6 +1399,7 @@ where /// Internal function, returns true when shuttdown is requested by a `SIGINT` signal #[inline] #[cfg(unix)] + #[allow(clippy::unused_self)] fn is_shutting_down(&self) -> bool { unsafe { ptr::read_volatile(&GLOBAL_SIGHANDLER_STATE.shutting_down) } } diff --git a/libafl/src/bolts/os/unix_signals.rs b/libafl/src/bolts/os/unix_signals.rs index 0c149afca8..b388b7bd1f 100644 --- a/libafl/src/bolts/os/unix_signals.rs +++ b/libafl/src/bolts/os/unix_signals.rs @@ -84,7 +84,7 @@ impl Display for Signal { pub trait Handler { /// Handle a signal - fn handle(&mut self, signal: Signal, info: siginfo_t, _void: c_void); + fn handle(&mut self, signal: Signal, info: siginfo_t, _void: *const c_void); /// Return a list of signals to handle fn signals(&self) -> Vec; } @@ -112,7 +112,7 @@ static mut SIGNAL_HANDLERS: [Option; 32] = [ /// # Safety /// This should be somewhat safe to call for signals previously registered, /// unless the signal handlers registered using [setup_signal_handler] are broken. -unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: c_void) { +unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: *const c_void) { let signal = &Signal::try_from(sig).unwrap(); let handler = { match &SIGNAL_HANDLERS[*signal as usize] { diff --git a/libafl/src/bolts/serdeany.rs b/libafl/src/bolts/serdeany.rs index 50e3d6034f..d1b52c7ed9 100644 --- a/libafl/src/bolts/serdeany.rs +++ b/libafl/src/bolts/serdeany.rs @@ -277,7 +277,7 @@ macro_rules! create_serde_registry_for_trait { None => None, Some(h) => h .get(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())) - .map(|x| x.as_ref()), + .map(AsRef::as_ref), } } @@ -304,7 +304,7 @@ macro_rules! create_serde_registry_for_trait { None => None, Some(h) => h .get_mut(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())) - .map(|x| x.as_mut()), + .map(AsMut::as_mut), } } diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 94c5ed52f6..f87b7afbfc 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -356,7 +356,7 @@ pub mod unix_shmem { } /// Deinitialize this shmem instance - #[allow(clippy::clippy::clippy::unnecessary_cast)] // for c_ types + #[allow(clippy::clippy::unnecessary_cast)] // for c_ types unsafe fn unix_shmem_deinit(shm: *mut UnixShMem) { if shm.is_null() || (*shm).map.is_null() { /* Serialized map id */ @@ -370,7 +370,7 @@ pub mod unix_shmem { /// Functions to create Shared memory region, for observation channels and /// opening inputs and stuff. - #[allow(clippy::clippy::clippy::unnecessary_cast)] // for c_ types + #[allow(clippy::clippy::unnecessary_cast)] // for c_ types unsafe fn unix_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar { (*shm).map_size = map_size; (*shm).map = ptr::null_mut(); @@ -390,8 +390,7 @@ pub mod unix_shmem { (*shm).shm_id, ); (*shm).shm_str - [(size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_int as c_ulong) as usize] = - 0u8; + [(size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_ulong) as usize] = 0u8; (*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar; if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar || (*shm).map.is_null() { shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut()); @@ -403,7 +402,7 @@ pub mod unix_shmem { } /// Uses a shmap id string to open a shared map - #[allow(clippy::clippy::unnecessary_cast)] // for c_int and c_long + #[allow(clippy::unnecessary_cast)] // for c_int and c_long unsafe fn unix_shmem_by_str( shm: *mut UnixShMem, shm_str: &CStr, diff --git a/libafl/src/corpus/minimizer.rs b/libafl/src/corpus/minimizer.rs index 475688909b..6b90d9dad3 100644 --- a/libafl/src/corpus/minimizer.rs +++ b/libafl/src/corpus/minimizer.rs @@ -150,6 +150,8 @@ where C: Corpus, R: Rand, { + /// Update the `Corpus` score using the `MinimizerCorpusScheduler` + #[allow(clippy::unused_self)] pub fn update_score(&self, state: &mut S, idx: usize) -> Result<(), Error> { // Create a new top rated meta if not existing if state.metadata().get::().is_none() { @@ -194,6 +196,8 @@ where Ok(()) } + /// Cull the `Corpus` using the `MinimizerCorpusScheduler` + #[allow(clippy::unused_self)] pub fn cull(&self, state: &mut S) -> Result<(), Error> { if state.metadata().get::().is_none() { return Ok(()); diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index eea9fd01ac..bae9860ed6 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -217,14 +217,15 @@ where self.cached_len = Some(l); l } - None => match self.cached_len { - Some(l) => l, - None => { + None => { + if let Some(l) = self.cached_len { + l + } else { let l = self.load_input()?.len(); self.cached_len = Some(l); l } - }, + } }) } } diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 0cc25b6e10..1772aca5b8 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -206,7 +206,7 @@ where } /// Handle arriving events in the broker - #[allow(clippy::clippy::unnecessary_wraps)] + #[allow(clippy::unnecessary_wraps)] fn handle_in_broker( stats: &mut ST, sender_id: u32, @@ -258,6 +258,7 @@ where } // Handle arriving events in the client + #[allow(clippy::unused_self)] fn handle_in_client( &mut self, state: &mut S, @@ -507,7 +508,11 @@ where /// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. /// The restarter will start a new process each time the child crashes or timeouts. #[cfg(feature = "std")] -#[allow(clippy::clippy::unnecessary_operation, clippy::clippy::type_complexity)] // for { mgr = LlmpEventManager... } +#[allow( + clippy::unnecessary_operation, + clippy::type_complexity, + clippy::similar_names +)] // for { mgr = LlmpEventManager... } pub fn setup_restarting_mgr( //mgr: &mut LlmpEventManager, stats: ST, diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index 82b7c401bb..1ce7598155 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -118,6 +118,7 @@ where } // Handle arriving events in the client + #[allow(clippy::needless_pass_by_value, clippy::unused_self)] fn handle_in_client(&mut self, _state: &mut S, event: Event) -> Result<(), Error> { Err(Error::Unknown(format!( "Received illegal message that message should not have arrived: {:?}.", diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 0071f194a0..b5230b9265 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -273,8 +273,8 @@ mod unix_signal_handler { pub event_mgr_ptr: *mut c_void, pub observers_ptr: *const c_void, pub current_input_ptr: *const c_void, - pub crash_handler: unsafe fn(Signal, siginfo_t, c_void, data: &mut Self), - pub timeout_handler: unsafe fn(Signal, siginfo_t, c_void, data: &mut Self), + pub crash_handler: unsafe fn(Signal, siginfo_t, *const c_void, data: &mut Self), + pub timeout_handler: unsafe fn(Signal, siginfo_t, *const c_void, data: &mut Self), } unsafe impl Send for InProcessExecutorHandlerData {} @@ -283,14 +283,14 @@ mod unix_signal_handler { unsafe fn nop_handler( _signal: Signal, _info: siginfo_t, - _void: c_void, + _void: *const c_void, _data: &mut InProcessExecutorHandlerData, ) { } #[cfg(unix)] impl Handler for InProcessExecutorHandlerData { - fn handle(&mut self, signal: Signal, info: siginfo_t, void: c_void) { + fn handle(&mut self, signal: Signal, info: siginfo_t, void: *const c_void) { unsafe { let data = &mut GLOBAL_STATE; match signal { @@ -320,7 +320,7 @@ mod unix_signal_handler { pub unsafe fn inproc_timeout_handler( _signal: Signal, _info: siginfo_t, - _void: c_void, + _void: *const c_void, data: &mut InProcessExecutorHandlerData, ) where EM: EventManager, @@ -382,7 +382,7 @@ mod unix_signal_handler { pub unsafe fn inproc_crash_handler( _signal: Signal, _info: siginfo_t, - _void: c_void, + _void: *const c_void, data: &mut InProcessExecutorHandlerData, ) where EM: EventManager, diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 0ba6cc7ffb..7ee4b4963b 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -118,7 +118,7 @@ where let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize; unsafe { // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8; + *input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128u8 >> (bit & 7)) as u8; } Ok(MutationResult::Mutated) } @@ -734,6 +734,7 @@ where S: HasRand, R: Rand, { + #[allow(clippy::cast_sign_loss)] fn mutate( &mut self, state: &mut S, @@ -796,6 +797,7 @@ where S: HasRand, R: Rand, { + #[allow(clippy::cast_sign_loss)] fn mutate( &mut self, state: &mut S, @@ -863,6 +865,7 @@ where S: HasRand, R: Rand, { + #[allow(clippy::cast_sign_loss)] fn mutate( &mut self, state: &mut S, @@ -1651,6 +1654,7 @@ where R: Rand, S: HasRand + HasCorpus, { + #[allow(clippy::cast_sign_loss)] fn mutate( &mut self, state: &mut S, @@ -1670,12 +1674,12 @@ where let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let other = other_testcase.load_input()?; - let mut counter = 0; + let mut counter: u32 = 0; loop { let (f, l) = locate_diffs(input.bytes(), other.bytes()); if f != l && f >= 0 && l >= 2 { - break (f, l); + break (f as u64, l as u64); } if counter == 3 { return Ok(MutationResult::Skipped); @@ -1684,9 +1688,7 @@ where } }; - let split_at = state - .rand_mut() - .between(first_diff as u64, last_diff as u64) as usize; + let split_at = state.rand_mut().between(first_diff, last_diff) as usize; let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); let other = other_testcase.load_input()?; diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 6f8a6bfe9a..e38a3d65d6 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -9,7 +9,7 @@ use std::{ use crate::{ inputs::{HasBytesVec, Input}, - mutators::*, + mutators::{buffer_self_copy, mutations, str_decode, MutationResult, Mutator, Named}, state::{HasMaxSize, HasMetadata, HasRand}, utils::Rand, Error, @@ -49,7 +49,7 @@ impl Tokens { /// Adds a token to a dictionary, checking it is not a duplicate /// Returns `false` if the token was already present and did not get added. - #[allow(clippy::clippy::ptr_arg)] + #[allow(clippy::ptr_arg)] pub fn add_token(&mut self, token: &Vec) -> bool { if self.token_vec.contains(token) { return false; @@ -82,7 +82,7 @@ impl Tokens { } let pos_quote = match line.find('\"') { Some(x) => x, - _ => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)), + None => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)), }; if line.chars().nth(line.len() - 1) != Some('"') { return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)); @@ -91,7 +91,7 @@ impl Tokens { // extract item let item = match line.get(pos_quote + 1..line.len() - 1) { Some(x) => x, - _ => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)), + None => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)), }; if item.is_empty() { continue; diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index d1e8f13a80..e92559551f 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -56,6 +56,7 @@ where /// A well-known example is the AFL-Style coverage map. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] +#[allow(clippy::unsafe_derive_deserialize)] pub struct StdMapObserver where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, @@ -156,6 +157,7 @@ where /// Overlooking a variable bitmap #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] +#[allow(clippy::unsafe_derive_deserialize)] pub struct VariableMapObserver where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, @@ -226,11 +228,11 @@ where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { /// Creates a new MapObserver - pub fn new(name: &'static str, map: &'static mut [T], size: &usize) -> Self { + pub fn new(name: &'static str, map: &'static mut [T], size: *const usize) -> Self { let initial = if map.is_empty() { T::default() } else { map[0] }; Self { map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())), - size: Cptr::Cptr(size as *const _), + size: Cptr::Cptr(size), name: name.into(), initial, } diff --git a/libafl/src/stats/mod.rs b/libafl/src/stats/mod.rs index d2a2386907..6b673922a5 100644 --- a/libafl/src/stats/mod.rs +++ b/libafl/src/stats/mod.rs @@ -50,6 +50,7 @@ impl ClientStats { } /// Get the calculated executions per second for this client + #[allow(clippy::cast_sign_loss, clippy::cast_precision_loss)] pub fn execs_per_sec(&mut self, cur_time: time::Duration) -> u64 { if self.executions == 0 { return 0; @@ -130,7 +131,7 @@ pub trait Stats { for _ in client_stat_count..(client_id + 1) as usize { self.client_stats_mut().push(ClientStats { last_window_time: current_time(), - ..Default::default() + ..ClientStats::default() }) } &mut self.client_stats_mut()[client_id as usize] diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 72b581d2ec..edc3a580af 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -179,7 +179,7 @@ pub struct Xoshiro256StarRand { } impl Rand for Xoshiro256StarRand { - #[allow(clippy::clippy::unreadable_literal)] + #[allow(clippy::unreadable_literal)] fn set_seed(&mut self, seed: u64) { self.rand_seed[0] = xxh3_64_with_seed(&HASH_CONST.to_le_bytes(), seed); self.rand_seed[1] = self.rand_seed[0] ^ 0x1234567890abcdef; @@ -224,7 +224,7 @@ pub struct XorShift64Rand { } impl Rand for XorShift64Rand { - #[allow(clippy::clippy::unreadable_literal)] + #[allow(clippy::unreadable_literal)] fn set_seed(&mut self, seed: u64) { self.rand_seed = seed ^ 0x1234567890abcdef; } @@ -256,13 +256,13 @@ pub struct Lehmer64Rand { } impl Rand for Lehmer64Rand { - #[allow(clippy::clippy::unreadable_literal)] + #[allow(clippy::unreadable_literal)] fn set_seed(&mut self, seed: u64) { - self.rand_seed = (seed as u128) ^ 0x1234567890abcdef; + self.rand_seed = u128::from(seed) ^ 0x1234567890abcdef; } #[inline] - #[allow(clippy::clippy::unreadable_literal)] + #[allow(clippy::unreadable_literal)] fn next(&mut self) -> u64 { self.rand_seed *= 0xda942042e4dd58b5; (self.rand_seed >> 64) as u64 @@ -307,7 +307,7 @@ impl Rand for RomuTrioRand { } #[inline] - #[allow(clippy::clippy::unreadable_literal)] + #[allow(clippy::unreadable_literal)] fn next(&mut self) -> u64 { let xp = self.x_state; let yp = self.y_state; @@ -344,7 +344,7 @@ impl Rand for RomuDuoJrRand { } #[inline] - #[allow(clippy::clippy::unreadable_literal)] + #[allow(clippy::unreadable_literal)] fn next(&mut self) -> u64 { let xp = self.x_state; self.x_state = 15241094284759029579u64.wrapping_mul(self.y_state); diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 6c41039300..c26675aceb 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -67,7 +67,7 @@ pub trait CompilerWrapper { } /// Wrap Clang -#[allow(clippy::clippy::struct_excessive_bools)] +#[allow(clippy::struct_excessive_bools)] pub struct ClangWrapper { optimize: bool, wrapped_cc: String, From 77cbb45b7c2face92ba93d0ff106e20175190f39 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 8 Apr 2021 10:34:31 +0200 Subject: [PATCH 052/104] docs --- libafl/src/bolts/bindings.rs | 2 ++ libafl/src/bolts/os/mod.rs | 2 ++ libafl/src/bolts/os/unix_signals.rs | 1 + libafl/src/bolts/os/windows_exceptions.rs | 2 ++ libafl/src/events/llmp.rs | 2 ++ libafl/src/feedbacks/map.rs | 2 ++ libafl/src/fuzzer.rs | 2 ++ libafl/src/mutators/scheduled.rs | 2 ++ libafl/src/observers/map.rs | 2 ++ libafl/src/observers/mod.rs | 2 ++ 10 files changed, 19 insertions(+) diff --git a/libafl/src/bolts/bindings.rs b/libafl/src/bolts/bindings.rs index a3fa41413e..1a4dad811f 100644 --- a/libafl/src/bolts/bindings.rs +++ b/libafl/src/bolts/bindings.rs @@ -1,2 +1,4 @@ +//! Generated bindings + #[cfg(all(windows, feature = "std"))] ::windows::include_bindings!(); diff --git a/libafl/src/bolts/os/mod.rs b/libafl/src/bolts/os/mod.rs index 7a45acb215..4ea718107f 100644 --- a/libafl/src/bolts/os/mod.rs +++ b/libafl/src/bolts/os/mod.rs @@ -1,3 +1,5 @@ +//! Operating System specific abstractions + #[cfg(unix)] pub mod unix_signals; #[cfg(windows)] diff --git a/libafl/src/bolts/os/unix_signals.rs b/libafl/src/bolts/os/unix_signals.rs index b388b7bd1f..1182798be9 100644 --- a/libafl/src/bolts/os/unix_signals.rs +++ b/libafl/src/bolts/os/unix_signals.rs @@ -1,3 +1,4 @@ +//! Signal handling for unix use alloc::vec::Vec; use core::{ cell::UnsafeCell, diff --git a/libafl/src/bolts/os/windows_exceptions.rs b/libafl/src/bolts/os/windows_exceptions.rs index 838d0d21cb..98bd392863 100644 --- a/libafl/src/bolts/os/windows_exceptions.rs +++ b/libafl/src/bolts/os/windows_exceptions.rs @@ -1,3 +1,5 @@ +//! Exception handling for Windows + pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS; use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error}; diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 1772aca5b8..ae85aebeb5 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -1,3 +1,5 @@ +//! LLMP-backed event manager for scalable multi-processed fuzzing + use alloc::{string::ToString, vec::Vec}; use core::{marker::PhantomData, time::Duration}; use serde::{de::DeserializeOwned, Serialize}; diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index c18d6b80e0..014550d0da 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -1,3 +1,5 @@ +//! Map feedback, maximizing or minimizing maps, for example the afl-style map observer. + use alloc::{ string::{String, ToString}, vec::Vec, diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index bcf75cf1f3..f24b2e5fc5 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -1,3 +1,5 @@ +//! The `Fuzzer` is the main struct for a fuzz campaign. + use crate::{ corpus::CorpusScheduler, events::{Event, EventManager}, diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index 70b02fe552..2a7e3f4b99 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -1,3 +1,5 @@ +//! The `ScheduledMutator` schedules multiple mutations internally. + use alloc::string::String; use alloc::vec::Vec; use core::{ diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index e92559551f..a32f3cf1e9 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -1,3 +1,5 @@ +//! The `MapObserver` provides access a map, usually injected into the target + use alloc::{ string::{String, ToString}, vec::Vec, diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 26798fdaf1..2ff66ca5fc 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -1,3 +1,5 @@ +//! Observers give insights about runs of a target, such as coverage, timing, stack depth, and more. + pub mod map; pub use map::*; From f4d5c045b4645b3520213614821edd187b912d4e Mon Sep 17 00:00:00 2001 From: s1341 Date: Sat, 10 Apr 2021 11:33:11 +0300 Subject: [PATCH 053/104] Ashmem server for Android (#50) * ashmem, initial commit * ashmem * ashmem_service: server side ready * ashmem_service: client side ready. Ready for integration * ashmem_service: changes to UnixShMem to make it 'threadable' * ashmem_service: format * ashmem_service: Undo changes to UnixShMem, make the thread own the AshmemService instead; Fix protocol bug * ashmem_service: actually fix the protocol issue; clippy warnings * no-std fixes * fmt Co-authored-by: Dominik Maier --- libafl/src/bolts/llmp.rs | 4 +- libafl/src/bolts/os/ashmem_server.rs | 268 +++++++++++++++++++++++++ libafl/src/bolts/os/mod.rs | 3 + libafl/src/inputs/bytes.rs | 2 +- libafl/src/mutators/token_mutations.rs | 13 +- 5 files changed, 281 insertions(+), 9 deletions(-) create mode 100644 libafl/src/bolts/os/ashmem_server.rs diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index ba40bd8d7e..aafde444d2 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -452,7 +452,7 @@ where { #[cfg(all(feature = "std", unix))] pub fn on_domain_socket(filename: &str) -> Result { - match UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename).unwrap()) { + match UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?) { Ok(listener) => { dbg!("We're the broker"); let mut broker = LlmpBroker::new()?; @@ -732,7 +732,7 @@ where #[cfg(feature = "std")] return None; #[cfg(not(feature = "std"))] - panic!(&format!("Unexpected error allocing new msg {:?}", e)); + panic!("Unexpected error allocing new msg {:?}", e); } }; (*ret).message_id = (*last_msg).message_id + 1 diff --git a/libafl/src/bolts/os/ashmem_server.rs b/libafl/src/bolts/os/ashmem_server.rs new file mode 100644 index 0000000000..e5b544c8d4 --- /dev/null +++ b/libafl/src/bolts/os/ashmem_server.rs @@ -0,0 +1,268 @@ +/*! +On Android, we can only share maps between processes by serializing fds over sockets. +Hence, the `ashmem_server` keeps track of existing maps, creates new maps for clients, +and forwards them over unix domain sockets. +*/ + +use crate::{ + bolts::shmem::{ShMem, ShMemDescription, UnixShMem}, + Error, +}; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; +use std::io::{Read, Write}; + +#[cfg(all(feature = "std", unix))] +use nix::poll::{poll, PollFd, PollFlags}; + +#[cfg(all(feature = "std", unix))] +use std::{ + os::unix::{ + net::{UnixListener, UnixStream}, + {io::AsRawFd, prelude::RawFd}, + }, + thread, +}; + +#[cfg(all(unix, feature = "std"))] +use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; + +#[derive(Debug)] +/// The Sharedmem backed by a `ShmemService`a +pub struct ServedShMem { + stream: UnixStream, + shmem: Option, + slice: Option<[u8; 20]>, + fd: Option, +} +const ASHMEM_SERVER_NAME: &str = "@ashmem_server"; + +impl ServedShMem { + /// Create a new ServedShMem and connect to the ashmem server. + pub fn connect(name: &str) -> Self { + Self { + stream: UnixStream::connect_to_unix_addr(&UnixSocketAddr::from_abstract(name).unwrap()) + .expect("Failed to connect to the ashmem server"), + shmem: None, + slice: None, + fd: None, + } + } + + /// Send a request to the server, and wait for a response + fn send_receive(&mut self, request: AshmemRequest) -> ([u8; 20], RawFd) { + let body = postcard::to_allocvec(&request).unwrap(); + + let header = (body.len() as u32).to_be_bytes(); + let mut message = header.to_vec(); + message.extend(body); + + self.stream + .write_all(&message) + .expect("Failed to send message"); + + let mut shm_slice = [0u8; 20]; + let mut fd_buf = [-1; 1]; + self.stream + .recv_fds(&mut shm_slice, &mut fd_buf) + .expect("Did not receive a response"); + (shm_slice, fd_buf[0]) + } +} + +impl ShMem for ServedShMem { + fn new_map(map_size: usize) -> Result { + let mut res = Self::connect(ASHMEM_SERVER_NAME); + let (shm_slice, fd) = res.send_receive(AshmemRequest::NewMap(map_size)); + if fd == -1 { + Err(Error::IllegalState( + "Could not allocate from the ashmem server".to_string(), + )) + } else { + res.slice = Some(shm_slice); + res.fd = Some(fd); + res.shmem = Some( + UnixShMem::existing_from_shm_slice(&shm_slice, map_size) + .expect("Failed to create the UnixShMem"), + ); + Ok(res) + } + } + + fn existing_from_shm_slice( + map_str_bytes: &[u8; 20], + map_size: usize, + ) -> Result { + let mut res = Self::connect(ASHMEM_SERVER_NAME); + let (shm_slice, fd) = res.send_receive(AshmemRequest::ExistingMap(ShMemDescription { + size: map_size, + str_bytes: *map_str_bytes, + })); + if fd == -1 { + Err(Error::IllegalState( + "Could not allocate from the ashmem server".to_string(), + )) + } else { + res.slice = Some(shm_slice); + res.fd = Some(fd); + res.shmem = Some( + UnixShMem::existing_from_shm_slice(&shm_slice, map_size) + .expect("Failed to create the UnixShMem"), + ); + Ok(res) + } + } + + fn shm_slice(&self) -> &[u8; 20] { + self.slice.as_ref().unwrap() + } + + fn map(&self) -> &[u8] { + self.shmem.as_ref().unwrap().map() + } + + fn map_mut(&mut self) -> &mut [u8] { + self.shmem.as_mut().unwrap().map_mut() + } +} + +/// A request sent to the ShMem server to receive a fd to a shared map +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub enum AshmemRequest { + /// Register a new map with a given size. + NewMap(usize), + /// Another client already has a map with this description mapped. + ExistingMap(ShMemDescription), + /// A client tells us it unregisters the previously allocated map + Deregister(u32), +} + +#[derive(Debug)] +pub struct AshmemClient { + unix_socket_file: String, +} + +#[derive(Debug)] +pub struct AshmemService { + maps: HashMap<[u8; 20], UnixShMem>, +} + +impl AshmemService { + /// Create a new AshMem service + #[must_use] + fn new() -> Self { + AshmemService { + maps: HashMap::new(), + } + } + + /// Read and handle the client request, send the answer over unix fd. + fn handle_client(&mut self, stream: &mut UnixStream) -> Result<(), Error> { + // Always receive one be u32 of size, then the command. + let mut size_bytes = [0u8; 4]; + stream.read_exact(&mut size_bytes)?; + let size = u32::from_be_bytes(size_bytes); + let mut bytes = vec![]; + bytes.resize(size as usize, 0u8); + stream + .read_exact(&mut bytes) + .expect("Failed to read message body"); + let request: AshmemRequest = postcard::from_bytes(&bytes)?; + + // Handle the client request + let (shmem_slice, fd): ([u8; 20], RawFd) = match request { + AshmemRequest::NewMap(map_size) => match UnixShMem::new(map_size) { + Err(e) => { + println!("Error allocating shared map {:?}", e); + ([0; 20], -1) + } + Ok(map) => { + let res = (*map.shm_slice(), map.shm_id); + self.maps.insert(*map.shm_slice(), map); + res + } + }, + AshmemRequest::ExistingMap(description) => { + match self.maps.get(&description.str_bytes) { + None => { + println!("Error finding shared map {:?}", description); + ([0; 20], -1) + } + Some(map) => (*map.shm_slice(), map.shm_id), + } + } + AshmemRequest::Deregister(_) => { + return Ok(()); + } + }; + + stream.send_fds(&shmem_slice, &[fd])?; + Ok(()) + } + + /// Create a new AshmemService, then listen and service incoming connections in a new thread. + pub fn start() -> Result, Error> { + Ok(thread::spawn(move || { + Self::new().listen(ASHMEM_SERVER_NAME).unwrap() + })) + } + + /// Listen on a filename (or abstract name) for new connections and serve them. This function + /// should not return. + fn listen(&mut self, filename: &str) -> Result<(), Error> { + let listener = UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?)?; + let mut clients: HashMap = HashMap::new(); + let mut poll_fds: HashMap = HashMap::new(); + + poll_fds.insert( + listener.as_raw_fd(), + PollFd::new(listener.as_raw_fd(), PollFlags::POLLIN), + ); + + loop { + let mut fds_to_poll: Vec = poll_fds.values().copied().collect(); + let fd = match poll(&mut fds_to_poll, -1) { + Ok(fd) => fd, + Err(e) => { + println!("Error polling for activity: {:?}", e); + continue; + } + }; + if fd == listener.as_raw_fd() { + let (stream, addr) = match listener.accept_unix_addr() { + Ok(stream_val) => stream_val, + Err(e) => { + println!("Error accepting client: {:?}", e); + continue; + } + }; + + println!("Recieved connection from {:?}", addr); + let pollfd = PollFd::new(stream.as_raw_fd(), PollFlags::POLLIN); + poll_fds.insert(stream.as_raw_fd(), pollfd); + clients + .insert(stream.as_raw_fd(), (stream, addr)) + .as_ref() + .unwrap(); + } else if poll_fds + .get(&fd) + .unwrap() + .revents() + .unwrap() + .contains(PollFlags::POLLHUP) + { + poll_fds.remove(&fd); + clients.remove(&fd); + } else { + let (stream, _addr) = clients.get_mut(&fd).unwrap(); + match self.handle_client(stream) { + Ok(()) => (), + Err(e) => { + dbg!("Ignoring failed read from client", e); + continue; + } + }; + } + } + } +} diff --git a/libafl/src/bolts/os/mod.rs b/libafl/src/bolts/os/mod.rs index 4ea718107f..6aacc14fd8 100644 --- a/libafl/src/bolts/os/mod.rs +++ b/libafl/src/bolts/os/mod.rs @@ -1,5 +1,8 @@ //! Operating System specific abstractions +#[cfg(all(unix, feature = "std"))] +pub mod ashmem_server; + #[cfg(unix)] pub mod unix_signals; #[cfg(windows)] diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index b92675c709..7fe08a105c 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -1,4 +1,4 @@ -//! The BytesInput is the "normal" input, a map of bytes, that can be sent directly to the client +//! The `BytesInput` is the "normal" input, a map of bytes, that can be sent directly to the client //! (As opposed to other, more abstract, imputs, like an Grammar-Based AST Input) use alloc::{borrow::ToOwned, rc::Rc, vec::Vec}; diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index e38a3d65d6..90b76bb58d 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -1,5 +1,8 @@ //! Tokens are what afl calls extras or dictionaries. //! They may be inserted as part of mutations during fuzzing. +use alloc::vec::Vec; +use core::marker::PhantomData; +use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::{ fs::File, @@ -9,18 +12,16 @@ use std::{ use crate::{ inputs::{HasBytesVec, Input}, - mutators::{buffer_self_copy, mutations, str_decode, MutationResult, Mutator, Named}, + mutators::{buffer_self_copy, mutations, MutationResult, Mutator, Named}, state::{HasMaxSize, HasMetadata, HasRand}, utils::Rand, Error, }; -use core::marker::PhantomData; - -use alloc::vec::Vec; -use serde::{Deserialize, Serialize}; - use mutations::buffer_copy; +#[cfg(feature = "std")] +use crate::mutators::str_decode; + /// A state metadata holding a list of tokens #[derive(Serialize, Deserialize)] pub struct Tokens { From 24a033de31f5dc2dcef0497b04bf5f5a95fb1496 Mon Sep 17 00:00:00 2001 From: Grant Hernandez Date: Sat, 10 Apr 2021 02:16:35 -0700 Subject: [PATCH 054/104] Add tool detection to build.rs, improve README (#43) Co-authored-by: Dominik Maier --- fuzzers/frida_libpng/build.rs | 257 +++++++++++++++------------- fuzzers/libfuzzer_libpng/Cargo.toml | 13 ++ fuzzers/libfuzzer_libpng/README.md | 31 +++- 3 files changed, 179 insertions(+), 122 deletions(-) diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs index 63c8ae17ee..211ff7c400 100644 --- a/fuzzers/frida_libpng/build.rs +++ b/fuzzers/frida_libpng/build.rs @@ -1,119 +1,138 @@ -// build.rs - -use std::{ - env, - path::Path, - process::{exit, Command}, -}; - -const LIBPNG_URL: &str = - "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; - -fn main() { - if cfg!(windows) { - println!("cargo:warning=Skipping libpng frida example on Windows"); - exit(0); - } - - let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); - let out_dir = out_dir.to_string_lossy().to_string(); - let out_dir_path = Path::new(&out_dir); - std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir)); - - println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); - println!("cargo:rerun-if-changed=harness.cc"); - - let libpng = format!("{}/libpng-1.6.37", &out_dir); - let libpng_path = Path::new(&libpng); - let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); - - // Enforce clang for its -fsanitize-coverage support. - let clang = match env::var("CLANG_PATH") { - Ok(path) => path, - Err(_) => "clang".to_string(), - }; - let clangpp = format!("{}++", &clang); - std::env::set_var("CC", &clang); - std::env::set_var("CXX", &clangpp); - let ldflags = match env::var("LDFLAGS") { - Ok(val) => val, - Err(_) => "".to_string(), - }; - - // println!("cargo:warning=output path is {}", libpng); - if !libpng_path.is_dir() { - if !Path::new(&libpng_tar).is_file() { - println!("cargo:warning=Libpng not found, downloading..."); - // Download libpng - Command::new("wget") - .arg("-c") - .arg(LIBPNG_URL) - .arg("-O") - .arg(&libpng_tar) - .status() - .unwrap(); - } - Command::new("tar") - .current_dir(&out_dir_path) - .arg("xvf") - .arg(&libpng_tar) - .status() - .unwrap(); - Command::new(format!("{}/configure", &libpng)) - .current_dir(&libpng_path) - .args(&[ - "--disable-shared", - &format!("--host={}", env::var("TARGET").unwrap())[..], - ]) - .env("CC", &clang) - .env("CXX", &clangpp) - .env( - "CFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", - ) - .env( - "CXXFLAGS", - "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", - ) - .env( - "LDFLAGS", - //format!("-g -fPIE -fsanitize=address {}", ldflags), - format!("-g -fPIE {}", ldflags), - ) - .status() - .unwrap(); - Command::new("make") - .current_dir(&libpng_path) - .status() - .unwrap(); - } - - let status = cc::Build::new() - .cpp(true) - .get_compiler() - .to_command() - .current_dir(&cwd) - .arg("-I") - .arg(&libpng) - //.arg("-D") - //.arg("HAS_DUMMY_CRASH=1") - .arg("-fPIC") - .arg("-shared") - .arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" { - "-static-libstdc++" - } else { - "" - }) - .arg("-o") - .arg(format!("{}/libpng-harness.so", &out_dir)) - .arg("./harness.cc") - .arg(format!("{}/.libs/libpng16.a", &libpng)) - .arg("-l") - .arg("z") - .status() - .unwrap(); - assert!(status.success()); - - println!("cargo:rerun-if-changed=build.rs"); -} +// build.rs + +use std::{ + env, + path::Path, + process::{exit, Command}, +}; + +use which::which; + +const LIBPNG_URL: &str = + "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; + +fn build_dep_check(tools: &[&str]) { + for tool in tools.into_iter() { + println!("Checking for build tool {}...", tool); + + match which::which(tool) { + Ok(path) => println!("Found build tool {}", path.to_str().unwrap()), + Err(_) => { + println!("ERROR: missing build tool {}", tool); + exit(1); + } + }; + } +} + + +fn main() { + if cfg!(windows) { + println!("cargo:warning=Skipping libpng frida example on Windows"); + exit(0); + } + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + let out_dir = out_dir.to_string_lossy().to_string(); + let out_dir_path = Path::new(&out_dir); + std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir)); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); + println!("cargo:rerun-if-changed=harness.cc"); + + build_dep_check(&["clang", "clang++", "wget", "tar", "make"]); + + let libpng = format!("{}/libpng-1.6.37", &out_dir); + let libpng_path = Path::new(&libpng); + let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); + + // Enforce clang for its -fsanitize-coverage support. + let clang = match env::var("CLANG_PATH") { + Ok(path) => path, + Err(_) => "clang".to_string(), + }; + let clangpp = format!("{}++", &clang); + std::env::set_var("CC", &clang); + std::env::set_var("CXX", &clangpp); + let ldflags = match env::var("LDFLAGS") { + Ok(val) => val, + Err(_) => "".to_string(), + }; + + // println!("cargo:warning=output path is {}", libpng); + if !libpng_path.is_dir() { + if !Path::new(&libpng_tar).is_file() { + println!("cargo:warning=Libpng not found, downloading..."); + // Download libpng + Command::new("wget") + .arg("-c") + .arg(LIBPNG_URL) + .arg("-O") + .arg(&libpng_tar) + .status() + .unwrap(); + } + Command::new("tar") + .current_dir(&out_dir_path) + .arg("xvf") + .arg(&libpng_tar) + .status() + .unwrap(); + Command::new(format!("{}/configure", &libpng)) + .current_dir(&libpng_path) + .args(&[ + "--disable-shared", + &format!("--host={}", env::var("TARGET").unwrap())[..], + ]) + .env("CC", &clang) + .env("CXX", &clangpp) + .env( + "CFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "CXXFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "LDFLAGS", + //format!("-g -fPIE -fsanitize=address {}", ldflags), + format!("-g -fPIE {}", ldflags), + ) + .status() + .unwrap(); + Command::new("make") + .current_dir(&libpng_path) + .status() + .unwrap(); + } + + let status = cc::Build::new() + .cpp(true) + .get_compiler() + .to_command() + .current_dir(&cwd) + .arg("-I") + .arg(&libpng) + //.arg("-D") + //.arg("HAS_DUMMY_CRASH=1") + .arg("-fPIC") + .arg("-shared") + .arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" { + "-static-libstdc++" + } else { + "" + }) + .arg("-o") + .arg(format!("{}/libpng-harness.so", &out_dir)) + .arg("./harness.cc") + .arg(format!("{}/.libs/libpng16.a", &libpng)) + .arg("-l") + .arg("z") + .status() + .unwrap(); + assert!(status.success()); + +} diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index 1f125edadc..3bb4df74f4 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -8,11 +8,24 @@ edition = "2018" default = ["std"] std = [] +<<<<<<< HEAD +#[profile.release] +#lto = true +#codegen-units = 1 +#opt-level = 3 +#debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +which = { version = "4.0.2" } +num_cpus = "1.0" +======= [profile.release] lto = true codegen-units = 1 opt-level = 3 debug = true +>>>>>>> dev [dependencies] libafl = { path = "../../libafl/" } diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index acb7426f8a..17035c6ddc 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -1,11 +1,18 @@ # Libfuzzer for libpng This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. -To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. +To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example. It has been tested on Linux. ## Build +<<<<<<< HEAD +You will need `clang` and `clang++` along with basic build dependencies for this example to compile. +To build this example, run `cargo build --example libfuzzer_libpng --release`. +This will call [the build.rs](./build.rs), which in turn downloads a libpng archive from the web. +Then, it will link [the fuzzer](./src/fuzzer.rs) against [the C++ harness](./harness.cc) and the instrumented `libpng`. +Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. +======= To build this example, run `cargo build --release`. This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target. @@ -29,12 +36,30 @@ Now, we have to build the libfuzzer harness and link all togheter to create our ``` Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`. +>>>>>>> dev ## Run -The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. +The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Currently you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus. + +``` +cargo run --release --example libfuzzer_libpng + +[libafl/src/bolts/llmp.rs:407] "We're the broker" = "We\'re the broker" +Doing broker things. Run this tool again to start fuzzing in a client. +``` + +And after running the above again in a separate terminal: + +``` +[libafl/src/bolts/llmp.rs:1464] "New connection" = "New connection" +[libafl/src/bolts/llmp.rs:1464] addr = 127.0.0.1:33500 +[libafl/src/bolts/llmp.rs:1464] stream.peer_addr().unwrap() = 127.0.0.1:33500 +[LOG Debug]: Loaded 4 initial testcases. +[New Testcase #2] clients: 3, corpus: 6, objectives: 0, executions: 5, exec/sec: 0 +< fuzzing stats > +``` -Each following execution will run a fuzzer client. As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). This means each client will start itself again to listen for crashes and timeouts. By restarting the actual fuzzer, it can recover from these exit conditions. From 70be959b820b444655ddc9ec0e98305a2558c70d Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 10 Apr 2021 17:03:33 +0200 Subject: [PATCH 055/104] debug --- libafl/src/bolts/llmp.rs | 71 +++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index aafde444d2..57d7573c0e 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -677,8 +677,11 @@ where let buf_len_padded; let mut complete_msg_size = llmp_align(size_of::() + buf_len); let map = self.out_maps.last_mut().unwrap(); + println!("Allocating on map {:?} (size: {})", map, complete_msg_size); let page = map.page_mut(); + println!("Allocating {} (>={}) bytes on page {:?}", complete_msg_size, buf_len, page); let last_msg = self.last_msg_sent; + println!("last msg: {:?}", last_msg); /* DBG("XXX complete_msg_size %lu (h: %lu)\n", complete_msg_size, sizeof(llmp_message)); */ /* In case we don't have enough space, make sure the next page will be large * enough */ @@ -697,19 +700,21 @@ where buf_len_padded = llmp_align(base_addr + complete_msg_size) - base_addr - size_of::(); complete_msg_size = buf_len_padded + size_of::(); - /* DBG("XXX complete_msg_size NEW %lu\n", complete_msg_size); */ + dbg!("LLMP_DEBUG: complete_msg_size NEW {}", complete_msg_size); /* Still space for the new message plus the additional "we're full" message? */ + + dbg!((*page).size_used, complete_msg_size, EOP_MSG_SIZE, (*page).size_total); if (*page).size_used + complete_msg_size + EOP_MSG_SIZE > (*page).size_total { /* We're full. */ return None; } /* We need to start with 1 for ids, as current message id is initialized * with 0... */ - (*ret).message_id = if !last_msg.is_null() { - (*last_msg).message_id + 1 - } else { + (*ret).message_id = if last_msg.is_null() { 1 + } else { + (*last_msg).message_id + 1 } } else if (*page).current_msg_id != (*last_msg).message_id { /* Oops, wrong usage! */ @@ -755,7 +760,9 @@ where (*ret).buf_len = buf_len as u64; /* DBG("Returning new message at %p with len %ld, TAG was %x", ret, ret->buf_len_padded, ret->tag); */ /* Maybe catch some bugs... */ - (*_llmp_next_msg_ptr(ret)).tag = LLMP_TAG_UNSET; + (dbg!(*_llmp_next_msg_ptr(ret))).tag = LLMP_TAG_UNSET; + dbg!(ret); + dbg!(*ret); (*ret).tag = LLMP_TAG_UNINITIALIZED; Some(ret) } @@ -765,6 +772,8 @@ where /// It will be read by the consuming threads (broker->clients or client->broker) #[inline(never)] // Not inlined to make cpu-level reodering (hopefully?) improbable unsafe fn send(&mut self, msg: *mut LlmpMsg) -> Result<(), Error> { + dbg!("Sending msg {:?}", msg); + if self.last_msg_sent == msg { panic!("Message sent twice!"); } @@ -805,6 +814,8 @@ where let old_map = self.out_maps.last_mut().unwrap().page_mut(); + println!("New Map Size {}", new_map_size((*old_map).max_alloc_size)); + // Create a new shard page. let mut new_map_shmem = LlmpSharedMap::new( (*old_map).sender, @@ -812,28 +823,42 @@ where ); let mut new_map = new_map_shmem.page_mut(); + println!("got new map at: {:?}", new_map); + ptr::write_volatile(&mut (*new_map).current_msg_id, (*old_map).current_msg_id); + + println!("Setting max alloc size: {:?}", (*old_map).max_alloc_size); + (*new_map).max_alloc_size = (*old_map).max_alloc_size; /* On the old map, place a last message linking to the new map for the clients * to consume */ let mut out: *mut LlmpMsg = self.alloc_eop()?; + println!("out"); (*out).sender = (*old_map).sender; + println!("sender"); let mut end_of_page_msg = (*out).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; + println!("size"); (*end_of_page_msg).map_size = new_map_shmem.shmem.map().len(); + println!("str"); (*end_of_page_msg).shm_str = *new_map_shmem.shmem.shm_slice(); + println!("send"); + /* Send the last msg on the old buf */ + self.send(out)?; + println!("sent"); + + // Set the new page as current page. + self.out_maps.push(new_map_shmem); // We never sent a msg on the new buf */ self.last_msg_sent = ptr::null_mut(); - /* Send the last msg on the old buf */ - self.send(out)?; - + // If we want to get red if old pages, (client to broker), do that now if !self.keep_pages_forever { + println!("pruning"); self.prune_old_pages(); } - - self.out_maps.push(new_map_shmem); + println!("Done"); Ok(()) } @@ -849,6 +874,8 @@ where self.handle_out_eop()?; } + println!("Handled out eop"); + match unsafe { self.alloc_next_if_space(buf_len) } { Some(msg) => Ok(msg), None => Err(Error::Unknown(format!( @@ -1173,18 +1200,18 @@ where /// Maps and wraps an existing pub fn existing(existing_map: SH) -> Self { #[cfg(all(feature = "llmp_debug", feature = "std"))] - { - #[cfg(debug_assertions)] - let bt = Backtrace::new(); - #[cfg(not(debug_assertions))] - let bt = ""; - println!( - "LLMP_DEBUG: Using existing map {} with size {}, bt: {:?}", - existing_map.shm_str(), - existing_map.map().len(), - bt - ); - } + //{ + //#[cfg(debug_assertions)] + //let bt = Backtrace::new(); + //#[cfg(not(debug_assertions))] + //let bt = ""; + dbg!( + "LLMP_DEBUG: Using existing map {} with size {}", + existing_map.shm_str(), + existing_map.map().len(), + //bt + ); + //} let ret = Self { shmem: existing_map, From fb0a23e767b8bd66a6972e72203f9a298ada84be Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 10 Apr 2021 19:57:30 +0200 Subject: [PATCH 056/104] cleaned up potential panic --- libafl/src/corpus/minimizer.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libafl/src/corpus/minimizer.rs b/libafl/src/corpus/minimizer.rs index 6b90d9dad3..e4f12f95a1 100644 --- a/libafl/src/corpus/minimizer.rs +++ b/libafl/src/corpus/minimizer.rs @@ -199,15 +199,15 @@ where /// Cull the `Corpus` using the `MinimizerCorpusScheduler` #[allow(clippy::unused_self)] pub fn cull(&self, state: &mut S) -> Result<(), Error> { - if state.metadata().get::().is_none() { - return Ok(()); - } - let mut acc = HashSet::new(); - let top_rated = state.metadata().get::().unwrap(); + let top_rated = match state.metadata().get::() { + None => return Ok(()), + Some(val) => val, + }; - for key in top_rated.map.keys() { + let mut acc = HashSet::new(); + + for (key, idx) in &top_rated.map { if !acc.contains(key) { - let idx = top_rated.map.get(key).unwrap(); let mut entry = state.corpus().get(*idx)?.borrow_mut(); let meta = entry.metadata().get::().ok_or_else(|| { Error::KeyNotFound(format!( From 9bc22d033111f747d3fae165e37f820b631c29e6 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 11 Apr 2021 02:21:09 +0200 Subject: [PATCH 057/104] Llmp Fixes (#51) * fixed llmp --- libafl/src/bolts/llmp.rs | 60 ++++++++++++++++++++++----------------- libafl/src/bolts/shmem.rs | 8 +++--- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 57d7573c0e..af745e6d2c 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -237,7 +237,7 @@ fn msg_offset_from_env(env_name: &str) -> Result, Error> { fn new_map_size(max_alloc: usize) -> usize { max( max_alloc * 2 + EOP_MSG_SIZE + LLMP_PAGE_HEADER_LEN, - LLMP_CFG_INITIAL_MAP_SIZE, + LLMP_CFG_INITIAL_MAP_SIZE - 1, ) .next_power_of_two() } @@ -546,7 +546,7 @@ where last_msg_sent: ptr::null_mut(), out_maps: vec![LlmpSharedMap::new( 0, - SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?, + SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, )], // drop pages to the broker if it already read them keep_pages_forever, @@ -659,7 +659,10 @@ where if (*ret).tag == LLMP_TAG_UNINITIALIZED { panic!("Did not call send() on last message!"); } - (*ret).buf_len_padded = size_of::() as u64; + (*ret).buf_len = size_of::() as u64; + + // We don't need to pad the EOP message: it'll always be the last in this page. + (*ret).buf_len_padded = (*ret).buf_len; (*ret).message_id = if !last_msg.is_null() { (*last_msg).message_id + 1 } else { @@ -677,11 +680,13 @@ where let buf_len_padded; let mut complete_msg_size = llmp_align(size_of::() + buf_len); let map = self.out_maps.last_mut().unwrap(); - println!("Allocating on map {:?} (size: {})", map, complete_msg_size); let page = map.page_mut(); - println!("Allocating {} (>={}) bytes on page {:?}", complete_msg_size, buf_len, page); let last_msg = self.last_msg_sent; - println!("last msg: {:?}", last_msg); + #[cfg(all(feature = "llmp_debug", feature = "std"))] + println!( + "Allocating {} (>={}) bytes on page {:?} / map {:?} (last msg: {:?})", + complete_msg_size, buf_len, page, map, last_msg + ); /* DBG("XXX complete_msg_size %lu (h: %lu)\n", complete_msg_size, sizeof(llmp_message)); */ /* In case we don't have enough space, make sure the next page will be large * enough */ @@ -700,11 +705,16 @@ where buf_len_padded = llmp_align(base_addr + complete_msg_size) - base_addr - size_of::(); complete_msg_size = buf_len_padded + size_of::(); - dbg!("LLMP_DEBUG: complete_msg_size NEW {}", complete_msg_size); /* Still space for the new message plus the additional "we're full" message? */ - dbg!((*page).size_used, complete_msg_size, EOP_MSG_SIZE, (*page).size_total); + #[cfg(all(feature = "llmp_debug", feature = "std"))] + dbg!( + (*page).size_used, + complete_msg_size, + EOP_MSG_SIZE, + (*page).size_total + ); if (*page).size_used + complete_msg_size + EOP_MSG_SIZE > (*page).size_total { /* We're full. */ return None; @@ -760,9 +770,7 @@ where (*ret).buf_len = buf_len as u64; /* DBG("Returning new message at %p with len %ld, TAG was %x", ret, ret->buf_len_padded, ret->tag); */ /* Maybe catch some bugs... */ - (dbg!(*_llmp_next_msg_ptr(ret))).tag = LLMP_TAG_UNSET; - dbg!(ret); - dbg!(*ret); + (*_llmp_next_msg_ptr(ret)).tag = LLMP_TAG_UNSET; (*ret).tag = LLMP_TAG_UNINITIALIZED; Some(ret) } @@ -772,7 +780,7 @@ where /// It will be read by the consuming threads (broker->clients or client->broker) #[inline(never)] // Not inlined to make cpu-level reodering (hopefully?) improbable unsafe fn send(&mut self, msg: *mut LlmpMsg) -> Result<(), Error> { - dbg!("Sending msg {:?}", msg); + // dbg!("Sending msg {:?}", msg); if self.last_msg_sent == msg { panic!("Message sent twice!"); @@ -814,6 +822,7 @@ where let old_map = self.out_maps.last_mut().unwrap().page_mut(); + #[cfg(all(feature = "llmp_debug", feature = "std"))] println!("New Map Size {}", new_map_size((*old_map).max_alloc_size)); // Create a new shard page. @@ -823,30 +832,26 @@ where ); let mut new_map = new_map_shmem.page_mut(); + #[cfg(all(feature = "llmp_debug", feature = "std"))] println!("got new map at: {:?}", new_map); ptr::write_volatile(&mut (*new_map).current_msg_id, (*old_map).current_msg_id); + #[cfg(all(feature = "llmp_debug", feature = "std"))] println!("Setting max alloc size: {:?}", (*old_map).max_alloc_size); (*new_map).max_alloc_size = (*old_map).max_alloc_size; /* On the old map, place a last message linking to the new map for the clients * to consume */ let mut out: *mut LlmpMsg = self.alloc_eop()?; - println!("out"); (*out).sender = (*old_map).sender; - println!("sender"); let mut end_of_page_msg = (*out).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - println!("size"); (*end_of_page_msg).map_size = new_map_shmem.shmem.map().len(); - println!("str"); (*end_of_page_msg).shm_str = *new_map_shmem.shmem.shm_slice(); - println!("send"); /* Send the last msg on the old buf */ self.send(out)?; - println!("sent"); // Set the new page as current page. self.out_maps.push(new_map_shmem); @@ -855,10 +860,10 @@ where // If we want to get red if old pages, (client to broker), do that now if !self.keep_pages_forever { + #[cfg(all(feature = "llmp_debug", feature = "std"))] println!("pruning"); self.prune_old_pages(); } - println!("Done"); Ok(()) } @@ -874,6 +879,7 @@ where self.handle_out_eop()?; } + #[cfg(all(feature = "llmp_debug", feature = "std"))] println!("Handled out eop"); match unsafe { self.alloc_next_if_space(buf_len) } { @@ -1046,7 +1052,8 @@ where // Handle end of page if (*msg).buf_len < size_of::() as u64 { panic!( - "Illegal message length for EOP (is {}, expected {})", + "Illegal message length for EOP (is {}/{}, expected {})", + (*msg).buf_len, (*msg).buf_len_padded, size_of::() ); @@ -1057,6 +1064,9 @@ where Copy the contents first to be safe (probably fine in rust either way). */ let pageinfo_cpy = *pageinfo; + // Set last msg we received to null (as the map may no longer exist) + self.last_msg_recvd = ptr::null(); + // Mark the old page save to unmap, in case we didn't so earlier. ptr::write_volatile(&mut (*page).save_to_unmap, 1); @@ -1397,7 +1407,8 @@ where If we should need zero copy, we could instead post a link to the original msg with the map_id and offset. */ let actual_size = (*out).buf_len_padded; - msg.copy_to_nonoverlapping(out, size_of::() + (*msg).buf_len_padded as usize); + let complete_size = actual_size as usize + size_of::(); + (msg as *const u8).copy_to_nonoverlapping(out as *mut u8, complete_size); (*out).buf_len_padded = actual_size; /* We need to replace the message ID with our own */ if let Err(e) = self.llmp_out.send(out) { @@ -1507,10 +1518,7 @@ where let llmp_tcp_id = self.llmp_clients.len() as u32; // Tcp out map sends messages from background thread tcp server to foreground client - let tcp_out_map = LlmpSharedMap::new( - llmp_tcp_id, - SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?, - ); + let tcp_out_map = LlmpSharedMap::new(llmp_tcp_id, SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?); let tcp_out_map_str = tcp_out_map.shmem.shm_str(); let tcp_out_map_size = tcp_out_map.shmem.map().len(); self.register_client(tcp_out_map); @@ -1822,7 +1830,7 @@ where last_msg_sent: ptr::null_mut(), out_maps: vec![LlmpSharedMap::new( 0, - SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?, + SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, )], // drop pages to the broker if it already read them keep_pages_forever: false, diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index f87b7afbfc..b2f43c4344 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -344,13 +344,13 @@ pub mod unix_shmem { pub fn new(map_size: usize) -> Result { let mut ret = unix_shmem_unitialized(); let map = unsafe { unix_shmem_init(&mut ret, map_size) }; - if !map.is_null() { - Ok(ret) - } else { + if map.is_null() { Err(Error::Unknown(format!( - "Could not allocate map of size {}", + "Could not allocate map of size {} - check OS limits, (i.e. shmall, shmmax)!", map_size ))) + } else { + Ok(ret) } } } From 357b807c338b76be841f26cc9899d3caf5875b65 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 11 Apr 2021 04:07:58 +0200 Subject: [PATCH 058/104] clippy warning muted --- libafl/src/stages/mutational.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index f8024b1fe3..0809f938d8 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -39,6 +39,7 @@ where fn iterations(&self, state: &mut S) -> usize; /// Runs this (mutational) stage for the given testcase + #[allow(clippy::clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... fn perform_mutational( &mut self, state: &mut S, From e505e7689c614d35fb1e0d81887d204e2936a10d Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 12 Apr 2021 11:30:44 +0200 Subject: [PATCH 059/104] fixed merge fails --- fuzzers/frida_libpng/Cargo.toml | 1 + fuzzers/frida_libpng/README.md | 2 +- fuzzers/frida_libpng/build.rs | 4 +--- fuzzers/libfuzzer_libpng/Cargo.toml | 8 -------- fuzzers/libfuzzer_libpng/README.md | 8 -------- 5 files changed, 3 insertions(+), 20 deletions(-) diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 4fe870bfdc..ae06dd2ea4 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -19,6 +19,7 @@ debug = true [build-dependencies] cc = { version = "1.0", features = ["parallel"] } num_cpus = "1.0" +which = "4.1" [target.'cfg(unix)'.dependencies] libafl = { path = "../../libafl/" } diff --git a/fuzzers/frida_libpng/README.md b/fuzzers/frida_libpng/README.md index f56138c2b5..dd032eb121 100644 --- a/fuzzers/frida_libpng/README.md +++ b/fuzzers/frida_libpng/README.md @@ -7,7 +7,7 @@ It has been tested on Linux. ## Build To build this example, run `cargo build --example libfuzzer_libpng --release`. -This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. +This will call (the build.rs)[./build.rs], which in turn downloads a libpng archive from the web. Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs index 211ff7c400..d6d7e094bf 100644 --- a/fuzzers/frida_libpng/build.rs +++ b/fuzzers/frida_libpng/build.rs @@ -15,7 +15,7 @@ fn build_dep_check(tools: &[&str]) { for tool in tools.into_iter() { println!("Checking for build tool {}...", tool); - match which::which(tool) { + match which(tool) { Ok(path) => println!("Found build tool {}", path.to_str().unwrap()), Err(_) => { println!("ERROR: missing build tool {}", tool); @@ -25,7 +25,6 @@ fn build_dep_check(tools: &[&str]) { } } - fn main() { if cfg!(windows) { println!("cargo:warning=Skipping libpng frida example on Windows"); @@ -134,5 +133,4 @@ fn main() { .status() .unwrap(); assert!(status.success()); - } diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index 3bb4df74f4..589fc6bb6b 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" default = ["std"] std = [] -<<<<<<< HEAD #[profile.release] #lto = true #codegen-units = 1 @@ -19,13 +18,6 @@ std = [] cc = { version = "1.0", features = ["parallel"] } which = { version = "4.0.2" } num_cpus = "1.0" -======= -[profile.release] -lto = true -codegen-units = 1 -opt-level = 3 -debug = true ->>>>>>> dev [dependencies] libafl = { path = "../../libafl/" } diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index 17035c6ddc..9d33d2f724 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -6,13 +6,6 @@ It has been tested on Linux. ## Build -<<<<<<< HEAD -You will need `clang` and `clang++` along with basic build dependencies for this example to compile. -To build this example, run `cargo build --example libfuzzer_libpng --release`. -This will call [the build.rs](./build.rs), which in turn downloads a libpng archive from the web. -Then, it will link [the fuzzer](./src/fuzzer.rs) against [the C++ harness](./harness.cc) and the instrumented `libpng`. -Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. -======= To build this example, run `cargo build --release`. This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target. @@ -36,7 +29,6 @@ Now, we have to build the libfuzzer harness and link all togheter to create our ``` Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`. ->>>>>>> dev ## Run From 022c12568b0fed98073f2371655e231bd49e5a07 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 12 Apr 2021 12:16:45 +0200 Subject: [PATCH 060/104] QoL improvements --- .gitignore | 3 ++ fuzzers/libfuzzer_libpng/README.md | 34 ++++++++++++------- .../src/bin/{cc.rs => libafl_cc.rs} | 2 ++ .../src/bin/{cxx.rs => libafl_cxx.rs} | 2 ++ libafl_cc/src/lib.rs | 8 ++++- 5 files changed, 36 insertions(+), 13 deletions(-) rename fuzzers/libfuzzer_libpng/src/bin/{cc.rs => libafl_cc.rs} (93%) rename fuzzers/libfuzzer_libpng/src/bin/{cxx.rs => libafl_cxx.rs} (94%) diff --git a/.gitignore b/.gitignore index 0c466bba6e..3727f15757 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ perf.data.old .vscode test.dict + +# Ignore all built fuzzers +fuzzer_* \ No newline at end of file diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index 9d33d2f724..8d88900306 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -6,36 +6,46 @@ It has been tested on Linux. ## Build -To build this example, run `cargo build --release`. -This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. -In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target. - -Then download libpng from https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz and unpack the archive. - -Now compile it with: +To build this example, run +```bash +cargo build --release ``` + +This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. +In addition, it will also build two C and C++ compiler wrappers (bin/libafl_c(libafl_c/xx).rs) that you must use to compile the target. + +Then download libpng, and unpack the archive: +```bash +wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz +tar -xvf libpng-1.6.37.tar.xz +``` + +Now compile libpng, using the libafl_cc compiler wrapper: + +```bash cd libpng-1.6.37 ./configure -make CC=/path/to/libfuzzer_libpng/target/release/cc -j `nproc` +make CC=../target/release/libafl_cc CXX=../target/release/libafl_cxx -j `nproc` ``` You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`. -Now, we have to build the libfuzzer harness and link all togheter to create our fuzzer binary. +Now, we have to build the libfuzzer harness and link all together to create our fuzzer binary. ``` -/path/to/libfuzzer_libpng/target/debug/cxx /path/to/libfuzzer_libpng/harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer -lz -lm +cd .. +./target/release/libafl_cxx ./harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer_libpng -lz -lm ``` -Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`. +Afterwards, the fuzzer will be ready to run. ## Run The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Currently you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus. ``` -cargo run --release --example libfuzzer_libpng +./fuzzer_libpng [libafl/src/bolts/llmp.rs:407] "We're the broker" = "We\'re the broker" Doing broker things. Run this tool again to start fuzzing in a client. diff --git a/fuzzers/libfuzzer_libpng/src/bin/cc.rs b/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs similarity index 93% rename from fuzzers/libfuzzer_libpng/src/bin/cc.rs rename to fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs index ad5b05c084..dc65a9a57b 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cc.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs @@ -27,5 +27,7 @@ fn main() { .add_link_arg("-lAdvapi32".into()) .unwrap(); cc.run().unwrap(); + } else { + panic!("LibAFL CC: No Arguments given"); } } diff --git a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs b/fuzzers/libfuzzer_libpng/src/bin/libafl_cxx.rs similarity index 94% rename from fuzzers/libfuzzer_libpng/src/bin/cxx.rs rename to fuzzers/libfuzzer_libpng/src/bin/libafl_cxx.rs index 842c915ba2..2183682d96 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/cxx.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/libafl_cxx.rs @@ -28,5 +28,7 @@ fn main() { .add_link_arg("-lAdvapi32".into()) .unwrap(); cc.run().unwrap(); + } else { + panic!("LibAFL CC: No Arguments given"); } } diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index c26675aceb..87fcb27117 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -90,7 +90,13 @@ impl CompilerWrapper for ClangWrapper { let mut new_args = vec![]; if args.is_empty() { return Err(Error::InvalidArguments( - "The number of arguments cannot be 0".into(), + "The number of arguments cannot be 0".to_string(), + )); + } + + if args.len() == 1 { + return Err(Error::InvalidArguments( + "LibAFL Compiler wrapper - no commands specified. Use me as compiler.".to_string(), )); } From 5846aa2292fc6e770da06902d687429246c2884d Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 12 Apr 2021 15:26:32 +0200 Subject: [PATCH 061/104] cmplog runtime --- libafl_targets/Cargo.toml | 1 + libafl_targets/build.rs | 9 ++ libafl_targets/src/cmplog.c | 185 +++++++++++++++++++++++++++++++++++ libafl_targets/src/cmplog.rs | 36 +++++++ libafl_targets/src/lib.rs | 6 ++ 5 files changed, 237 insertions(+) create mode 100644 libafl_targets/src/cmplog.c create mode 100644 libafl_targets/src/cmplog.rs diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index 67d4e88e64..d0863696ba 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -10,6 +10,7 @@ pcguard_edges = [] pcguard_hitcounts = [] libfuzzer = [] value_profile = [] +cmplog = [] pcguard = ["pcguard_hitcounts"] [build-dependencies] diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 01caad4adf..c8745ca084 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -29,6 +29,15 @@ fn main() { .file(_src_dir.join("value_profile.c")) .compile("value_profile"); } + + #[cfg(feature = "cmplog")] + { + println!("cargo:rerun-if-changed=src/cmplog.c"); + + cc::Build::new() + .file(_src_dir.join("cmplog.c")) + .compile("cmplog"); + } println!("cargo:rustc-link-search=native={}", &out_dir); diff --git a/libafl_targets/src/cmplog.c b/libafl_targets/src/cmplog.c new file mode 100644 index 0000000000..efa7acbbcb --- /dev/null +++ b/libafl_targets/src/cmplog.c @@ -0,0 +1,185 @@ +#include + +#define CMPLOG_MAP_W 65536 +#define CMPLOG_MAP_H 32 + +#define CMPLOG_KIND_INS 0 +#define CMPLOG_KIND_RTN 1 + +typedef struct CmpLogHeader { + uint16_t hits; + uint8_t shape; + uint8_t kind; +} CmpLogHeader; + +typedef struct CmpLogOperands { + uint64_t v0; + uint64_t v1; +} CmpLogOperands; + +typedef struct CmpLogMap { + CmpLogHeader headers[CMPLOG_MAP_W]; + CmpLogOperands operands[CMPLOG_MAP_W][CMPLOG_MAP_H]; +} CmpLogMap; + +extern CmpLogMap libafl_cmplog_map; + +extern uint8_t libafl_cmplog_enabled; + +#if defined(__APPLE__) + #pragma weak __sanitizer_cov_trace_const_cmp1 = __sanitizer_cov_trace_cmp1 + #pragma weak __sanitizer_cov_trace_const_cmp2 = __sanitizer_cov_trace_cmp2 + #pragma weak __sanitizer_cov_trace_const_cmp4 = __sanitizer_cov_trace_cmp4 + #pragma weak __sanitizer_cov_trace_const_cmp8 = __sanitizer_cov_trace_cmp8 +#else +void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__sanitizer_cov_trace_cmp1"))); +void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) + __attribute__((alias("__sanitizer_cov_trace_cmp2"))); +void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) + __attribute__((alias("__sanitizer_cov_trace_cmp4"))); +void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) + __attribute__((alias("__sanitizer_cov_trace_cmp8"))); +#endif + +void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { + + if (!libafl_cmplog_enabled) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMPLOG_MAP_W - 1; + + uint16_t hits; + if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { + libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; + libafl_cmplog_map.headers[k].hits = 1; + libafl_cmplog_map.headers[k].shape = 1; + hits = 0; + } else { + hits = libafl_cmplog_map.headers[k].hits++; + if (libafl_cmplog_map.headers[k].shape < 1) { + libafl_cmplog_map.headers[k].shape = 1; + } + } + + hits &= CMPLOG_MAP_H - 1; + libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; + libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; + +} + +void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { + + if (!libafl_cmplog_enabled) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMPLOG_MAP_W - 1; + + uint16_t hits; + if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { + libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; + libafl_cmplog_map.headers[k].hits = 1; + libafl_cmplog_map.headers[k].shape = 2; + hits = 0; + } else { + hits = libafl_cmplog_map.headers[k].hits++; + if (libafl_cmplog_map.headers[k].shape < 2) { + libafl_cmplog_map.headers[k].shape = 2; + } + } + + hits &= CMPLOG_MAP_H - 1; + libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; + libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; + +} + +void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { + + if (!libafl_cmplog_enabled) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMPLOG_MAP_W - 1; + + uint16_t hits; + if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { + libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; + libafl_cmplog_map.headers[k].hits = 1; + libafl_cmplog_map.headers[k].shape = 4; + hits = 0; + } else { + hits = libafl_cmplog_map.headers[k].hits++; + if (libafl_cmplog_map.headers[k].shape < 4) { + libafl_cmplog_map.headers[k].shape = 4; + } + } + + hits &= CMPLOG_MAP_H - 1; + libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; + libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; +} + +void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { + + if (!libafl_cmplog_enabled) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMPLOG_MAP_W - 1; + + uint16_t hits; + if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { + libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; + libafl_cmplog_map.headers[k].hits = 1; + libafl_cmplog_map.headers[k].shape = 8; + hits = 0; + } else { + hits = libafl_cmplog_map.headers[k].hits++; + if (libafl_cmplog_map.headers[k].shape < 8) { + libafl_cmplog_map.headers[k].shape = 8; + } + } + + hits &= CMPLOG_MAP_H - 1; + libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; + libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; + +} + +void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { + + if (!libafl_cmplog_enabled) return; + + uint8_t shape = (uint8_t)cases[1]; + if (shape) { + shape /= 8; + } + + for (uint64_t i = 0; i < cases[0]; i++) { + + uintptr_t k = (uintptr_t)__builtin_return_address(0) + i; + k = (k >> 4) ^ (k << 8); + k &= CMPLOG_MAP_W - 1; + + uint16_t hits; + if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { + libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; + libafl_cmplog_map.headers[k].hits = 1; + libafl_cmplog_map.headers[k].shape = shape; + hits = 0; + } else { + hits = libafl_cmplog_map.headers[k].hits++; + if (libafl_cmplog_map.headers[k].shape < shape) { + libafl_cmplog_map.headers[k].shape = shape; + } + } + + hits &= CMPLOG_MAP_H - 1; + libafl_cmplog_map.operands[k][hits].v0 = val; + libafl_cmplog_map.operands[k][hits].v1 = cases[i + 2]; + + } + +} diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs new file mode 100644 index 0000000000..6731cad6e1 --- /dev/null +++ b/libafl_targets/src/cmplog.rs @@ -0,0 +1,36 @@ +// TODO compile time flag +pub const CMPLOG_MAP_W: usize = 65536; +pub const CMPLOG_MAP_H: usize = 32; +pub const CMPLOG_MAP_SIZE: usize = CMPLOG_MAP_W * CMPLOG_MAP_H; + +pub const CMPLOG_KIND_INS: u8 = 0; +pub const CMPLOG_KIND_RTN: u8 = 1; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct CmpLogHeader { + hits: u16, + shape: u8, + kind: u8, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct CmpLogOperands(u64, u64); + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct CmpLogMap { + headers: [CmpLogHeader; CMPLOG_MAP_W], + operands: [[CmpLogOperands; CMPLOG_MAP_H]; CMPLOG_MAP_W], +} + +#[no_mangle] +pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap { headers: [CmpLogHeader {hits: 0, shape: 0, kind: 0}; CMPLOG_MAP_W], operands: [[CmpLogOperands(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W] }; + +pub use libafl_cmplog_map as CMPLOG_MAP; + +#[no_mangle] +pub static mut libafl_cmplog_enabled: u8 = 0; + +pub use libafl_cmplog_enabled as CMPLOG_ENABLED; diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index e8f1636554..bddfde4743 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -14,3 +14,9 @@ pub use value_profile::*; pub mod libfuzzer; #[cfg(feature = "libfuzzer")] pub use libfuzzer::*; + +#[cfg(feature = "cmplog")] +pub mod cmplog; +#[cfg(feature = "cmplog")] +pub use cmplog::*; + From 15917fa189f150757fe8b8027286d315252d1ec2 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 12 Apr 2021 15:26:59 +0200 Subject: [PATCH 062/104] format --- libafl_targets/build.rs | 2 +- libafl_targets/src/cmplog.rs | 9 ++++++++- libafl_targets/src/lib.rs | 1 - 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index c8745ca084..3c7d83ccb5 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -29,7 +29,7 @@ fn main() { .file(_src_dir.join("value_profile.c")) .compile("value_profile"); } - + #[cfg(feature = "cmplog")] { println!("cargo:rerun-if-changed=src/cmplog.c"); diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs index 6731cad6e1..c2d997556e 100644 --- a/libafl_targets/src/cmplog.rs +++ b/libafl_targets/src/cmplog.rs @@ -26,7 +26,14 @@ pub struct CmpLogMap { } #[no_mangle] -pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap { headers: [CmpLogHeader {hits: 0, shape: 0, kind: 0}; CMPLOG_MAP_W], operands: [[CmpLogOperands(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W] }; +pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap { + headers: [CmpLogHeader { + hits: 0, + shape: 0, + kind: 0, + }; CMPLOG_MAP_W], + operands: [[CmpLogOperands(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W], +}; pub use libafl_cmplog_map as CMPLOG_MAP; diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index bddfde4743..9ab270f050 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -19,4 +19,3 @@ pub use libfuzzer::*; pub mod cmplog; #[cfg(feature = "cmplog")] pub use cmplog::*; - From 6f9a81b7992afadc7efd30726127a4101dc1d337 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 15 Apr 2021 11:29:59 +0200 Subject: [PATCH 063/104] docs --- docs/src/design/architecture.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/src/design/architecture.md b/docs/src/design/architecture.md index 886c187e10..0dde078b77 100644 --- a/docs/src/design/architecture.md +++ b/docs/src/design/architecture.md @@ -1,3 +1,13 @@ # Architecture -The LibAFL architecture +The LibAFL architecture is built around some entities to allow code reuse and low-cost abstractions. + +Initially, we started thinking to implement LibAFL in an Object Oriented language, such C++. When we landed to Rust, we immediately changed our idea as we realized that, while Rust allow a sort of OOP pattern, we can build the library using a more sane approach like the one described in [this blogpost](https://kyren.github.io/2018/09/14/rustconf-talk.html) about game design in Rust. + +The LibAFL code reuse meachanism is so based on components rather than sub-classes, but there are still some OOP patterns in the library. + +Thinking about similar fuzzers, you can observe that most of the times the data structures that are modified are the ones related to testcases and the fuzzer global state. + +Beside the entities described previously, we then introduce the Testcase and State entities. The Testcase is a container for an Input stored in the Corpus and its metadata (so, in the implementation, the Corpus stores Testcases) and the State contains all the metadata that are evolved while running the fuzzer, Corpus included. + + From 655d30519be3d4beecbadd25aac9a9b33a72f118 Mon Sep 17 00:00:00 2001 From: s1341 Date: Fri, 16 Apr 2021 12:26:06 +0300 Subject: [PATCH 064/104] Convert ShMem into a state-full ShMemProvider and otherwise refactor shmem/llmp (#54) * shmeme/llmp refactor to convert ShMem into a stateful ShMemProvider factory. At the moment we use parking_lot::ReentrantMutex. That may not be necessary. * fix merge issue * formatting * Fix fuzzer examples for new ShMemProvider * Fix clippy warnings * Fix build and clippy for x86_64 * Resolve review comments * Remove ReentrantMutex and RefCell - they are not needed * Hopefully fix win32 build * Fix tests, windows build * Rename ShMemProvider to ShMem * Revert "Rename ShMemProvider to ShMem" This reverts commit eca07c8d7bb3d5e829fecf3f7213c763470a41e9. * Rename ShMemMapping to ShMem; Test fixes * Add missing trait to scope * Fix from_int * Fix try_into * Move to alloc::sync::Arc and spin::Mutex to support nostd * Fix tests * nostd fixes; Make new() a part of the ShMemProvider trait * Fix errant ? * Fix windows * Fix missing trait * nostd remove dbg! * Add Default and Clone to ShMemProvider * Formatting * Fix windows * Get rid of ArcMutex in favor of RefCell * Rc RefCell * moved to refs * SHP->SP * Use alloc::rc::Rc instead of std::rc::Rc * Format * Add setup_restarting_mgr_std which selects the right ShMemProvider; changed fuzzers to use it * Get rid of unnecessary clone * Fix clippy error on windows * Fix nostd * Fix formatting * Make StdShmemProvider include ServedShMemProvider * Get rid of lifetime specifiers now that we are using Rc * Get rid of unneccesary spin * Rename ShMemProvider::Mapping to ShMemProvider::Mem * Formatting * fix Windows * Rename DefaultUnixShmem* to CommonUnixShmem* Co-authored-by: Dominik Maier --- fuzzers/frida_libpng/src/fuzzer.rs | 13 +- fuzzers/libfuzzer_libmozjpeg/src/lib.rs | 18 +- fuzzers/libfuzzer_libpng/src/lib.rs | 6 +- libafl/examples/llmp_test/main.rs | 26 +- libafl/src/bolts/llmp.rs | 578 ++++++-------- libafl/src/bolts/os/ashmem_server.rs | 344 +++++---- libafl/src/bolts/shmem.rs | 987 +++++++++++++----------- libafl/src/events/llmp.rs | 216 +++--- 8 files changed, 1159 insertions(+), 1029 deletions(-) diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index b9cc649bbc..1c816c16e1 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -2,15 +2,12 @@ //! The example harness is built for libpng. use libafl::{ - bolts::{ - shmem::UnixShMem, - tuples::{tuple_list, Named}, - }, + bolts::tuples::{tuple_list, Named}, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, }, - events::{setup_restarting_mgr, EventManager}, + events::{setup_restarting_mgr_std, EventManager}, executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -25,6 +22,7 @@ use libafl::{ Error, }; +use core::cell::RefCell; #[cfg(target_arch = "x86_64")] use frida_gum::instruction_writer::X86Register; #[cfg(target_arch = "aarch64")] @@ -34,8 +32,7 @@ use frida_gum::{ stalker::{NoneEventSink, Stalker, Transformer}, }; use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection}; - -use std::{cell::RefCell, env, ffi::c_void, path::PathBuf}; +use std::{env, ffi::c_void, path::PathBuf}; /// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation pub trait FridaHelper<'a> { @@ -399,7 +396,7 @@ unsafe fn fuzz( // The restarting state will spawn the same process again as child, then restarted it each time it crashes. let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { + match setup_restarting_mgr_std(stats, broker_port) { Ok(res) => res, Err(err) => match err { Error::ShuttingDown => { diff --git a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs index 14d8556eb5..6c42c3eb47 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs @@ -4,9 +4,9 @@ use std::{env, path::PathBuf}; use libafl::{ - bolts::{shmem::UnixShMem, tuples::tuple_list}, + bolts::tuples::tuple_list, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, - events::setup_restarting_mgr, + events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -20,9 +20,11 @@ use libafl::{ Error, }; -use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM, CMP_MAP, CMP_MAP_SIZE}; +use libafl_targets::{ + libfuzzer_initialize, libfuzzer_test_one_input, CMP_MAP, CMP_MAP_SIZE, EDGES_MAP, MAX_EDGES_NUM, +}; -const ALLOC_MAP_SIZE: usize = 16*1024; +const ALLOC_MAP_SIZE: usize = 16 * 1024; extern "C" { static mut libafl_alloc_map: [usize; ALLOC_MAP_SIZE]; } @@ -53,17 +55,19 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The restarting state will spawn the same process again as child, then restarted it each time it crashes. let (state, mut restarting_mgr) = - setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) + setup_restarting_mgr_std(stats, broker_port) .expect("Failed to setup the restarter".into()); // Create an observation channel using the coverage map - let edges_observer = StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM }); + let edges_observer = + StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM }); // Create an observation channel using the cmp map let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP }, CMP_MAP_SIZE); // Create an observation channel using the allocations map - let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }, ALLOC_MAP_SIZE); + let allocs_observer = + StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }, ALLOC_MAP_SIZE); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 92cd079c24..0a4a661e9c 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -5,12 +5,12 @@ use core::time::Duration; use std::{env, path::PathBuf}; use libafl::{ - bolts::{shmem::StdShMem, tuples::tuple_list}, + bolts::tuples::tuple_list, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, }, - events::setup_restarting_mgr, + events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -52,7 +52,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The restarting state will spawn the same process again as child, then restarted it each time it crashes. let (state, mut restarting_mgr) = - match setup_restarting_mgr::<_, _, StdShMem, _>(stats, broker_port) { + match setup_restarting_mgr_std(stats, broker_port) { Ok(res) => res, Err(err) => match err { Error::ShuttingDown => { diff --git a/libafl/examples/llmp_test/main.rs b/libafl/examples/llmp_test/main.rs index fcb03d3872..eda837b958 100644 --- a/libafl/examples/llmp_test/main.rs +++ b/libafl/examples/llmp_test/main.rs @@ -3,15 +3,19 @@ This shows how llmp can be used directly, without libafl abstractions */ extern crate alloc; +use alloc::rc::Rc; #[cfg(all(unix, feature = "std"))] -use core::{convert::TryInto, time::Duration}; +use core::{cell::RefCell, convert::TryInto, time::Duration}; #[cfg(all(unix, feature = "std"))] use std::{thread, time}; use libafl::bolts::llmp::Tag; #[cfg(all(unix, feature = "std"))] use libafl::{ - bolts::{llmp, shmem::UnixShMem}, + bolts::{ + llmp, + shmem::{ShMemProvider, StdShMemProvider}, + }, Error, }; @@ -21,7 +25,8 @@ const _TAG_1MEG_V1: Tag = 0xB1111161; #[cfg(all(unix, feature = "std"))] fn adder_loop(port: u16) -> ! { - let mut client = llmp::LlmpClient::::create_attach_to_tcp(port).unwrap(); + let shmem_provider = Rc::new(RefCell::new(StdShMemProvider::new())); + let mut client = llmp::LlmpClient::create_attach_to_tcp(&shmem_provider, port).unwrap(); let mut last_result: u32 = 0; let mut current_result: u32 = 0; loop { @@ -63,7 +68,11 @@ fn adder_loop(port: u16) -> ! { #[cfg(all(unix, feature = "std"))] fn large_msg_loop(port: u16) -> ! { - let mut client = llmp::LlmpClient::::create_attach_to_tcp(port).unwrap(); + let mut client = llmp::LlmpClient::create_attach_to_tcp( + &Rc::new(RefCell::new(StdShMemProvider::new())), + port, + ) + .unwrap(); let meg_buf = [1u8; 1 << 20]; @@ -124,7 +133,8 @@ fn main() { match mode.as_str() { "broker" => { - let mut broker = llmp::LlmpBroker::::new().unwrap(); + let mut broker = + llmp::LlmpBroker::new(&Rc::new(RefCell::new(StdShMemProvider::new()))).unwrap(); broker .launch_listener(llmp::Listener::Tcp( std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(), @@ -133,7 +143,11 @@ fn main() { broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5))) } "ctr" => { - let mut client = llmp::LlmpClient::::create_attach_to_tcp(port).unwrap(); + let mut client = llmp::LlmpClient::create_attach_to_tcp( + &Rc::new(RefCell::new(StdShMemProvider::new())), + port, + ) + .unwrap(); let mut counter: u32 = 0; loop { counter = counter.wrapping_add(1); diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index af745e6d2c..2cbadc4655 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -52,8 +52,9 @@ Then register some clientloops using llmp_broker_register_threaded_clientloop */ -use alloc::{string::String, vec::Vec}; +use alloc::{rc::Rc, string::String, vec::Vec}; use core::{ + cell::RefCell, cmp::max, fmt::Debug, mem::size_of, @@ -64,38 +65,19 @@ use core::{ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::{ - env, fs, + env, io::{Read, Write}, net::{SocketAddr, TcpListener, TcpStream}, thread, }; -#[cfg(all(feature = "std", unix))] -use nix::{ - cmsg_space, - sys::{ - socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags}, - uio::IoVec, - }, -}; - -#[cfg(all(feature = "std", unix))] -use std::os::unix::{ - self, - net::{UnixListener, UnixStream}, - {io::AsRawFd, prelude::RawFd}, -}; - #[cfg(all(feature = "llmp_debug", feature = "std"))] use backtrace::Backtrace; -#[cfg(all(unix, feature = "std"))] -use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; - #[cfg(unix)] use crate::bolts::os::unix_signals::{c_void, setup_signal_handler, siginfo_t, Handler, Signal}; use crate::{ - bolts::shmem::{ShMem, ShMemDescription}, + bolts::shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider}, Error, }; @@ -146,15 +128,11 @@ pub type Tag = u32; #[cfg(feature = "std")] pub enum Listener { Tcp(TcpListener), - #[cfg(unix)] - Unix(UnixListener), } #[cfg(feature = "std")] pub enum ListenerStream { Tcp(TcpStream, SocketAddr), - #[cfg(unix)] - Unix(UnixStream, unix::net::SocketAddr), Empty(), } @@ -169,27 +147,19 @@ impl Listener { ListenerStream::Empty() } }, - #[cfg(unix)] - Listener::Unix(inner) => match inner.accept() { - Ok(res) => ListenerStream::Unix(res.0, res.1), - Err(err) => { - dbg!("Ignoring failed accept", err); - ListenerStream::Empty() - } - }, } } } /// Get sharedmem from a page #[inline] -unsafe fn shmem2page_mut(afl_shmem: &mut SH) -> *mut LlmpPage { +unsafe fn shmem2page_mut(afl_shmem: &mut SHM) -> *mut LlmpPage { afl_shmem.map_mut().as_mut_ptr() as *mut LlmpPage } /// Get sharedmem from a page #[inline] -unsafe fn shmem2page(afl_shmem: &SH) -> *const LlmpPage { +unsafe fn shmem2page(afl_shmem: &SHM) -> *const LlmpPage { afl_shmem.map().as_ptr() as *const LlmpPage } @@ -244,7 +214,7 @@ fn new_map_size(max_alloc: usize) -> usize { /// Initialize a new llmp_page. size should be relative to /// llmp_page->messages -unsafe fn _llmp_page_init(shmem: &mut SH, sender: u32, allow_reinit: bool) { +unsafe fn _llmp_page_init(shmem: &mut SHM, sender: u32, allow_reinit: bool) { let map_size = shmem.map().len(); let page = shmem2page_mut(shmem); if (*page).magic == PAGE_INITIALIZED_MAGIC && !allow_reinit { @@ -268,8 +238,8 @@ unsafe fn _llmp_page_init(shmem: &mut SH, sender: u32, allow_reinit: /// Get the next pointer and make sure it's in the current page, and has enough space. #[inline] -unsafe fn llmp_next_msg_ptr_checked( - map: &mut LlmpSharedMap, +unsafe fn llmp_next_msg_ptr_checked( + map: &mut LlmpSharedMap, last_msg: *const LlmpMsg, alloc_size: usize, ) -> Result<*mut LlmpMsg, Error> { @@ -347,7 +317,7 @@ impl LlmpMsg { /// Gets the buffer from this message as slice, with the corrent length. #[inline] - pub fn as_slice(&self, map: &mut LlmpSharedMap) -> Result<&[u8], Error> { + pub fn as_slice(&self, map: &mut LlmpSharedMap) -> Result<&[u8], Error> { unsafe { if self.in_map(map) { Ok(self.as_slice_unsafe()) @@ -359,7 +329,7 @@ impl LlmpMsg { /// Returns true, if the pointer is, indeed, in the page of this shared map. #[inline] - pub fn in_map(&self, map: &mut LlmpSharedMap) -> bool { + pub fn in_map(&self, map: &mut LlmpSharedMap) -> bool { unsafe { let map_size = map.shmem.map().len(); let buf_ptr = self.buf.as_ptr(); @@ -378,39 +348,40 @@ impl LlmpMsg { } /// An Llmp instance -#[derive(Clone, Debug)] -pub enum LlmpConnection +#[derive(Debug)] +pub enum LlmpConnection where - SH: ShMem, + SP: ShMemProvider + 'static, { /// A broker and a thread using this tcp background thread - IsBroker { broker: LlmpBroker }, + IsBroker { broker: LlmpBroker }, /// A client, connected to the port - IsClient { client: LlmpClient }, + IsClient { client: LlmpClient }, } -impl LlmpConnection +impl LlmpConnection where - SH: ShMem, + SP: ShMemProvider, { #[cfg(feature = "std")] /// Creates either a broker, if the tcp port is not bound, or a client, connected to this port. - pub fn on_port(port: u16) -> Result { + pub fn on_port(shmem_provider: &Rc>, port: u16) -> Result { match TcpListener::bind(format!("127.0.0.1:{}", port)) { Ok(listener) => { // We got the port. We are the broker! :) dbg!("We're the broker"); - let mut broker = LlmpBroker::new()?; + let mut broker = LlmpBroker::new(shmem_provider)?; let _listener_thread = broker.launch_listener(Listener::Tcp(listener))?; Ok(LlmpConnection::IsBroker { broker }) } Err(e) => { + println!("error: {:?}", e); match e.kind() { std::io::ErrorKind::AddrInUse => { // We are the client :) dbg!("We're the client", e); Ok(LlmpConnection::IsClient { - client: LlmpClient::create_attach_to_tcp(port)?, + client: LlmpClient::create_attach_to_tcp(shmem_provider, port)?, }) } _ => Err(Error::File(e)), @@ -419,6 +390,12 @@ where } } + pub fn shmem_provider(&mut self) -> &Rc> { + match self { + LlmpConnection::IsBroker { broker } => &broker.shmem_provider, + LlmpConnection::IsClient { client } => &client.shmem_provider, + } + } /// Describe this in a reproducable fashion, if it's a client pub fn describe(&self) -> Result { Ok(match self { @@ -429,10 +406,11 @@ where /// Recreate an existing client from the stored description pub fn existing_client_from_description( + shmem_provider: &Rc>, description: &LlmpClientDescription, - ) -> Result, Error> { + ) -> Result, Error> { Ok(LlmpConnection::IsClient { - client: LlmpClient::existing_client_from_description(description)?, + client: LlmpClient::existing_client_from_description(shmem_provider, description)?, }) } @@ -445,37 +423,6 @@ where } } -#[cfg(all(unix, feature = "std"))] -impl LlmpConnection -where - SH: ShMem, -{ - #[cfg(all(feature = "std", unix))] - pub fn on_domain_socket(filename: &str) -> Result { - match UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?) { - Ok(listener) => { - dbg!("We're the broker"); - let mut broker = LlmpBroker::new()?; - broker.socket_name = Some(filename.to_string()); - let _listener_thread = broker.launch_listener(Listener::Unix(listener))?; - Ok(LlmpConnection::IsBroker { broker }) - } - Err(e) => { - match e.kind() { - std::io::ErrorKind::AddrInUse => { - // We are the client :) - dbg!("We're the client", e); - Ok(LlmpConnection::IsClient { - client: LlmpClient::create_attach_to_unix(filename)?, - }) - } - _ => Err(Error::File(e)), - } - } - } - } -} - /// Contents of the share mem pages, used by llmp internally #[derive(Copy, Clone, Debug)] #[repr(C, packed)] @@ -516,10 +463,10 @@ struct LlmpPayloadSharedMapInfo { } /// Sending end on a (unidirectional) sharedmap channel -#[derive(Clone, Debug)] -pub struct LlmpSender +#[derive(Debug)] +pub struct LlmpSender where - SH: ShMem, + SP: ShMemProvider, { /// ID of this sender. Only used in the broker. pub id: u32, @@ -527,29 +474,37 @@ where /// If null, a new page (just) started. pub last_msg_sent: *const LlmpMsg, /// A vec of page wrappers, each containing an intialized AfShmem - pub out_maps: Vec>, + pub out_maps: Vec>, /// If true, pages will never be pruned. /// The broker uses this feature. /// By keeping the message history around, /// new clients may join at any time in the future. pub keep_pages_forever: bool, + shmem_provider: Rc>, } -/// An actor on the sendin part of the shared map -impl LlmpSender +/// An actor on the sending part of the shared map +impl LlmpSender where - SH: ShMem, + SP: ShMemProvider, { - pub fn new(id: u32, keep_pages_forever: bool) -> Result { + pub fn new( + shmem_provider: &Rc>, + id: u32, + keep_pages_forever: bool, + ) -> Result { Ok(Self { id, last_msg_sent: ptr::null_mut(), out_maps: vec![LlmpSharedMap::new( 0, - SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, + shmem_provider + .borrow_mut() + .new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, )], // drop pages to the broker if it already read them keep_pages_forever, + shmem_provider: shmem_provider.clone(), }) } @@ -565,9 +520,16 @@ where /// Reattach to a vacant out_map, to with a previous sender stored the information in an env before. #[cfg(feature = "std")] - pub fn on_existing_from_env(env_name: &str) -> Result { + pub fn on_existing_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { let msg_sent_offset = msg_offset_from_env(env_name)?; - Self::on_existing_map(SH::existing_from_env(env_name)?, msg_sent_offset) + Self::on_existing_map( + shmem_provider.clone(), + shmem_provider.borrow_mut().existing_from_env(env_name)?, + msg_sent_offset, + ) } /// Store the info to this sender to env. @@ -603,7 +565,8 @@ where /// It is essential, that the receiver (or someone else) keeps a pointer to this map /// else reattach will get a new, empty page, from the OS, or fail. pub fn on_existing_map( - current_out_map: SH, + shmem_provider: Rc>, + current_out_map: SP::Mem, last_msg_sent_offset: Option, ) -> Result { let mut out_map = LlmpSharedMap::existing(current_out_map); @@ -618,6 +581,7 @@ where out_maps: vec![out_map], // drop pages to the broker if it already read them keep_pages_forever: false, + shmem_provider, }) } @@ -710,6 +674,8 @@ where #[cfg(all(feature = "llmp_debug", feature = "std"))] dbg!( + page, + (*page), (*page).size_used, complete_msg_size, EOP_MSG_SIZE, @@ -814,8 +780,8 @@ where let shm = self.out_maps.last().unwrap(); println!( "LLMP_DEBUG: End of page reached for map {} with len {}, sending EOP, bt: {:?}", - shm.shmem.shm_str(), - shm.shmem.map().len(), + shm.shmem.id().to_string(), + shm.shmem.len(), bt ); } @@ -828,7 +794,9 @@ where // Create a new shard page. let mut new_map_shmem = LlmpSharedMap::new( (*old_map).sender, - SH::new_map(new_map_size((*old_map).max_alloc_size))?, + self.shmem_provider + .borrow_mut() + .new_map(new_map_size((*old_map).max_alloc_size))?, ); let mut new_map = new_map_shmem.page_mut(); @@ -847,8 +815,8 @@ where (*out).sender = (*old_map).sender; let mut end_of_page_msg = (*out).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*end_of_page_msg).map_size = new_map_shmem.shmem.map().len(); - (*end_of_page_msg).shm_str = *new_map_shmem.shmem.shm_slice(); + (*end_of_page_msg).map_size = new_map_shmem.shmem.len(); + (*end_of_page_msg).shm_str = *new_map_shmem.shmem.id().as_slice(); /* Send the last msg on the old buf */ self.send(out)?; @@ -940,37 +908,49 @@ where } // Create this client on an existing map from the given description. acquired with `self.describe` - pub fn on_existing_from_description(description: &LlmpDescription) -> Result { + pub fn on_existing_from_description( + shmem_provider: &Rc>, + description: &LlmpDescription, + ) -> Result { Self::on_existing_map( - SH::existing_from_description(&description.shmem)?, + shmem_provider.clone(), + shmem_provider + .borrow_mut() + .from_description(description.shmem)?, description.last_message_offset, ) } } /// Receiving end on a (unidirectional) sharedmap channel -#[derive(Clone, Debug)] -pub struct LlmpReceiver +#[derive(Debug)] +pub struct LlmpReceiver where - SH: ShMem, + SP: ShMemProvider, { pub id: u32, /// Pointer to the last meg this received pub last_msg_recvd: *const LlmpMsg, + /// The shmem provider + pub shmem_provider: Rc>, /// current page. After EOP, this gets replaced with the new one - pub current_recv_map: LlmpSharedMap, + pub current_recv_map: LlmpSharedMap, } /// Receiving end of an llmp channel -impl LlmpReceiver +impl LlmpReceiver where - SH: ShMem, + SP: ShMemProvider, { /// Reattach to a vacant recv_map, to with a previous sender stored the information in an env before. #[cfg(feature = "std")] - pub fn on_existing_from_env(env_name: &str) -> Result { + pub fn on_existing_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { Self::on_existing_map( - SH::existing_from_env(env_name)?, + shmem_provider.clone(), + shmem_provider.borrow_mut().existing_from_env(env_name)?, msg_offset_from_env(env_name)?, ) } @@ -988,7 +968,8 @@ where /// It is essential, that the sender (or someone else) keeps a pointer to the sender_map /// else reattach will get a new, empty page, from the OS, or fail. pub fn on_existing_map( - current_sender_map: SH, + shmem_provider: Rc>, + current_sender_map: SP::Mem, last_msg_recvd_offset: Option, ) -> Result { let mut current_recv_map = LlmpSharedMap::existing(current_sender_map); @@ -1001,6 +982,7 @@ where id: 0, current_recv_map, last_msg_recvd, + shmem_provider, }) } @@ -1071,10 +1053,12 @@ where ptr::write_volatile(&mut (*page).save_to_unmap, 1); // Map the new page. The old one should be unmapped by Drop - self.current_recv_map = LlmpSharedMap::existing(SH::existing_from_shm_slice( - &pageinfo_cpy.shm_str, - pageinfo_cpy.map_size, - )?); + self.current_recv_map = LlmpSharedMap::existing( + self.shmem_provider.borrow_mut().from_id_and_size( + ShMemId::from_slice(&pageinfo_cpy.shm_str), + pageinfo_cpy.map_size, + )?, + ); page = self.current_recv_map.page_mut(); // Mark the new page save to unmap also (it's mapped by us, the broker now) ptr::write_volatile(&mut (*page).save_to_unmap, 1); @@ -1082,8 +1066,8 @@ where #[cfg(all(feature = "llmp_debug", feature = "std"))] println!( "LLMP_DEBUG: Got a new recv map {} with len {:?}", - self.current_recv_map.shmem.shm_str(), - self.current_recv_map.shmem.map().len() + self.current_recv_map.shmem.id().to_string(), + self.current_recv_map.shmem.len() ); // After we mapped the new page, return the next message, if available return self.recv(); @@ -1166,9 +1150,15 @@ where } // Create this client on an existing map from the given description. acquired with `self.describe` - pub fn on_existing_from_description(description: &LlmpDescription) -> Result { + pub fn on_existing_from_description( + shmem_provider: &Rc>, + description: &LlmpDescription, + ) -> Result { Self::on_existing_map( - SH::existing_from_description(&description.shmem)?, + shmem_provider.clone(), + shmem_provider + .borrow_mut() + .from_description(description.shmem)?, description.last_message_offset, ) } @@ -1176,29 +1166,29 @@ where /// A page wrapper #[derive(Clone, Debug)] -pub struct LlmpSharedMap +pub struct LlmpSharedMap where - SH: ShMem, + SHM: ShMem, { /// Shmem containg the actual (unsafe) page, /// shared between one LlmpSender and one LlmpReceiver - pub shmem: SH, + pub shmem: SHM, } // TODO: May be obsolete /// The page struct, placed on a shared mem instance. /// A thin wrapper around a ShMem implementation, with special Llmp funcs -impl LlmpSharedMap +impl LlmpSharedMap where - SH: ShMem, + SHM: ShMem, { /// Creates a new page, initializing the passed shared mem struct - pub fn new(sender: u32, mut new_map: SH) -> Self { + pub fn new(sender: u32, mut new_map: SHM) -> Self { #[cfg(all(feature = "llmp_debug", feature = "std"))] println!( "LLMP_DEBUG: Initializing map on {} with size {}", - new_map.shm_str(), - new_map.map().len() + new_map.id().to_string(), + new_map.len() ); unsafe { @@ -1208,7 +1198,7 @@ where } /// Maps and wraps an existing - pub fn existing(existing_map: SH) -> Self { + pub fn existing(existing_map: SHM) -> Self { #[cfg(all(feature = "llmp_debug", feature = "std"))] //{ //#[cfg(debug_assertions)] @@ -1217,8 +1207,8 @@ where //let bt = ""; dbg!( "LLMP_DEBUG: Using existing map {} with size {}", - existing_map.shm_str(), - existing_map.map().len(), + existing_map.id().to_string(), + existing_map.len(), //bt ); //} @@ -1321,22 +1311,24 @@ where } /// The broker (node 0) -#[derive(Clone, Debug)] -pub struct LlmpBroker +#[derive(Debug)] +pub struct LlmpBroker where - SH: ShMem, + SP: ShMemProvider + 'static, { /// Broadcast map from broker to all clients - pub llmp_out: LlmpSender, + pub llmp_out: LlmpSender, /// Users of Llmp can add message handlers in the broker. /// This allows us to intercept messages right in the broker /// This keeps the out map clean. - pub llmp_clients: Vec>, + pub llmp_clients: Vec>, /// This is the socket name, when unix domain sockets are used. socket_name: Option, /// This flag is used to indicate that shutdown has been requested by the SIGINT and SIGTERM /// handlers shutting_down: bool, + /// The ShMemProvider to use + shmem_provider: Rc>, } #[cfg(unix)] @@ -1357,27 +1349,30 @@ impl Handler for LlmpBrokerSignalHandler { /// The broker forwards all messages to its own bus-like broadcast map. /// It may intercept messages passing through. -impl LlmpBroker +impl LlmpBroker where - SH: ShMem, + SP: ShMemProvider, { /// Create and initialize a new llmp_broker - pub fn new() -> Result { - let broker = LlmpBroker { + pub fn new(shmem_provider: &Rc>) -> Result { + Ok(LlmpBroker { llmp_out: LlmpSender { id: 0, last_msg_sent: ptr::null_mut(), - out_maps: vec![LlmpSharedMap::new(0, SH::new_map(new_map_size(0))?)], + out_maps: vec![LlmpSharedMap::new( + 0, + shmem_provider.borrow_mut().new_map(new_map_size(0))?, + )], // Broker never cleans up the pages so that new // clients may join at any time keep_pages_forever: true, + shmem_provider: shmem_provider.clone(), }, llmp_clients: vec![], socket_name: None, shutting_down: false, - }; - - Ok(broker) + shmem_provider: shmem_provider.clone(), + }) } /// Allocate the next message on the outgoing map @@ -1387,7 +1382,7 @@ where /// Registers a new client for the given sharedmap str and size. /// Returns the id of the new client in broker.client_map - pub fn register_client(&mut self, mut client_page: LlmpSharedMap) { + pub fn register_client(&mut self, mut client_page: LlmpSharedMap) { // Tell the client it may unmap this page now. client_page.mark_save_to_unmap(); @@ -1396,6 +1391,7 @@ where id, current_recv_map: client_page, last_msg_recvd: ptr::null_mut(), + shmem_provider: self.shmem_provider.clone(), }); } @@ -1511,27 +1507,41 @@ where let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; let broadcast_map_description = postcard::to_allocvec(&client_out_map_mem.description())?; - let client_out_map_mem_fd: i32 = client_out_map_mem.shm_str().parse().unwrap(); let mut incoming_map_description_serialized = vec![0u8; broadcast_map_description.len()]; let llmp_tcp_id = self.llmp_clients.len() as u32; // Tcp out map sends messages from background thread tcp server to foreground client - let tcp_out_map = LlmpSharedMap::new(llmp_tcp_id, SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?); - let tcp_out_map_str = tcp_out_map.shmem.shm_str(); - let tcp_out_map_size = tcp_out_map.shmem.map().len(); + let tcp_out_map = LlmpSharedMap::new( + llmp_tcp_id, + self.shmem_provider + .borrow_mut() + .new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, + ); + let shmem_id = tcp_out_map.shmem.id(); + let tcp_out_map_str = *shmem_id.as_slice(); + let tcp_out_map_size = tcp_out_map.shmem.len(); self.register_client(tcp_out_map); + let shmem_provider_clone = self.shmem_provider.borrow_mut().clone(); + Ok(thread::spawn(move || { + let shmem_provider = Rc::new(RefCell::new(shmem_provider_clone)); + // Clone so we get a new connection to the AshmemServer if we are using + // ServedShMemProvider let mut new_client_sender = LlmpSender { id: 0, last_msg_sent: ptr::null_mut(), out_maps: vec![LlmpSharedMap::existing( - SH::existing_from_shm_str(&tcp_out_map_str, tcp_out_map_size).unwrap(), + shmem_provider + .borrow_mut() + .from_id_and_size(ShMemId::from_slice(&tcp_out_map_str), tcp_out_map_size) + .unwrap(), )], // drop pages to the broker if it already read them keep_pages_forever: false, + shmem_provider: shmem_provider.clone(), }; loop { @@ -1562,7 +1572,7 @@ where (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*pageinfo).shm_str = incoming_map_description.str_bytes; + (*pageinfo).shm_str = *incoming_map_description.id.as_slice(); (*pageinfo).map_size = incoming_map_description.size; match new_client_sender.send(msg) { Ok(()) => (), @@ -1571,65 +1581,6 @@ where } } } - #[cfg(unix)] - ListenerStream::Unix(stream, addr) => unsafe { - dbg!("New connection", addr); - - let broadcast_fd_initial: i32 = client_out_map_mem_fd; - - match sendmsg( - stream.as_raw_fd(), - &[IoVec::from_slice(b"\x00")], - &[ControlMessage::ScmRights(&[broadcast_fd_initial])], - MsgFlags::empty(), - None, - ) { - Ok(_) => {} - Err(err) => { - dbg!("Error sending fd over stream: {}", err); - continue; - } - }; - - let mut buf = [0u8; 5]; - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg( - stream.as_raw_fd(), - &[IoVec::from_mut_slice(&mut buf[..])], - Some(&mut cmsgspace), - MsgFlags::empty(), - ) - .unwrap(); - - for cmsg in msg.cmsgs() { - if let ControlMessageOwned::ScmRights(fds) = cmsg { - for fd in fds { - let mut fdstr = [0u8; 20]; - match write!(&mut fdstr[..], "{}", fd) { - Ok(_) => {} - Err(_) => { - dbg!("error converting fd to string"); - } - } - - let msg = new_client_sender - .alloc_next(size_of::()) - .expect("Could not allocate a new message in shared map."); - (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; - let pageinfo = - (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*pageinfo).shm_str = fdstr; - (*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE; - match new_client_sender.send(msg) { - Ok(()) => (), - Err(e) => { - println!("Error forwarding client on map: {:?}", e) - } - }; - } - } - } - }, ListenerStream::Empty() => { continue; } @@ -1676,7 +1627,10 @@ where } else { let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - match SH::existing_from_shm_slice(&(*pageinfo).shm_str, (*pageinfo).map_size) { + match self.shmem_provider.borrow_mut().from_id_and_size( + ShMemId::from_slice(&(*pageinfo).shm_str), + (*pageinfo).map_size, + ) { Ok(new_map) => { let mut new_page = LlmpSharedMap::existing(new_map); let id = next_id; @@ -1686,6 +1640,7 @@ where id, current_recv_map: new_page, last_msg_recvd: ptr::null_mut(), + shmem_provider: self.shmem_provider.clone(), }); } Err(e) => { @@ -1716,24 +1671,6 @@ where } } -#[cfg(feature = "std")] -impl Drop for LlmpBroker -where - SH: ShMem, -{ - fn drop(&mut self) { - match &self.socket_name { - Some(name) => match fs::remove_file(&name) { - Ok(_) => {} - Err(err) => { - dbg!("failed to close socket: {}", err); - } - }, - None => {} - } - } -} - /// A restorable client description #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct LlmpClientDescription { @@ -1744,44 +1681,65 @@ pub struct LlmpClientDescription { } /// Client side of LLMP -#[derive(Clone, Debug)] -pub struct LlmpClient +#[derive(Debug)] +pub struct LlmpClient where - SH: ShMem, + SP: ShMemProvider, { + shmem_provider: Rc>, /// Outgoing channel to the broker - pub sender: LlmpSender, + pub sender: LlmpSender, /// Incoming (broker) broadcast map - pub receiver: LlmpReceiver, + pub receiver: LlmpReceiver, } /// `n` clients connect to a broker. They share an outgoing map with the broker, /// and get incoming messages from the shared broker bus -impl LlmpClient +impl LlmpClient where - SH: ShMem, + SP: ShMemProvider, { /// Reattach to a vacant client map. /// It is essential, that the broker (or someone else) kept a pointer to the out_map /// else reattach will get a new, empty page, from the OS, or fail pub fn on_existing_map( - current_out_map: SH, - last_msg_sent_offset: Option, - current_broker_map: SH, + shmem_provider: Rc>, + _current_out_map: SP::Mem, + _last_msg_sent_offset: Option, + current_broker_map: SP::Mem, last_msg_recvd_offset: Option, ) -> Result { Ok(Self { - receiver: LlmpReceiver::on_existing_map(current_broker_map, last_msg_recvd_offset)?, - sender: LlmpSender::on_existing_map(current_out_map, last_msg_sent_offset)?, + receiver: LlmpReceiver::on_existing_map( + shmem_provider.clone(), + current_broker_map.clone(), + last_msg_recvd_offset, + )?, + sender: LlmpSender::on_existing_map( + shmem_provider.clone(), + current_broker_map, + last_msg_recvd_offset, + )?, + shmem_provider, }) } /// Recreate this client from a previous client.to_env #[cfg(feature = "std")] - pub fn on_existing_from_env(env_name: &str) -> Result { + pub fn on_existing_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { Ok(Self { - sender: LlmpSender::on_existing_from_env(&format!("{}_SENDER", env_name))?, - receiver: LlmpReceiver::on_existing_from_env(&format!("{}_RECEIVER", env_name))?, + sender: LlmpSender::on_existing_from_env( + shmem_provider, + &format!("{}_SENDER", env_name), + )?, + receiver: LlmpReceiver::on_existing_from_env( + shmem_provider, + &format!("{}_RECEIVER", env_name), + )?, + shmem_provider: shmem_provider.clone(), }) } @@ -1803,11 +1761,16 @@ where /// Create an existing client from description fn existing_client_from_description( + shmem_provider: &Rc>, description: &LlmpClientDescription, ) -> Result { Ok(Self { - sender: LlmpSender::on_existing_from_description(&description.sender)?, - receiver: LlmpReceiver::on_existing_from_description(&description.receiver)?, + sender: LlmpSender::on_existing_from_description(shmem_provider, &description.sender)?, + receiver: LlmpReceiver::on_existing_from_description( + shmem_provider, + &description.receiver, + )?, + shmem_provider: shmem_provider.clone(), }) } @@ -1823,24 +1786,31 @@ where } /// Creates a new LlmpClient - pub fn new(initial_broker_map: LlmpSharedMap) -> Result { + pub fn new( + shmem_provider: &Rc>, + initial_broker_map: LlmpSharedMap, + ) -> Result { Ok(Self { sender: LlmpSender { id: 0, last_msg_sent: ptr::null_mut(), - out_maps: vec![LlmpSharedMap::new( - 0, - SH::new_map(LLMP_CFG_INITIAL_MAP_SIZE)?, - )], + out_maps: vec![LlmpSharedMap::new(0, { + shmem_provider + .borrow_mut() + .new_map(LLMP_CFG_INITIAL_MAP_SIZE)? + })], // drop pages to the broker if it already read them keep_pages_forever: false, + shmem_provider: shmem_provider.clone(), }, receiver: LlmpReceiver { id: 0, current_recv_map: initial_broker_map, last_msg_recvd: ptr::null_mut(), + shmem_provider: shmem_provider.clone(), }, + shmem_provider: shmem_provider.clone(), }) } @@ -1917,20 +1887,27 @@ where #[cfg(feature = "std")] /// Creates a new LlmpClient, reading the map id and len from env - pub fn create_using_env(env_var: &str) -> Result { - Self::new(LlmpSharedMap::existing(SH::existing_from_env(env_var)?)) + pub fn create_using_env( + shmem_provider: &Rc>, + env_var: &str, + ) -> Result { + let map = LlmpSharedMap::existing(shmem_provider.borrow_mut().existing_from_env(env_var)?); + Self::new(shmem_provider, map) } #[cfg(feature = "std")] /// Create a LlmpClient, getting the ID from a given port - pub fn create_attach_to_tcp(port: u16) -> Result { + pub fn create_attach_to_tcp( + shmem_provider: &Rc>, + port: u16, + ) -> Result { let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))?; println!("Connected to port {}", port); // First, get the serialized description size by serializing a dummy. let dummy_description = ShMemDescription { size: 0, - str_bytes: Default::default(), + id: ShMemId::default(), }; let mut new_broker_map_str = postcard::to_allocvec(&dummy_description)?; @@ -1938,9 +1915,12 @@ where let broker_map_description: ShMemDescription = postcard::from_bytes(&new_broker_map_str)?; - let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_description( - &broker_map_description, - )?))?; + let map = LlmpSharedMap::existing( + shmem_provider + .borrow_mut() + .from_description(broker_map_description)?, + ); + let ret = Self::new(shmem_provider, map)?; let own_map_description_bytes = postcard::to_allocvec(&ret.sender.out_maps.first().unwrap().shmem.description())?; @@ -1949,80 +1929,11 @@ where } } -/// `n` clients connect to a broker. They share an outgoing map with the broker, -/// and get incoming messages from the shared broker bus -/// If the Shm has a fd, we can attach to it. -#[cfg(all(unix, feature = "std"))] -impl LlmpClient -where - SH: ShMem, -{ - #[cfg(all(unix, feature = "std"))] - /// Create a LlmpClient, getting the ID from a given filename - pub fn create_attach_to_unix(filename: &str) -> Result { - let stream = UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(filename).unwrap())?; - println!("Connected to socket {}", filename); - - let mut buf = [0u8; 5]; - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg( - stream.as_raw_fd(), - &[IoVec::from_mut_slice(&mut buf[..])], - Some(&mut cmsgspace), - MsgFlags::empty(), - ) - .unwrap(); - - for cmsg in msg.cmsgs() { - if let ControlMessageOwned::ScmRights(fds) = cmsg { - for fd in fds { - let mut fdstr = [0u8; 20]; - match write!(&mut fdstr[..], "{}", fd) { - Ok(_) => {} - Err(_) => { - dbg!("error converting fd to string"); - } - } - - let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice( - &fdstr, - LLMP_CFG_INITIAL_MAP_SIZE, - )?))?; - - match sendmsg( - stream.as_raw_fd(), - &[IoVec::from_slice(b"\x00")], - &[ControlMessage::ScmRights(&[ret - .sender - .out_maps - .first() - .unwrap() - .shmem - .shm_str() - .parse() - .unwrap()])], - MsgFlags::empty(), - None, - ) { - Ok(_) => {} - Err(err) => { - dbg!("Error sending fd over stream {}", err); - continue; - } - }; - return Ok(ret); - } - } - } - - panic!("Didn't receive a file descriptor from the broker!"); - } -} - #[cfg(test)] #[cfg(all(unix, feature = "std"))] mod tests { + use alloc::rc::Rc; use std::{thread::sleep, time::Duration}; use super::{ @@ -2032,17 +1943,20 @@ mod tests { Tag, }; - use crate::bolts::shmem::UnixShMem; + use crate::bolts::shmem::{ShMemProvider, StdShMemProvider}; + + use core::cell::RefCell; #[test] pub fn llmp_connection() { - let mut broker = match LlmpConnection::::on_port(1337).unwrap() { + let shmem_provider = Rc::new(RefCell::new(StdShMemProvider::new())); + let mut broker = match LlmpConnection::on_port(&shmem_provider, 1337).unwrap() { IsClient { client: _ } => panic!("Could not bind to port as broker"), IsBroker { broker } => broker, }; // Add the first client (2nd, actually, because of the tcp listener client) - let mut client = match LlmpConnection::::on_port(1337).unwrap() { + let mut client = match LlmpConnection::on_port(&shmem_provider, 1337).unwrap() { IsBroker { broker: _ } => panic!("Second connect should be a client!"), IsClient { client } => client, }; @@ -2066,7 +1980,7 @@ mod tests { } /* recreate the client from env, check if it still works */ - client = LlmpClient::::on_existing_from_env("_ENV_TEST").unwrap(); + client = LlmpClient::on_existing_from_env(&shmem_provider, "_ENV_TEST").unwrap(); client.send_buf(tag, &arr).unwrap(); diff --git a/libafl/src/bolts/os/ashmem_server.rs b/libafl/src/bolts/os/ashmem_server.rs index e5b544c8d4..b5aee980e6 100644 --- a/libafl/src/bolts/os/ashmem_server.rs +++ b/libafl/src/bolts/os/ashmem_server.rs @@ -5,12 +5,18 @@ and forwards them over unix domain sockets. */ use crate::{ - bolts::shmem::{ShMem, ShMemDescription, UnixShMem}, + bolts::shmem::{ + unix_shmem::ashmem::{AshmemShMem, AshmemShMemProvider}, + ShMem, ShMemDescription, ShMemId, ShMemProvider, + }, Error, }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::io::{Read, Write}; +use std::{ + io::{Read, Write}, + sync::{Arc, Condvar, Mutex}, +}; #[cfg(all(feature = "std", unix))] use nix::poll::{poll, PollFd, PollFlags}; @@ -18,8 +24,8 @@ use nix::poll::{poll, PollFd, PollFlags}; #[cfg(all(feature = "std", unix))] use std::{ os::unix::{ + io::{AsRawFd, RawFd}, net::{UnixListener, UnixStream}, - {io::AsRawFd, prelude::RawFd}, }, thread, }; @@ -27,30 +33,43 @@ use std::{ #[cfg(all(unix, feature = "std"))] use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; -#[derive(Debug)] -/// The Sharedmem backed by a `ShmemService`a -pub struct ServedShMem { - stream: UnixStream, - shmem: Option, - slice: Option<[u8; 20]>, - fd: Option, -} const ASHMEM_SERVER_NAME: &str = "@ashmem_server"; -impl ServedShMem { - /// Create a new ServedShMem and connect to the ashmem server. - pub fn connect(name: &str) -> Self { - Self { - stream: UnixStream::connect_to_unix_addr(&UnixSocketAddr::from_abstract(name).unwrap()) - .expect("Failed to connect to the ashmem server"), - shmem: None, - slice: None, - fd: None, - } +#[derive(Debug)] +pub struct ServedShMemProvider { + stream: UnixStream, + inner: AshmemShMemProvider, +} + +#[derive(Clone, Debug)] +pub struct ServedShMem { + inner: AshmemShMem, + server_fd: i32, +} + +impl ShMem for ServedShMem { + fn id(&self) -> ShMemId { + let client_id = self.inner.id(); + ShMemId::from_string(&format!("{}:{}", self.server_fd, client_id.to_string())) } + fn len(&self) -> usize { + self.inner.len() + } + + fn map(&self) -> &[u8] { + self.inner.map() + } + + fn map_mut(&mut self) -> &mut [u8] { + self.inner.map_mut() + } +} + +impl ServedShMemProvider { /// Send a request to the server, and wait for a response - fn send_receive(&mut self, request: AshmemRequest) -> ([u8; 20], RawFd) { + #[allow(clippy::similar_names)] // id and fd + fn send_receive(&mut self, request: AshmemRequest) -> (i32, i32) { let body = postcard::to_allocvec(&request).unwrap(); let header = (body.len() as u32).to_be_bytes(); @@ -66,63 +85,62 @@ impl ServedShMem { self.stream .recv_fds(&mut shm_slice, &mut fd_buf) .expect("Did not receive a response"); - (shm_slice, fd_buf[0]) + + let server_id = ShMemId::from_slice(&shm_slice); + let server_id_str = server_id.to_string(); + let server_fd: i32 = server_id_str.parse().unwrap(); + (server_fd, fd_buf[0]) } } -impl ShMem for ServedShMem { - fn new_map(map_size: usize) -> Result { - let mut res = Self::connect(ASHMEM_SERVER_NAME); - let (shm_slice, fd) = res.send_receive(AshmemRequest::NewMap(map_size)); - if fd == -1 { - Err(Error::IllegalState( - "Could not allocate from the ashmem server".to_string(), - )) - } else { - res.slice = Some(shm_slice); - res.fd = Some(fd); - res.shmem = Some( - UnixShMem::existing_from_shm_slice(&shm_slice, map_size) - .expect("Failed to create the UnixShMem"), - ); - Ok(res) +impl Default for ServedShMemProvider { + fn default() -> Self { + Self::new() + } +} + +impl Clone for ServedShMemProvider { + fn clone(&self) -> Self { + Self::new() + } +} + +impl ShMemProvider for ServedShMemProvider { + type Mem = ServedShMem; + + /// Connect to the server and return a new ServedShMemProvider + fn new() -> Self { + Self { + stream: UnixStream::connect_to_unix_addr( + &UnixSocketAddr::new(ASHMEM_SERVER_NAME).unwrap(), + ) + .expect("Unable to open connection to ashmem service"), + inner: AshmemShMemProvider::new(), } } + fn new_map(&mut self, map_size: usize) -> Result { + let (server_fd, client_fd) = self.send_receive(AshmemRequest::NewMap(map_size)); - fn existing_from_shm_slice( - map_str_bytes: &[u8; 20], - map_size: usize, - ) -> Result { - let mut res = Self::connect(ASHMEM_SERVER_NAME); - let (shm_slice, fd) = res.send_receive(AshmemRequest::ExistingMap(ShMemDescription { - size: map_size, - str_bytes: *map_str_bytes, - })); - if fd == -1 { - Err(Error::IllegalState( - "Could not allocate from the ashmem server".to_string(), - )) - } else { - res.slice = Some(shm_slice); - res.fd = Some(fd); - res.shmem = Some( - UnixShMem::existing_from_shm_slice(&shm_slice, map_size) - .expect("Failed to create the UnixShMem"), - ); - Ok(res) - } + Ok(ServedShMem { + inner: self + .inner + .from_id_and_size(ShMemId::from_string(&format!("{}", client_fd)), map_size)?, + server_fd, + }) } - fn shm_slice(&self) -> &[u8; 20] { - self.slice.as_ref().unwrap() - } - - fn map(&self) -> &[u8] { - self.shmem.as_ref().unwrap().map() - } - - fn map_mut(&mut self) -> &mut [u8] { - self.shmem.as_mut().unwrap().map_mut() + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + let parts = id.to_string().split(':').collect::>(); + let server_id_str = parts.get(0).unwrap(); + let (server_fd, client_fd) = self.send_receive(AshmemRequest::ExistingMap( + ShMemDescription::from_string_and_size(server_id_str, size), + )); + Ok(ServedShMem { + inner: self + .inner + .from_id_and_size(ShMemId::from_string(&format!("{}", client_fd)), size)?, + server_fd, + }) } } @@ -138,13 +156,20 @@ pub enum AshmemRequest { } #[derive(Debug)] -pub struct AshmemClient { - unix_socket_file: String, +struct AshmemClient { + stream: UnixStream, +} + +impl AshmemClient { + fn new(stream: UnixStream) -> Self { + Self { stream } + } } #[derive(Debug)] pub struct AshmemService { - maps: HashMap<[u8; 20], UnixShMem>, + provider: AshmemShMemProvider, + maps: Vec, } impl AshmemService { @@ -152,116 +177,145 @@ impl AshmemService { #[must_use] fn new() -> Self { AshmemService { - maps: HashMap::new(), + provider: AshmemShMemProvider::new(), + maps: Vec::new(), } } /// Read and handle the client request, send the answer over unix fd. - fn handle_client(&mut self, stream: &mut UnixStream) -> Result<(), Error> { + fn handle_client(&mut self, client: &mut AshmemClient) -> Result<(), Error> { // Always receive one be u32 of size, then the command. let mut size_bytes = [0u8; 4]; - stream.read_exact(&mut size_bytes)?; + client.stream.read_exact(&mut size_bytes)?; let size = u32::from_be_bytes(size_bytes); let mut bytes = vec![]; bytes.resize(size as usize, 0u8); - stream + client + .stream .read_exact(&mut bytes) .expect("Failed to read message body"); let request: AshmemRequest = postcard::from_bytes(&bytes)?; // Handle the client request - let (shmem_slice, fd): ([u8; 20], RawFd) = match request { - AshmemRequest::NewMap(map_size) => match UnixShMem::new(map_size) { - Err(e) => { - println!("Error allocating shared map {:?}", e); - ([0; 20], -1) - } - Ok(map) => { - let res = (*map.shm_slice(), map.shm_id); - self.maps.insert(*map.shm_slice(), map); - res - } - }, + let mapping = match request { + AshmemRequest::NewMap(map_size) => self.provider.new_map(map_size)?, AshmemRequest::ExistingMap(description) => { - match self.maps.get(&description.str_bytes) { - None => { - println!("Error finding shared map {:?}", description); - ([0; 20], -1) - } - Some(map) => (*map.shm_slice(), map.shm_id), - } + self.provider.from_description(description)? } AshmemRequest::Deregister(_) => { return Ok(()); } }; - stream.send_fds(&shmem_slice, &[fd])?; + let id = mapping.id(); + let server_fd: i32 = id.to_string().parse().unwrap(); + client + .stream + .send_fds(&id.to_string().as_bytes(), &[server_fd])?; + self.maps.push(mapping); Ok(()) } /// Create a new AshmemService, then listen and service incoming connections in a new thread. - pub fn start() -> Result, Error> { - Ok(thread::spawn(move || { - Self::new().listen(ASHMEM_SERVER_NAME).unwrap() - })) + pub fn start() -> Result>, Error> { + #[allow(clippy::mutex_atomic)] + let syncpair = Arc::new((Mutex::new(false), Condvar::new())); + let childsyncpair = Arc::clone(&syncpair); + let join_handle = + thread::spawn(move || Self::new().listen(ASHMEM_SERVER_NAME, childsyncpair)); + + let (lock, cvar) = &*syncpair; + let mut started = lock.lock().unwrap(); + while !*started { + started = cvar.wait(started).unwrap(); + } + + Ok(join_handle) } /// Listen on a filename (or abstract name) for new connections and serve them. This function /// should not return. - fn listen(&mut self, filename: &str) -> Result<(), Error> { - let listener = UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?)?; - let mut clients: HashMap = HashMap::new(); - let mut poll_fds: HashMap = HashMap::new(); - - poll_fds.insert( + fn listen( + &mut self, + filename: &str, + syncpair: Arc<(Mutex, Condvar)>, + ) -> Result<(), Error> { + let listener = if let Ok(listener) = + UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename)?) + { + listener + } else { + let (lock, cvar) = &*syncpair; + *lock.lock().unwrap() = true; + cvar.notify_one(); + return Err(Error::Unknown( + "The server appears to already be running. We are probably a client".to_string(), + )); + }; + let mut clients: HashMap = HashMap::new(); + let mut poll_fds: Vec = vec![PollFd::new( listener.as_raw_fd(), - PollFd::new(listener.as_raw_fd(), PollFlags::POLLIN), - ); + PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND, + )]; + + let (lock, cvar) = &*syncpair; + *lock.lock().unwrap() = true; + cvar.notify_one(); loop { - let mut fds_to_poll: Vec = poll_fds.values().copied().collect(); - let fd = match poll(&mut fds_to_poll, -1) { - Ok(fd) => fd, + match poll(&mut poll_fds, -1) { + Ok(num_fds) if num_fds > 0 => (), + Ok(_) => continue, Err(e) => { println!("Error polling for activity: {:?}", e); continue; } }; - if fd == listener.as_raw_fd() { - let (stream, addr) = match listener.accept_unix_addr() { - Ok(stream_val) => stream_val, - Err(e) => { - println!("Error accepting client: {:?}", e); - continue; - } - }; + let copied_poll_fds: Vec = poll_fds.iter().copied().collect(); + for poll_fd in copied_poll_fds { + let revents = poll_fd.revents().expect("revents should not be None"); + let raw_polled_fd = + unsafe { *((&poll_fd as *const PollFd) as *const libc::pollfd) }.fd; + if revents.contains(PollFlags::POLLHUP) { + poll_fds.remove(poll_fds.iter().position(|item| *item == poll_fd).unwrap()); + clients.remove(&raw_polled_fd); + } else if revents.contains(PollFlags::POLLIN) { + if clients.contains_key(&raw_polled_fd) { + let mut client = clients.get_mut(&raw_polled_fd).unwrap(); + match self.handle_client(&mut client) { + Ok(()) => (), + Err(e) => { + dbg!("Ignoring failed read from client", e, poll_fd); + continue; + } + }; + } else { + let (stream, addr) = match listener.accept_unix_addr() { + Ok(stream_val) => stream_val, + Err(e) => { + println!("Error accepting client: {:?}", e); + continue; + } + }; - println!("Recieved connection from {:?}", addr); - let pollfd = PollFd::new(stream.as_raw_fd(), PollFlags::POLLIN); - poll_fds.insert(stream.as_raw_fd(), pollfd); - clients - .insert(stream.as_raw_fd(), (stream, addr)) - .as_ref() - .unwrap(); - } else if poll_fds - .get(&fd) - .unwrap() - .revents() - .unwrap() - .contains(PollFlags::POLLHUP) - { - poll_fds.remove(&fd); - clients.remove(&fd); - } else { - let (stream, _addr) = clients.get_mut(&fd).unwrap(); - match self.handle_client(stream) { - Ok(()) => (), - Err(e) => { - dbg!("Ignoring failed read from client", e); - continue; + println!("Recieved connection from {:?}", addr); + let pollfd = PollFd::new( + stream.as_raw_fd(), + PollFlags::POLLIN | PollFlags::POLLRDNORM | PollFlags::POLLRDBAND, + ); + poll_fds.push(pollfd); + let mut client = AshmemClient::new(stream); + match self.handle_client(&mut client) { + Ok(()) => (), + Err(e) => { + dbg!("Ignoring failed read from client", e); + } + }; + clients.insert(client.stream.as_raw_fd(), client); } - }; + } else { + //println!("Unknown revents flags: {:?}", revents); + } } } } diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index b2f43c4344..930dc38c24 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -2,21 +2,38 @@ // too.) #[cfg(all(feature = "std", unix))] -pub use unix_shmem::UnixShMem; +pub use unix_shmem::{UnixShMem, UnixShMemProvider}; #[cfg(all(feature = "std", unix))] -pub type StdShMem = UnixShMem; +pub type OsShMemProvider = UnixShMemProvider; +#[cfg(all(feature = "std", unix))] +pub type OsShMem = UnixShMem; #[cfg(all(windows, feature = "std"))] -pub use win32_shmem::Win32ShMem; +pub use win32_shmem::{Win32ShMem, Win32ShMemProvider}; #[cfg(all(windows, feature = "std"))] -pub type StdShMem = Win32ShMem; +pub type OsShMemProvider = Win32ShMemProvider; +#[cfg(all(windows, feature = "std"))] +pub type OsShMem = Win32ShMem; + +#[cfg(target_os = "android")] +use crate::bolts::os::ashmem_server::{ServedShMem, ServedShMemProvider}; +#[cfg(target_os = "android")] +pub type StdShMemProvider = ServedShMemProvider; +#[cfg(target_os = "android")] +pub type StdShMem = ServedShMem; + +#[cfg(all(feature = "std", not(target_os = "android")))] +pub type StdShMemProvider = OsShMemProvider; +#[cfg(all(feature = "std", not(target_os = "android")))] +pub type StdShMem = OsShMem; -use alloc::string::{String, ToString}; use core::fmt::Debug; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::env; +use alloc::string::ToString; + use crate::Error; /// Description of a shared map. @@ -25,419 +42,526 @@ use crate::Error; pub struct ShMemDescription { /// Size of this map pub size: usize, - /// of name of this map, as fixed 20 bytes c-string - pub str_bytes: [u8; 20], + /// Id of this map + pub id: ShMemId, } -/// A Shared map -pub trait ShMem: Sized + Debug { - /// Creates a new map with the given size - fn new_map(map_size: usize) -> Result; +impl ShMemDescription { + pub fn from_string_and_size(string: &str, size: usize) -> Self { + Self { + size, + id: ShMemId::from_string(string), + } + } +} - /// Creates a new reference to the same map - fn clone_ref(old_ref: &Self) -> Result { - Self::existing_from_shm_slice(old_ref.shm_slice(), old_ref.map().len()) +/// An id associated with a given shared memory mapping (ShMem), which can be used to +/// establish shared-mappings between proccesses. +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +pub struct ShMemId { + id: [u8; 20], +} + +impl ShMemId { + /// Create a new id from a fixed-size string + pub fn from_slice(slice: &[u8; 20]) -> Self { + Self { id: *slice } } - /// Creates a nes variable with the given name, strigified to 20 bytes. - fn existing_from_shm_slice(map_str_bytes: &[u8; 20], map_size: usize) -> Result; + /// Create a new id from an int + pub fn from_int(val: i32) -> Self { + Self::from_string(&val.to_string()) + } - /// Initialize from a shm_str with fixed len of 20 - fn existing_from_shm_str(shm_str: &str, map_size: usize) -> Result { + /// Create a new id from a string + pub fn from_string(val: &str) -> Self { let mut slice: [u8; 20] = [0; 20]; - for (i, val) in shm_str.as_bytes().iter().enumerate() { + for (i, val) in val.as_bytes().iter().enumerate() { slice[i] = *val; } - Self::existing_from_shm_slice(&slice, map_size) + Self { id: slice } } - /// The string to identify this shm - fn shm_str(&self) -> String { - let bytes = self.shm_slice(); - let eof_pos = bytes.iter().position(|&c| c == 0).unwrap(); - alloc::str::from_utf8(&bytes[..eof_pos]) - .unwrap() - .to_string() + /// Get the id as a fixed-length slice + pub fn as_slice(&self) -> &[u8; 20] { + &self.id } - /// Let's just fix this to a large enough buf - fn shm_slice(&self) -> &[u8; 20]; + /// Get a string representation of this id + pub fn to_string(&self) -> &str { + let eof_pos = self.id.iter().position(|&c| c == 0).unwrap(); + alloc::str::from_utf8(&self.id[..eof_pos]).unwrap() + } + + /// Get an integer representation of this id + pub fn to_int(&self) -> i32 { + let id: i32 = self.to_string().parse().unwrap(); + id + } +} + +pub trait ShMem: Sized + Debug + Clone { + /// Get the id of this shared memory mapping + fn id(&self) -> ShMemId; + + /// Get the size of this mapping + fn len(&self) -> usize; + + /// Check if the mapping is empty + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get the description of the shared memory mapping + fn description(&self) -> ShMemDescription { + ShMemDescription { + size: self.len(), + id: self.id(), + } + } /// The actual shared map, in memory fn map(&self) -> &[u8]; /// The actual shared map, mutable fn map_mut(&mut self) -> &mut [u8]; - - /// Describe this shared map in a recreatable fashion - fn description(&self) -> ShMemDescription { - ShMemDescription { - size: self.map().len(), - str_bytes: *self.shm_slice(), - } - } - - /// Create a map from a map description - fn existing_from_description(description: &ShMemDescription) -> Result { - Self::existing_from_shm_slice(&description.str_bytes, description.size) - } - + /// /// Write this map's config to env #[cfg(feature = "std")] fn write_to_env(&self, env_name: &str) -> Result<(), Error> { - let map_size = self.map().len(); + let map_size = self.len(); let map_size_env = format!("{}_SIZE", env_name); - env::set_var(env_name, self.shm_str()); + env::set_var(env_name, self.id().to_string()); env::set_var(map_size_env, format!("{}", map_size)); Ok(()) } +} + +pub trait ShMemProvider: Send + Clone + Default { + type Mem: ShMem; + + /// Create a new instance of the provider + fn new() -> Self; + + /// Create a new shared memory mapping + fn new_map(&mut self, map_size: usize) -> Result; + + /// Get a mapping given its id and size + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result; + + /// Get a mapping given a description + fn from_description(&mut self, description: ShMemDescription) -> Result { + self.from_id_and_size(description.id, description.size) + } + + fn clone_ref(&mut self, mapping: &Self::Mem) -> Result { + self.from_id_and_size(mapping.id(), mapping.len()) + } /// Reads an existing map config from env vars, then maps it #[cfg(feature = "std")] - fn existing_from_env(env_name: &str) -> Result { + fn existing_from_env(&mut self, env_name: &str) -> Result { let map_shm_str = env::var(env_name)?; let map_size = str::parse::(&env::var(format!("{}_SIZE", env_name))?)?; - Self::existing_from_shm_str(&map_shm_str, map_size) + self.from_description(ShMemDescription::from_string_and_size( + &map_shm_str, + map_size, + )) } } #[cfg(all(unix, feature = "std"))] pub mod unix_shmem { - use core::{mem::size_of, ptr, slice}; - use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; #[cfg(target_os = "android")] - use libc::{off_t, size_t, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE}; - use std::ffi::CStr; + pub type UnixShMemProvider = ashmem::AshmemShMemProvider; #[cfg(target_os = "android")] - use std::ffi::CString; + pub type UnixShMem = ashmem::AshmemShMem; + #[cfg(not(target_os = "android"))] + pub type UnixShMemProvider = default::CommonUnixShMemProvider; + #[cfg(not(target_os = "android"))] + pub type UnixShMem = ashmem::AshmemShMem; - use crate::Error; + #[cfg(all(unix, feature = "std", not(target_os = "android")))] + mod default { + use core::{ptr, slice}; + use libc::{c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; - use super::ShMem; + use crate::Error; - #[cfg(unix)] - extern "C" { - #[cfg(feature = "std")] - fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int; - #[cfg(feature = "std")] - fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char; - #[cfg(all(feature = "std", not(target_os = "android")))] - fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int; - #[cfg(all(feature = "std", not(target_os = "android")))] - fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int; - #[cfg(all(feature = "std", not(target_os = "android")))] - fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void; - #[cfg(all(feature = "std", target_os = "android"))] - fn ioctl(fd: c_int, request: c_long, ...) -> c_int; - #[cfg(all(feature = "std", target_os = "android"))] - fn open(path: *const c_char, oflag: c_int, ...) -> c_int; - #[cfg(all(feature = "std", target_os = "android"))] - fn close(fd: c_int) -> c_int; - #[cfg(all(feature = "std", target_os = "android"))] - fn mmap( - addr: *mut c_void, - len: size_t, - prot: c_int, - flags: c_int, - fd: c_int, - offset: off_t, - ) -> *mut c_void; + use super::super::{ShMem, ShMemId, ShMemProvider}; - } - - #[cfg(target_os = "android")] - #[derive(Copy, Clone)] - #[repr(C)] - struct ashmem_pin { - pub offset: c_uint, - pub len: c_uint, - } - - #[cfg(target_os = "android")] - const ASHMEM_GET_SIZE: c_long = 0x00007704; - #[cfg(target_os = "android")] - const ASHMEM_UNPIN: c_long = 0x40087708; - #[cfg(target_os = "android")] - const ASHMEM_SET_NAME: c_long = 0x41007701; - #[cfg(target_os = "android")] - const ASHMEM_SET_SIZE: c_long = 0x40087703; - #[cfg(target_os = "android")] - const ASHMEM_DEVICE: &str = "/dev/ashmem"; - - #[cfg(target_os = "android")] - unsafe fn shmctl(__shmid: c_int, __cmd: c_int, _buf: *mut shmid_ds) -> c_int { - if __cmd == 0 { - let length = ioctl(__shmid, ASHMEM_GET_SIZE); - - let ap = ashmem_pin { - offset: 0, - len: length as u32, - }; - - let ret = ioctl(__shmid, ASHMEM_UNPIN, &ap); - close(__shmid); - ret - } else { - 0 - } - } - - #[cfg(target_os = "android")] - unsafe fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int { - let boot_id = std::fs::read_to_string("/proc/sys/kernel/random/boot_id").unwrap(); - - let path = CString::new(format!("{}{}", ASHMEM_DEVICE, boot_id).trim()) - .expect("CString::new failed!"); - let fd = open(path.as_ptr(), O_RDWR); - - let mut ourkey: [c_char; 20] = [0; 20]; - snprintf( - ourkey.as_mut_ptr() as *mut c_char, - size_of::<[c_char; 20]>() as c_ulong, - b"%d\x00" as *const u8 as *const c_char, - if __key == 0 { fd } else { __key }, - ); - - if ioctl(fd, ASHMEM_SET_NAME, &ourkey) != 0 { - close(fd); - return 0; - }; - - if ioctl(fd, ASHMEM_SET_SIZE, __size) != 0 { - close(fd); - return 0; - }; - - fd - } - - #[cfg(target_os = "android")] - unsafe fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void { - let size = ioctl(__shmid, ASHMEM_GET_SIZE); - if size < 0 { - return 0 as *mut c_void; + #[cfg(unix)] + #[derive(Copy, Clone)] + #[repr(C)] + struct ipc_perm { + pub __key: c_int, + pub uid: c_uint, + pub gid: c_uint, + pub cuid: c_uint, + pub cgid: c_uint, + pub mode: c_ushort, + pub __pad1: c_ushort, + pub __seq: c_ushort, + pub __pad2: c_ushort, + pub __glibc_reserved1: c_ulong, + pub __glibc_reserved2: c_ulong, } - let ptr = mmap( - 0 as *mut c_void, - size as usize, - PROT_READ | PROT_WRITE, - MAP_SHARED, - __shmid, - 0, - ); - if ptr == usize::MAX as *mut c_void { - return 0 as *mut c_void; + #[cfg(unix)] + #[derive(Copy, Clone)] + #[repr(C)] + struct shmid_ds { + pub shm_perm: ipc_perm, + pub shm_segsz: c_ulong, + pub shm_atime: c_long, + pub shm_dtime: c_long, + pub shm_ctime: c_long, + pub shm_cpid: c_int, + pub shm_lpid: c_int, + pub shm_nattch: c_ulong, + pub __glibc_reserved4: c_ulong, + pub __glibc_reserved5: c_ulong, } - ptr - } + extern "C" { + fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int; + fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int; + fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void; + } - #[cfg(unix)] - #[derive(Copy, Clone)] - #[repr(C)] - struct ipc_perm { - pub __key: c_int, - pub uid: c_uint, - pub gid: c_uint, - pub cuid: c_uint, - pub cgid: c_uint, - pub mode: c_ushort, - pub __pad1: c_ushort, - pub __seq: c_ushort, - pub __pad2: c_ushort, - pub __glibc_reserved1: c_ulong, - pub __glibc_reserved2: c_ulong, - } - - #[cfg(unix)] - #[derive(Copy, Clone)] - #[repr(C)] - struct shmid_ds { - pub shm_perm: ipc_perm, - pub shm_segsz: c_ulong, - pub shm_atime: c_long, - pub shm_dtime: c_long, - pub shm_ctime: c_long, - pub shm_cpid: c_int, - pub shm_lpid: c_int, - pub shm_nattch: c_ulong, - pub __glibc_reserved4: c_ulong, - pub __glibc_reserved5: c_ulong, - } - - /// The default Sharedmap impl for unix using shmctl & shmget - #[cfg(unix)] - #[derive(Clone, Debug)] - pub struct UnixShMem { - pub shm_str: [u8; 20], - pub shm_id: c_int, - pub map: *mut u8, - pub map_size: usize, - } - - #[cfg(unix)] - impl ShMem for UnixShMem { - fn existing_from_shm_slice( - map_str_bytes: &[u8; 20], + /// The default sharedmap impl for unix using shmctl & shmget + #[derive(Clone, Debug)] + pub struct CommonUnixShMem { + id: ShMemId, + map: *mut u8, map_size: usize, - ) -> Result { - unsafe { - let str_bytes = map_str_bytes as *const [u8; 20] as *const libc::c_char; - Self::from_str(CStr::from_ptr(str_bytes), map_size) + } + + impl CommonUnixShMem { + /// Create a new shared memory mapping, using shmget/shmat + pub fn new(map_size: usize) -> Result { + unsafe { + let os_id = shmget(0, map_size as c_ulong, 0o1000 | 0o2000 | 0o600); + + if os_id < 0_i32 { + return Err(Error::Unknown(format!("Failed to allocate a shared mapping of size {} - check OS limits (i.e shmall, shmmax)", map_size))); + } + + let map = shmat(os_id, ptr::null(), 0) as *mut c_uchar; + + if map == usize::MAX as c_int as *mut c_void as *mut c_uchar || map.is_null() { + shmctl(os_id, 0, ptr::null_mut()); + return Err(Error::Unknown( + "Failed to map the shared mapping".to_string(), + )); + } + + Ok(Self { + id: ShMemId::from_int(os_id), + map, + map_size, + }) + } + } + + /// Get a UnixShMem of the existing shared memory mapping identified by id + pub fn from_id_and_size(id: ShMemId, map_size: usize) -> Result { + unsafe { + let map = shmat(id.to_int(), ptr::null(), 0) as *mut c_uchar; + + if map == usize::MAX as *mut c_void as *mut c_uchar || map.is_null() { + return Err(Error::Unknown( + "Failed to map the shared mapping".to_string(), + )); + } + + Ok(Self { id, map, map_size }) + } } } - fn new_map(map_size: usize) -> Result { - Self::new(map_size) + #[cfg(unix)] + impl ShMem for CommonUnixShMem { + fn id(&self) -> ShMemId { + self.id + } + + fn len(&self) -> usize { + self.map_size + } + + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } + } } - fn shm_slice(&self) -> &[u8; 20] { - &self.shm_str + /// Drop implementation for UnixShMem, which cleans up the mapping + #[cfg(unix)] + impl Drop for CommonUnixShMem { + fn drop(&mut self) { + unsafe { + shmctl(self.id.to_int(), 0, ptr::null_mut()); + } + } } - fn map(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.map, self.map_size) } + /// A ShMemProvider which uses shmget/shmat/shmctl to provide shared memory mappings. + #[cfg(unix)] + #[derive(Clone, Debug)] + pub struct CommonUnixShMemProvider {} + + unsafe impl Send for CommonUnixShMemProvider {} + + #[cfg(unix)] + impl Default for CommonUnixShMemProvider { + fn default() -> Self { + Self::new() + } } - fn map_mut(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } - } - } + /// Implement ShMemProvider for UnixShMemProvider + #[cfg(unix)] + impl ShMemProvider for CommonUnixShMemProvider { + type Mem = CommonUnixShMem; - /// Deinit sharedmaps on drop - impl Drop for UnixShMem { - fn drop(&mut self) { - unsafe { - unix_shmem_deinit(self); + fn new() -> Self { + Self {} + } + fn new_map(&mut self, map_size: usize) -> Result { + CommonUnixShMem::new(map_size) + } + + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + CommonUnixShMem::from_id_and_size(id, size) } } } - /// Create an uninitialized shmap - #[cfg(unix)] - const fn unix_shmem_unitialized() -> UnixShMem { - UnixShMem { - shm_str: [0; 20], - shm_id: -1, - map: 0 as *mut c_uchar, - map_size: 0, - } - } + #[cfg(all(unix, feature = "std"))] + pub mod ashmem { + use core::slice; + use libc::{ + c_char, c_int, c_long, c_uint, c_void, off_t, size_t, MAP_SHARED, O_RDWR, PROT_READ, + PROT_WRITE, + }; + use std::ffi::CString; - #[cfg(unix)] - impl UnixShMem { - pub fn from_str(shm_str: &CStr, map_size: usize) -> Result { - let mut ret = unix_shmem_unitialized(); - let map = unsafe { unix_shmem_by_str(&mut ret, shm_str, map_size) }; - if !map.is_null() { - Ok(ret) - } else { - Err(Error::Unknown(format!( - "Could not allocate map with id {:?} and size {}", - shm_str, map_size - ))) + use crate::Error; + + use super::super::{ShMem, ShMemId, ShMemProvider}; + + extern "C" { + fn ioctl(fd: c_int, request: c_long, ...) -> c_int; + fn open(path: *const c_char, oflag: c_int, ...) -> c_int; + fn close(fd: c_int) -> c_int; + fn mmap( + addr: *mut c_void, + len: size_t, + prot: c_int, + flags: c_int, + fd: c_int, + offset: off_t, + ) -> *mut c_void; + + } + + /// An ashmem based impl for linux/android + #[cfg(unix)] + #[derive(Clone, Debug)] + pub struct AshmemShMem { + id: ShMemId, + map: *mut u8, + map_size: usize, + } + + #[derive(Copy, Clone)] + #[repr(C)] + struct ashmem_pin { + pub offset: c_uint, + pub len: c_uint, + } + + const ASHMEM_GET_SIZE: c_long = 0x00007704; + const ASHMEM_UNPIN: c_long = 0x40087708; + //const ASHMEM_SET_NAME: c_long = 0x41007701; + const ASHMEM_SET_SIZE: c_long = 0x40087703; + + impl AshmemShMem { + /// Create a new shared memory mapping, using shmget/shmat + pub fn new(map_size: usize) -> Result { + unsafe { + let device_path = CString::new( + if let Ok(boot_id) = + std::fs::read_to_string("/proc/sys/kernel/random/boot_id") + { + format!("{}{}", "/dev/ashmem", boot_id).trim().to_string() + } else { + "/dev/ashmem".to_string() + }, + ) + .unwrap(); + + let fd = open(device_path.as_ptr(), O_RDWR); + if fd == -1 { + return Err(Error::Unknown(format!( + "Failed to open the ashmem device at {:?}", + device_path + ))); + } + + //if ioctl(fd, ASHMEM_SET_NAME, name) != 0 { + //close(fd); + //return Err(Error::Unknown("Failed to set the ashmem mapping's name".to_string())); + //}; + + if ioctl(fd, ASHMEM_SET_SIZE, map_size) != 0 { + close(fd); + return Err(Error::Unknown( + "Failed to set the ashmem mapping's size".to_string(), + )); + }; + + let map = mmap( + std::ptr::null_mut(), + map_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0, + ); + if map == usize::MAX as *mut c_void { + close(fd); + return Err(Error::Unknown( + "Failed to map the ashmem mapping".to_string(), + )); + } + + Ok(Self { + id: ShMemId::from_string(&format!("{}", fd)), + map: map as *mut u8, + map_size, + }) + } + } + + /// Get a UnixShMem of the existing shared memory mapping identified by id + pub fn from_id_and_size(id: ShMemId, map_size: usize) -> Result { + unsafe { + let fd: i32 = id.to_string().parse().unwrap(); + if ioctl(fd, ASHMEM_GET_SIZE) != map_size as i32 { + return Err(Error::Unknown( + "The mapping's size differs from the requested size".to_string(), + )); + }; + + let map = mmap( + std::ptr::null_mut(), + map_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0, + ); + if map == usize::MAX as *mut c_void { + close(fd); + return Err(Error::Unknown( + "Failed to map the ashmem mapping".to_string(), + )); + } + + Ok(Self { + id, + map: map as *mut u8, + map_size, + }) + } } } - pub fn new(map_size: usize) -> Result { - let mut ret = unix_shmem_unitialized(); - let map = unsafe { unix_shmem_init(&mut ret, map_size) }; - if map.is_null() { - Err(Error::Unknown(format!( - "Could not allocate map of size {} - check OS limits, (i.e. shmall, shmmax)!", - map_size - ))) - } else { - Ok(ret) + #[cfg(unix)] + impl ShMem for AshmemShMem { + fn id(&self) -> ShMemId { + self.id + } + + fn len(&self) -> usize { + self.map_size + } + + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } } } - } - /// Deinitialize this shmem instance - #[allow(clippy::clippy::unnecessary_cast)] // for c_ types - unsafe fn unix_shmem_deinit(shm: *mut UnixShMem) { - if shm.is_null() || (*shm).map.is_null() { - /* Serialized map id */ - // Not set or not initialized; - return; - } - (*shm).shm_str[0_usize] = 0u8; - shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut()); - (*shm).map = ptr::null_mut(); - } + /// Drop implementation for AshmemShMem, which cleans up the mapping + #[cfg(unix)] + impl Drop for AshmemShMem { + fn drop(&mut self) { + unsafe { + //let fd = Self::fd_from_id(self.id).unwrap(); + let fd: i32 = self.id.to_string().parse().unwrap(); - /// Functions to create Shared memory region, for observation channels and - /// opening inputs and stuff. - #[allow(clippy::clippy::unnecessary_cast)] // for c_ types - unsafe fn unix_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar { - (*shm).map_size = map_size; - (*shm).map = ptr::null_mut(); - (*shm).shm_id = shmget( - 0 as c_int, - map_size as c_ulong, - 0o1000 as c_int | 0o2000 as c_int | 0o600 as c_int, - ); - if (*shm).shm_id < 0 as c_int { - (*shm).shm_str[0] = 0u8; - return ptr::null_mut(); - } - snprintf( - (*shm).shm_str.as_mut_ptr() as *mut c_char, - size_of::<[c_char; 20]>() as c_ulong, - b"%d\x00" as *const u8 as *const c_char, - (*shm).shm_id, - ); - (*shm).shm_str - [(size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_ulong) as usize] = 0u8; - (*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar; - if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar || (*shm).map.is_null() { - shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut()); - (*shm).shm_id = -(1 as c_int); - (*shm).shm_str[0] = 0u8; - return ptr::null_mut(); - } - (*shm).map - } + let length = ioctl(fd, ASHMEM_GET_SIZE); - /// Uses a shmap id string to open a shared map - #[allow(clippy::unnecessary_cast)] // for c_int and c_long - unsafe fn unix_shmem_by_str( - shm: *mut UnixShMem, - shm_str: &CStr, - map_size: usize, - ) -> *mut c_uchar { - if shm.is_null() || shm_str.to_bytes().is_empty() || map_size == 0 { - return ptr::null_mut(); + let ap = ashmem_pin { + offset: 0, + len: length as u32, + }; + + ioctl(fd, ASHMEM_UNPIN, &ap); + close(fd); + } + } } - (*shm).map = ptr::null_mut(); - (*shm).map_size = map_size; - strncpy( - (*shm).shm_str.as_mut_ptr() as *mut c_char, - shm_str.as_ptr() as *const c_char, - (size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_ulong), - ); - (*shm).shm_id = shm_str - .to_str() - .unwrap_or_else(|_| panic!("illegal shm_str {:?}", shm_str)) - .parse::() - .unwrap(); - (*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar; - if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar { - (*shm).map = ptr::null_mut(); - (*shm).map_size = 0; - (*shm).shm_str[0] = 0u8; - return ptr::null_mut(); + + /// A ShMemProvider which uses ashmem to provide shared memory mappings. + #[cfg(unix)] + #[derive(Clone, Debug)] + pub struct AshmemShMemProvider {} + + unsafe impl Send for AshmemShMemProvider {} + + #[cfg(unix)] + impl Default for AshmemShMemProvider { + fn default() -> Self { + Self::new() + } + } + + /// Implement ShMemProvider for AshmemShMemProvider + #[cfg(unix)] + impl ShMemProvider for AshmemShMemProvider { + type Mem = AshmemShMem; + + fn new() -> Self { + Self {} + } + + fn new_map(&mut self, map_size: usize) -> Result { + let mapping = AshmemShMem::new(map_size)?; + Ok(mapping) + } + + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + AshmemShMem::from_id_and_size(id, size) + } } - (*shm).map } } #[cfg(all(feature = "std", windows))] pub mod win32_shmem { - use super::ShMem; + use super::{ShMem, ShMemId, ShMemProvider}; use crate::{ bolts::bindings::{ windows::win32::system_services::{ @@ -450,6 +574,7 @@ pub mod win32_shmem { }; use core::{ffi::c_void, ptr, slice}; + use std::convert::TryInto; use uuid::Uuid; const INVALID_HANDLE_VALUE: isize = -1; @@ -458,80 +583,14 @@ pub mod win32_shmem { /// The default Sharedmap impl for windows using shmctl & shmget #[derive(Clone, Debug)] pub struct Win32ShMem { - pub shm_str: [u8; 20], - pub handle: HANDLE, - pub map: *mut u8, - pub map_size: usize, - } - - impl ShMem for Win32ShMem { - fn existing_from_shm_slice( - map_str_bytes: &[u8; 20], - map_size: usize, - ) -> Result { - Self::from_str(map_str_bytes, map_size) - } - - fn new_map(map_size: usize) -> Result { - Self::new(map_size) - } - - fn shm_slice(&self) -> &[u8; 20] { - &self.shm_str - } - - fn map(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.map, self.map_size) } - } - - fn map_mut(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } - } - } - - /// Deinit sharedmaps on drop - impl Drop for Win32ShMem { - fn drop(&mut self) { - unsafe { - UnmapViewOfFile(self.map as *mut c_void); - CloseHandle(self.handle); - } - } + id: ShMemId, + handle: HANDLE, + map: *mut u8, + map_size: usize, } impl Win32ShMem { - pub fn from_str(map_str_bytes: &[u8; 20], map_size: usize) -> Result { - unsafe { - let handle = OpenFileMappingA( - FILE_MAP_ALL_ACCESS, - BOOL(0), - PSTR(map_str_bytes as *const u8 as *mut u8), - ); - if handle == HANDLE(0) { - return Err(Error::Unknown(format!( - "Cannot open shared memory {}", - String::from_utf8_lossy(map_str_bytes) - ))); - } - let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; - if map.is_null() { - return Err(Error::Unknown(format!( - "Cannot map shared memory {}", - String::from_utf8_lossy(map_str_bytes) - ))); - } - let mut ret = Self { - shm_str: [0; 20], - handle: handle, - map: map, - map_size: map_size, - }; - ret.shm_str.clone_from_slice(map_str_bytes); - Ok(ret) - } - } - - pub fn new(map_size: usize) -> Result { + fn new_map(map_size: usize) -> Result { unsafe { let uuid = Uuid::new_v4(); let mut map_str = format!("libafl_{}", uuid.to_simple()); @@ -558,39 +617,99 @@ pub mod win32_shmem { String::from_utf8_lossy(map_str_bytes) ))); } - let mut ret = Self { - shm_str: [0; 20], + + Ok(Self { + id: ShMemId::from_slice(&map_str_bytes[0..20].try_into().unwrap()), handle, map, map_size, - }; - ret.shm_str.clone_from_slice(&map_str_bytes[0..20]); - Ok(ret) + }) + } + } + + fn from_id_and_size(id: ShMemId, map_size: usize) -> Result { + unsafe { + let map_str_bytes = id.id; + + let handle = OpenFileMappingA( + FILE_MAP_ALL_ACCESS, + BOOL(0), + PSTR(&map_str_bytes as *const u8 as *mut u8), + ); + if handle == HANDLE(0) { + return Err(Error::Unknown(format!( + "Cannot open shared memory {}", + String::from_utf8_lossy(&map_str_bytes) + ))); + } + let map = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; + if map.is_null() { + return Err(Error::Unknown(format!( + "Cannot map shared memory {}", + String::from_utf8_lossy(&map_str_bytes) + ))); + } + Ok(Self { + id, + handle, + map, + map_size, + }) } } } -} -#[cfg(test)] -mod tests { + impl ShMem for Win32ShMem { + fn id(&self) -> ShMemId { + self.id + } - #[cfg(all(unix, feature = "std"))] - use super::{ShMem, UnixShMem}; + fn len(&self) -> usize { + self.map_size + } - #[cfg(all(unix, feature = "std"))] - #[test] - fn test_str_conversions() { - let mut shm_str: [u8; 20] = [0; 20]; - shm_str[0] = 'A' as u8; - shm_str[1] = 'B' as u8; - shm_str[2] = 'C' as u8; - let faux_shmem = UnixShMem { - shm_id: 0, - shm_str, - map: 0 as *mut u8, - map_size: 20, - }; - let str = faux_shmem.shm_str(); - assert_eq!(str, "ABC"); + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } + } + } + + /// Deinit sharedmaps on drop + impl Drop for Win32ShMem { + fn drop(&mut self) { + unsafe { + UnmapViewOfFile(self.map as *mut c_void); + CloseHandle(self.handle); + } + } + } + + /// A ShMemProvider which uses win32 functions to provide shared memory mappings. + #[derive(Clone, Debug)] + pub struct Win32ShMemProvider {} + + impl Default for Win32ShMemProvider { + fn default() -> Self { + Self::new() + } + } + + /// Implement ShMemProvider for Win32ShMemProvider + impl ShMemProvider for Win32ShMemProvider { + type Mem = Win32ShMem; + + fn new() -> Self { + Self {} + } + fn new_map(&mut self, map_size: usize) -> Result { + Win32ShMem::new_map(map_size) + } + + fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { + Win32ShMem::from_id_and_size(id, size) + } } } diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index ae85aebeb5..e9a8115efe 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -1,7 +1,7 @@ //! LLMP-backed event manager for scalable multi-processed fuzzing -use alloc::{string::ToString, vec::Vec}; -use core::{marker::PhantomData, time::Duration}; +use alloc::{rc::Rc, string::ToString, vec::Vec}; +use core::{cell::RefCell, marker::PhantomData, time::Duration}; use serde::{de::DeserializeOwned, Serialize}; #[cfg(feature = "std")] @@ -17,12 +17,16 @@ use crate::utils::startable_self; use crate::utils::{fork, ForkResult}; #[cfg(all(feature = "std", unix))] -use crate::bolts::shmem::UnixShMem; +use crate::bolts::shmem::UnixShMemProvider; +#[cfg(all(feature = "std", target_os = "android"))] +use crate::bolts::os::ashmem_server::AshmemService; +#[cfg(feature = "std")] +use crate::bolts::shmem::StdShMemProvider; use crate::{ bolts::{ llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag}, - shmem::ShMem, + shmem::ShMemProvider, }, corpus::CorpusScheduler, events::{BrokerEventResult, Event, EventManager}, @@ -46,23 +50,23 @@ const LLMP_TAG_EVENT_TO_BOTH: llmp::Tag = 0x2B0741; const _LLMP_TAG_RESTART: llmp::Tag = 0x8357A87; const _LLMP_TAG_NO_RESTART: llmp::Tag = 0x57A7EE71; -#[derive(Clone, Debug)] -pub struct LlmpEventManager +#[derive(Debug)] +pub struct LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider + 'static, ST: Stats, //CE: CustomEvent, { stats: Option, - llmp: llmp::LlmpConnection, + llmp: llmp::LlmpConnection, phantom: PhantomData<(I, S)>, } #[cfg(feature = "std")] #[cfg(unix)] -impl LlmpEventManager +impl LlmpEventManager where I: Input, S: IfInteresting, @@ -72,10 +76,14 @@ where /// If the port is not yet bound, it will act as broker /// Else, it will act as client. #[cfg(feature = "std")] - pub fn new_on_port_std(stats: ST, port: u16) -> Result { + pub fn new_on_port_std( + shmem_provider: &Rc>, + stats: ST, + port: u16, + ) -> Result { Ok(Self { stats: Some(stats), - llmp: llmp::LlmpConnection::on_port(port)?, + llmp: llmp::LlmpConnection::on_port(shmem_provider, port)?, phantom: PhantomData, }) } @@ -83,16 +91,19 @@ where /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env /// Std uses UnixShMem. #[cfg(feature = "std")] - pub fn existing_client_from_env_std(env_name: &str) -> Result { - Self::existing_client_from_env(env_name) + pub fn existing_client_from_env_std( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { + Self::existing_client_from_env(shmem_provider, env_name) } } -impl Drop for LlmpEventManager +impl Drop for LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { /// LLMP clients will have to wait until their pages are mapped by somebody. @@ -101,32 +112,39 @@ where } } -impl LlmpEventManager +impl LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { /// Create llmp on a port /// If the port is not yet bound, it will act as broker /// Else, it will act as client. #[cfg(feature = "std")] - pub fn new_on_port(stats: ST, port: u16) -> Result { + pub fn new_on_port( + shmem_provider: &Rc>, + stats: ST, + port: u16, + ) -> Result { Ok(Self { stats: Some(stats), - llmp: llmp::LlmpConnection::on_port(port)?, + llmp: llmp::LlmpConnection::on_port(shmem_provider, port)?, phantom: PhantomData, }) } /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env #[cfg(feature = "std")] - pub fn existing_client_from_env(env_name: &str) -> Result { + pub fn existing_client_from_env( + shmem_provider: &Rc>, + env_name: &str, + ) -> Result { Ok(Self { stats: None, llmp: llmp::LlmpConnection::IsClient { - client: LlmpClient::on_existing_from_env(env_name)?, + client: LlmpClient::on_existing_from_env(shmem_provider, env_name)?, }, // Inserting a nop-stats element here so rust won't complain. // In any case, the client won't currently use it. @@ -141,26 +159,21 @@ where /// Create an existing client from description pub fn existing_client_from_description( + shmem_provider: &Rc>, description: &LlmpClientDescription, ) -> Result { Ok(Self { stats: None, - llmp: llmp::LlmpConnection::existing_client_from_description(description)?, + llmp: llmp::LlmpConnection::existing_client_from_description( + shmem_provider, + description, + )?, // Inserting a nop-stats element here so rust won't complain. // In any case, the client won't currently use it. phantom: PhantomData, }) } - /// A client on an existing map - pub fn for_client(client: LlmpClient) -> Self { - Self { - stats: None, - llmp: llmp::LlmpConnection::IsClient { client }, - phantom: PhantomData, - } - } - /// Write the config for a client eventmgr to env vars, a new client can reattach using existing_client_from_env #[cfg(feature = "std")] pub fn to_env(&self, env_name: &str) { @@ -309,29 +322,11 @@ where } } -#[cfg(all(feature = "std", unix))] -impl LlmpEventManager +impl EventManager for LlmpEventManager where I: Input, S: IfInteresting, - SH: ShMem, - ST: Stats, -{ - #[cfg(all(feature = "std", unix))] - pub fn new_on_domain_socket(stats: ST, filename: &str) -> Result { - Ok(Self { - stats: Some(stats), - llmp: llmp::LlmpConnection::on_domain_socket(filename)?, - phantom: PhantomData, - }) - } -} - -impl EventManager for LlmpEventManager -where - I: Input, - S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, //CE: CustomEvent, { /// The llmp client needs to wait until a broker mapped all pages, before shutting down. @@ -388,14 +383,14 @@ where /// Serialize the current state and corpus during an executiont to bytes. /// On top, add the current llmp event manager instance to be restored /// This method is needed when the fuzzer run crashes and has to restart. -pub fn serialize_state_mgr( +pub fn serialize_state_mgr( state: &S, - mgr: &LlmpEventManager, + mgr: &LlmpEventManager, ) -> Result, Error> where I: Input, S: Serialize + IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { Ok(postcard::to_allocvec(&(&state, &mgr.describe()?))?) @@ -403,43 +398,44 @@ where /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` #[allow(clippy::type_complexity)] -pub fn deserialize_state_mgr( +pub fn deserialize_state_mgr( + shmem_provider: &Rc>, state_corpus_serialized: &[u8], -) -> Result<(S, LlmpEventManager), Error> +) -> Result<(S, LlmpEventManager), Error> where I: Input, S: DeserializeOwned + IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { let tuple: (S, _) = postcard::from_bytes(&state_corpus_serialized)?; Ok(( tuple.0, - LlmpEventManager::existing_client_from_description(&tuple.1)?, + LlmpEventManager::existing_client_from_description(shmem_provider, &tuple.1)?, )) } /// A manager that can restart on the fly, storing states in-between (in `on_resatrt`) -#[derive(Clone, Debug)] -pub struct LlmpRestartingEventManager +#[derive(Debug)] +pub struct LlmpRestartingEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider + 'static, ST: Stats, //CE: CustomEvent, { /// The embedded llmp event manager - llmp_mgr: LlmpEventManager, + llmp_mgr: LlmpEventManager, /// The sender to serialize the state for the next runner - sender: LlmpSender, + sender: LlmpSender, } -impl EventManager for LlmpRestartingEventManager +impl EventManager for LlmpRestartingEventManager where I: Input, S: IfInteresting + Serialize, - SH: ShMem, + SP: ShMemProvider, ST: Stats, //CE: CustomEvent, { /// The llmp client needs to wait until a broker mapped all pages, before shutting down. @@ -484,29 +480,53 @@ const _ENV_FUZZER_RECEIVER: &str = &"_AFL_ENV_FUZZER_RECEIVER"; /// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT"; -impl LlmpRestartingEventManager +impl LlmpRestartingEventManager where I: Input, S: IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, //CE: CustomEvent, { /// Create a new runner, the executed child doing the actual fuzzing. - pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> Self { + pub fn new(llmp_mgr: LlmpEventManager, sender: LlmpSender) -> Self { Self { llmp_mgr, sender } } /// Get the sender - pub fn sender(&self) -> &LlmpSender { + pub fn sender(&self) -> &LlmpSender { &self.sender } /// Get the sender (mut) - pub fn sender_mut(&mut self) -> &mut LlmpSender { + pub fn sender_mut(&mut self) -> &mut LlmpSender { &mut self.sender } } +#[cfg(feature = "std")] +#[allow(clippy::type_complexity)] +pub fn setup_restarting_mgr_std( + //mgr: &mut LlmpEventManager, + stats: ST, + broker_port: u16, +) -> Result< + ( + Option, + LlmpRestartingEventManager, + ), + Error, +> +where + I: Input, + S: DeserializeOwned + IfInteresting, + ST: Stats, +{ + #[cfg(target_os = "android")] + AshmemService::start().expect("Error starting Ashmem Service"); + + setup_restarting_mgr(StdShMemProvider::new(), stats, broker_port) +} + /// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. /// The restarter will start a new process each time the child crashes or timeouts. #[cfg(feature = "std")] @@ -515,30 +535,25 @@ where clippy::type_complexity, clippy::similar_names )] // for { mgr = LlmpEventManager... } -pub fn setup_restarting_mgr( +pub fn setup_restarting_mgr( + shmem_provider: SP, //mgr: &mut LlmpEventManager, stats: ST, broker_port: u16, -) -> Result<(Option, LlmpRestartingEventManager), Error> +) -> Result<(Option, LlmpRestartingEventManager), Error> where I: Input, S: DeserializeOwned + IfInteresting, - SH: ShMem, + SP: ShMemProvider, ST: Stats, { - let mut mgr; + let shmem_provider = Rc::new(RefCell::new(shmem_provider)); + + let mut mgr = + LlmpEventManager::::new_on_port(&shmem_provider, stats, broker_port)?; // We start ourself as child process to actually fuzz - let (sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() { - #[cfg(target_os = "android")] - { - mgr = LlmpEventManager::::new_on_domain_socket(stats, "\x00llmp_socket")?; - }; - #[cfg(not(target_os = "android"))] - { - mgr = LlmpEventManager::::new_on_port(stats, broker_port)? - }; - + let (sender, mut receiver, shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER).is_err() { if mgr.is_broker() { // Yep, broker. Just loop here. println!("Doing broker things. Run this tool again to start fuzzing in a client."); @@ -550,11 +565,14 @@ where mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL); // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. - let sender = LlmpSender::new(0, false)?; - let receiver = LlmpReceiver::on_existing_map( - SH::clone_ref(&sender.out_maps.last().unwrap().shmem)?, - None, - )?; + let sender = { LlmpSender::new(&shmem_provider, 0, false)? }; + + let map = { + shmem_provider + .borrow_mut() + .clone_ref(&sender.out_maps.last().unwrap().shmem)? + }; + let receiver = LlmpReceiver::on_existing_map(shmem_provider.clone(), map, None)?; // Store the information to a map. sender.to_env(_ENV_FUZZER_SENDER)?; receiver.to_env(_ENV_FUZZER_RECEIVER)?; @@ -568,7 +586,7 @@ where #[cfg(unix)] let _ = match unsafe { fork() }? { ForkResult::Parent(handle) => handle.status(), - ForkResult::Child => break (sender, receiver), + ForkResult::Child => break (sender, receiver, shmem_provider), }; // On windows, we spawn ourself again @@ -585,20 +603,29 @@ where } else { // We are the newly started fuzzing instance, first, connect to our own restore map. // A sender and a receiver for single communication + // Clone so we get a new connection to the AshmemServer if we are using + // ServedShMemProvider + let shmem_provider = Rc::new(RefCell::new(shmem_provider.borrow_mut().clone())); ( - LlmpSender::::on_existing_from_env(_ENV_FUZZER_SENDER)?, - LlmpReceiver::::on_existing_from_env(_ENV_FUZZER_RECEIVER)?, + LlmpSender::on_existing_from_env(&shmem_provider, _ENV_FUZZER_SENDER)?, + LlmpReceiver::on_existing_from_env(&shmem_provider, _ENV_FUZZER_RECEIVER)?, + shmem_provider, ) }; println!("We're a client, let's fuzz :)"); + for (var, val) in std::env::vars() { + println!("ENV VARS: {:?}: {:?}", var, val); + } + // If we're restarting, deserialize the old state. let (state, mut mgr) = match receiver.recv_buf()? { None => { println!("First run. Let's set it all up"); // Mgr to send and receive msgs from/to all other fuzzer instances - let client_mgr = LlmpEventManager::::existing_client_from_env( + let client_mgr = LlmpEventManager::::existing_client_from_env( + &shmem_provider, _ENV_FUZZER_BROKER_CLIENT_INITIAL, )?; @@ -607,7 +634,8 @@ where // Restoring from a previous run, deserialize state and corpus. Some((_sender, _tag, msg)) => { println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); - let (state, mgr): (S, LlmpEventManager) = deserialize_state_mgr(&msg)?; + let (state, mgr): (S, LlmpEventManager) = + deserialize_state_mgr(&shmem_provider, &msg)?; (Some(state), LlmpRestartingEventManager::new(mgr, sender)) } From 0732028190d9cecf3cd0626c4c17fa5213798925 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 21 Apr 2021 09:58:11 +0200 Subject: [PATCH 065/104] remove qemufuzzer --- .gitmodules | 3 - fuzzers/qemufuzzer/Cargo.toml | 23 ------- fuzzers/qemufuzzer/build.sh | 12 ---- fuzzers/qemufuzzer/qemu-fuzz | 1 - fuzzers/qemufuzzer/qemu_fuzz | Bin 19651504 -> 0 bytes fuzzers/qemufuzzer/src/lib.rs | 102 ------------------------------- fuzzers/qemufuzzer/src/regs.rs | 107 --------------------------------- fuzzers/qemufuzzer/test/test.c | 43 ------------- 8 files changed, 291 deletions(-) delete mode 100644 fuzzers/qemufuzzer/Cargo.toml delete mode 100755 fuzzers/qemufuzzer/build.sh delete mode 160000 fuzzers/qemufuzzer/qemu-fuzz delete mode 100644 fuzzers/qemufuzzer/qemu_fuzz delete mode 100644 fuzzers/qemufuzzer/src/lib.rs delete mode 100644 fuzzers/qemufuzzer/src/regs.rs delete mode 100755 fuzzers/qemufuzzer/test/test.c diff --git a/.gitmodules b/.gitmodules index a0c3ef7ad6..e69de29bb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "fuzzers/qemufuzzer/qemu-fuzz"] - path = fuzzers/qemufuzzer/qemu-fuzz - url = git@github.com:AFLplusplus/qemu-fuzz.git diff --git a/fuzzers/qemufuzzer/Cargo.toml b/fuzzers/qemufuzzer/Cargo.toml deleted file mode 100644 index b9f8e8c9b9..0000000000 --- a/fuzzers/qemufuzzer/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "qemufuzzer" -version = "0.1.0" -authors = ["Andrea Fioraldi "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -default = ["std"] -std = [] - -[profile.release] -lto = true -codegen-units = 1 -opt-level = 3 -debug = true - -[dependencies] -afl = { path = "../../afl/" } - -[lib] -crate-type = ["staticlib", "cdylib"] diff --git a/fuzzers/qemufuzzer/build.sh b/fuzzers/qemufuzzer/build.sh deleted file mode 100755 index bb656dfac9..0000000000 --- a/fuzzers/qemufuzzer/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -cargo build --release - -git submodule init -git submodule update qemu_fuzz - -cd qemu-fuzz - -./build_qemu_fuzz.sh ../target/release/libqemufuzzer.a - -cp build/qemu-x86_64 ../qemu_fuzz diff --git a/fuzzers/qemufuzzer/qemu-fuzz b/fuzzers/qemufuzzer/qemu-fuzz deleted file mode 160000 index 6f719f6aed..0000000000 --- a/fuzzers/qemufuzzer/qemu-fuzz +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6f719f6aedc9c199d7b99ef42c532a9a20605ff3 diff --git a/fuzzers/qemufuzzer/qemu_fuzz b/fuzzers/qemufuzzer/qemu_fuzz deleted file mode 100644 index 5fab60227343f50c87fbd70ea1715422303c1661..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19651504 zcmagn34CK!`9AQ=uozZ@Y+(_C3PunN142-gC%g40e}x6L*l-nR9g>jj^6w%~^`>D6Bc^}+wAh+}T1Edb`aq!@Ma6j$M|CYTK@4dLK4DJ_>s3-TP_m@5K z31K1LZ+*Y;9;3Z&5WVle_i3Ty{nqyj=El1;8{Y0+>Ar7lW9PRqzmL6L`V6KcxL+Vw+%KFL$@~AklWFnJl;dgh>0K49DDx)ov0k1rH@o53W1ca$ z@rb$Eh1K#A<;<~19DB@>%Zo>@568Wyxl3@*Q%`?|_k?}!y&|*EFW>z4^&2;O|NP;b z@4oDDy9a#-Jp_3{P4E`f%QS}{-)(LTZV$%w7Vk|j@`{PW0=vij>6<^NJa_cuyQc4$ zpF3-h|DN#K<9EGubj9n>zT)x&{<`;X{{Gc>e(xG{H}$P#Cfu$}x8(i1M*DHQMEiE8 zkEooLJ)->^W@4i1FWxQMQ;(1KOU>j(<*eH)+LOw-K43zkaxPJh``ewO>&s@rN9BA@ z$^Vfu|NHJ9owJM5&*PN(N0fe=O3oXU^KzS#UsrM-RMyFzW^qT&&p(y({%0lUwaR>6 zVYa=f{IZfWtE`_nWj$Z28Gij_x8@w<9e^TPM`EYXT|eU#*z{KEM-1lXEy4n zdAn1&UY@V)50{vYJu2tz%6fZLIbZ9Q_48F_ecr0f+dIr0MD>$Z#`UOjUd~s>n^(rQ zyHbCiGXIAu`@?gT@!qcVIa2nqA1U*5r7}OO%KQwJ<6fs+FF#i5f31x7MrA%5%6d3M zS+_4$=53v_KEJVJ^g6jsIghVW@}Hy3!(&R$uF8IXfie$&ROW3)SywwMIsZ|{d!mwa zkm8?F*3XlaeeVlO{SL}`e1Wph7nOO~tjzN+%KBfg?Ar$@>-P0Z{|_qr*p+rA{}tuBcv#7qRpxC@nTMF- z$1CTnqtw@wern3N{-az+2Pym8*^0kPxvpGUfbUYsMS({Lm~b zFKPbp+s?}MvYm2W{6iU6@H#%~@%=odes5)+XOx_OEB)-P_)f~cI;ZS!X=Pv9TUl41 zQtF##f}`f)LS>%g%KH3*vd{cNSr5mV<0kp!n$KUUtn<5+b^B7KpFb<}d7-in->&q3 zyYjg3Ds5cK9rCocopWzDgPIHp;$zjxrA)RQ7={E9>EI<@`QTIgeAy`gyamey&&ge3LSt zEu~Li8P^6S|3k_=Kd9`7dnh@dR_gat&eva*oPsi+A5g~iRb_t8SN4swl=C>N><_yr z=j)S--=mD{I%PfFp!B)D^0@z8WxSJ0&fk>ta-6a+JziN?$0+mmD`mVdQTiV$$K6BO z&tI&p&u1vt`)P_ltmG7x`MF=&ub!*)xu~q4A1mwR24#LuQ}VY}`hU4HpJyuP@mI=q z6ny)}{QrO6KA`k}t@1eXVI}8U<@wK(mHlB#ng3&z{wI~|;xwh7hm~=?MOn`&<+!&h zP7(%6{@HQRe3wWqziW^|`9d!-dLt&r$qE%6b2svd_FyIbWYt>W@|) zFCI|#rMr~#_)6uv`-gJA{;I5>o0ajNsmya-8Sk@|^LUqX-an+&zd>0)e^-wCZDoEg zQP$^f$~t*OIc`r`CnqcC@pH;?zpc#wXO(q+p_20)X2GPgb7CmX!KWDErTi$~+&cTrUqQ`(av{ z&q?Kcy+K)5_bdBPMpV!A?X61vY0CUxr_95CO8*xr>wHehf16VOurl73vfh5DoX1BMze%}{ zJY`%*DErtoO8p;{$JsNK>*!Wxo!_clzn@a}nU5*!a2KVYos{eSqh@{%{-4$3c@HS_ z`4c7QYGr?WNLh#PQO^5%WuAYate>`WzJ8|kbC$CI7nSw)L1jKCmHh80`^N8->-{b> z_vX<5^LD+G-&E#*M#H?mD=VTfMy={ruGRBb4j$4(YS;pj*GIGG5R8ed(o@ z;O|EtFJ5N#l>@1ee>D;^!&!~gXNW_iRtrJC+7^Bk7pM)O-%Zx z?Tx`*OqV8bZenF|Vs?2kb8Px3qov8^(!@$IM-vOv=i9rjWi8l`%s8eOHV3!OnVFrK zF~<(_mM)kub2B%$I5n~0CfUX&rpxAj8>fSVpSL=_YKE{pF+K0ETwuT^Et-I-lKIp$ z8^neOSy?iNo$yWfD>L?{U>ws+lgq(jO*0cyt4m8}CMGt{26M4>6TX>%$)%;q3nrEq zmsZS;;c>RiK`?E0obzGl>9BR%o%JmVljg70OTg^H^0Zkv8>VMW#|FdcUszb&I31iG zJ1tAo=3Iv@%FH{*!JTYUaHp+L&&2A&>{Mwo7|m8EWNCWdte>?5_)F7rD(wMfgl3wK zJtn-{meduq=B9#W%g5nam2)Z0tlb*UnqA9g2FLV0SuGPYt8=m>gHyYC?KpPg{psm*%^IBDYSnHT>(b=H#>IKF`d22* zhns@KZoLG;tH`YK@SLwMEKLVXCRmzse$8h=LU7V2&Yv|ySe~BVXv@t_8!lLxmiw-) z5oe7D*W^73SO;K}7+8O)lEM9eI4-(HS5pb3?6udVIyf?i(T;4dvsnJ81S)v!y` zlff#(YFJ*L-9#4?2DN2j+J&{%LRs4gwhTNx1K~2YQ?ga{tl2`s-NYQ1dCQVpS(+-X zZrakCoRNvCwf<%8Y!wk)9rk+OYGTb+H?cf>VQ}KkpIlm-y0w^KUo)SqF3(L*2b@4O*>|uH%`w?uFkEPn1zjV z;RA(CUT^|1Y(rD?e(+GU)zR!`H@Q4DI~&a7%A&nuw=B9KJ~-p1r-k4gtnCM86P6RT zRh-!vSC$roS=tJD@7uBuYiSkTnIk!<`*~4c+0a3 z!Jnzc<>0B$rm3lkWqI~9;V=4Ai>nJO8)w&>=Q~q#OUoOA4^5fBQZnsqw9ham%tPtY z!lGF-<`Kf|WuDp2H<~+`x~a8CRCipm5 z#opx9)U>}s#h433n9TXf<#X4n&RI116BD>O*yrpsw~47q^Mq$^t!(Guof644`=6*PC zcoPP+G(EL>tK;$Pnd@}z{LGn0y>m^D8Q;m++8 zhc`QS*fQ7VUMm;)({8`PL-fY*^aV@IuJmbtac&OJt(T`tW^)Q2)y)%wMKfgcU}~1e zRB35(am8CU(>lFkpEu8hW#MD~R4}-S$&KfPpUuq7nSH=BXFAxM%`lf&m%}GZ%cXe} z?9DDLuFTF{5I*c3<;_f$%*7PUUHBw(Vybj*u!POAR+eN5n2#3BF1dDCS#gHVG`RZInTbl z2yR@dJ%2TLz?ojbln0X*JVOaD(X}(;PcBW) z2RE6^+$3MUyFmI)>Nt}kI zd2>CR4cKf>=B1jcJvVH#G{14ho7@mQb2lxom?h$c*R^@YVjt1W+LZO+PnvNB9hHOi z7!Jhra<1vp3lAAgjCoBFE{^35OTmR{pS{^0&ElDy3vQeUXJ^H%hVY2z%7MMPsnX*4 zW^W9a<^{9Ub76rw^$V+W-tzgA{^Cq<%I3po$GARlR_lVOXgXSIkn5?wZU|-j%I_NP0QgvHDh|3 z-4wo}3eJd`@bJW#HEd?Y{Fz$z7rdqUU}{&U%uV4;Z7`csP#P@m`QRGz%7tKa)iZ?ASuKLSsSMrvtetvnAsg!HTG-;mEuMhrnw3%YNEQ7f(t*&egC(vxo z%iiYY1$oYIYPP!Qf|u0hN%+=h*X&RmHwNRF2`9r$p^1~ryc{%ET3ily`=Ep12xk46 z%YAyqTzzKH8y8p2UCrV)vuvNt+R0zL+Ggz477W(RxOum<8a}(9nAo^gV|Fo^#SzSu zxpHRAc4J=K$q5gyGILAtJS@D<=FOF}GHo`HiHWtLhBt&cX2r_!<3)Vx@om_UJfh7fGk6B~jj0znv-){6NOiW&FvreIewQ)@O8bI5Renpu`f zHxn>9H@P(L?5k^gr75@9ojK;*?EGx7OPL8UdslF>WF^>n3$FWMp$Fr&n~7b;;jOlF zq)nQQ*KF8fSK(E;v=lyOm}lA3OXlku!5C!Gnva4oY{B6Nli?G)d9x-=8>S%KkR^N3 zT#)9us!XhT?3oH*=9?>KwHzeNi(2!FeLk4M`E$)Hf7A2A#^A}18QetpHHh#Tak%RS zuU!^{ytTEu8cawq{RYj1nw&ZFkzJab=Yy9VQ)Y7rAMS(u1=Y4GyBB!!cxlea{N%>X zvt}vIpKEr``QXvO)K3S)HSf(Sc2yLcq+Q&z&U;FTA^UE|)a@3ZJW7jaV_R&%1 zU$lu#{c&prM{Q|n%ST%(kJ_s8s4aKea?~xiA0K`NeO>rlJA(h(|7|PpgRhLQ zGrt|iYVa}s7yM3-b>Z(wS?8H=GMox7#z|PUZmZu@vxB#r z`HKbdu);G*!ToLhI^)4-yO=-t{!DO;U8O2GDvtMrurjDN-~3tAbtX6Xd^?l4hm6AB z1KkCm?GfHLIP%WsIOdx{;oI)!^SJpu$PC`>`{487x1Ox?%r`AU3+^9$wDq4@*zb0s z+OgQ2;C}AgUPg8`e}dch4s+vS2f@+SdBN|$vB`<>b2~plr@{FNJ`3K0|JwTCqhPjz zGZIt zlY;-^{y}BTBm}eQncAJrXSHTA?dh@NXwfdEPXHESc-hZTiIsE+b-cIuCo`U0lTk@0UeyjTh_1k-Uuid_# z_hci%@$=@-``&v+_?^2ancD-idAo*r!RPk=vGD%E=S$)7w)2iy%iF`d-gF$?7QB5R z%sbFKUh?)1$A5r#vV5KhKi}UwT|Tdc_4|1R`Mer_zOOeWpT9NyJmD?K=a+_`@8ew{ zpT9r+d@t`U^7)=&{_fs;*ez+!p|S)-6Wsy6Xx&WeOo@?H~f5C?+*Fg&ZpT|%;(|TmD%u}y)rzn zv4i=Bsd+kJWpN` zFOWCHi{u^g68S*fC-=6s{g=s;;uZ3&c$K^;UL&uF*U1~=4f2k7lYAuJBKNkl{kO?S z;vMp&{2sh6c~-nfUKH=M%Q_s8Pss0+8v`JD&Y-@}$9 zZ#~hDH%(p?&yhFwwe=P9SO3#qU{&(7#cSjn#p~qfh&Ra36K|5AFWw@*SiDXCR`Cw` zW#V1(%f)--9~AGCUn4#s|CIQUyd^#&Z;Ow~JL29|?s?B%FXx^72~V^hCx62&))VC4 zmHZ_6o#H9-yT#MwKNioB?^ie^T&WH6pGkd_{C@G|2i@`hRy;-iNAcQ6-1>*bTjY<3 zcgWX`?Rp-N?<77VkBg7V_ZE*|?e=q^c$)kv;#u;;#EazX#eMROc$xfp;x+OYiMPnl z5buz`QhY!@AwD9X79W!@h$sHfouAF(8S*!XXRmSV-zuIXf5AWOdCV(bP`s#kNpWBC zvf>r;cS!$L^7n|>$T6lZ&@e%oV#K+_qmv^nZe!eI5G4h^xocvz#1o?g9N%CKbr^tUVo+kgRc!vBD@hthe zf7x}OBaeya$>ZV$^1a22K*D&A7O zt$2t0MCr3je!6&%{A}?)`Ly_ed`^5wj(HdDxmct-Io`CFyW969>TD_&5%sCY?nU-7cy74k#mx~`Hpj@dpuPj&LVc$d5=IX!aZ z49Jla|5!FW4%WlFWIhw*s4tMeSL%!8sBe%TBz-o?(Px_+IYaUnOU{TKIo`+JaUmy7 zex>xEAxBP*964q34@*vk962>|SLd9$Mp%RkCUUmPLAusYq~j~k(?Mg z`cIG}KS%xr$;p!=r$~;R8u?9pH@^47afE+m^a^xgG<<1Z0t)=)_@$9GF zoUZhfBS$|4a`aOr?@3OL961egx?ggVoKd5h#pD4#|k)zKJIr*Pz~4e~dNH_6{A-XgDyx5+;y-XZ_I zc$XaG?U8>?>iguki4Vx{79Wz|Cq5#7KzvO82XXJq?)iFHJVx$4y0;g+#mRRSPmu2? zo+M9+r^ufvo+dv@JVTxr&yt@lo+Cd;JWpO0FOa`Qyhx7oSR#L~)cfS9FOz>*>MP_; z@hbTZ;x+P)c%A%q@do)%#GB;57H^UNRlH69AMpig*EhTy-Sz)csgIGrLOf3X8u0}Aq#q zSv*I6sd%3J-QorE%f*Z2SBaO%uNL>ouM;nme?h!L{)~a^2j7aWPklSueZ|{~w{DHjiGMHJ3yL=sA1a>yesq3W@s8rL+oN;x ziq{n%D4zO3biS{6TXFA>=$xG5HO2diC-02TFDc$qe5`o37oA^Kyr+2LuIQYi;!VXz zif4WponKMBt9bnG=$wM$4aJ9wr+*ZkUsk-Mclr<{Wv-& zr+7{AzT(M#bbd+kmf~Z@vpuPWYCJn_@$oTB1Q#Yc)~eiofyQM{{o{J!X%g5nLu zhl;0v9-UuSyrX#R7tuL+#p{X>6i@v!I^S2kt++Rc&dDiWQ@pQu^8V=jlHx7J$BJhk zh|aGn-cvmBtLU7f;!VXzif0~-&aWumRXqOd=$wM$4aJ9wr+*WjUsk-Mcyb98=L@s8rLQFKmT@w(yz#Z!NY&i55>EAIU@Iwz-i zP4T|s$%mu!ONzG?A1j{yTXcR^@t)$9e?;eW6_5Wjy1t-zL-C>F>2Y*^S@DkIv42J9 zDv6dx*{-X=P~tay7{w|*pFr|OX7>r_2*e4T1Qo_^`}c7)rx z`513b@tWd&#j`s^=T{Z)DV}&-bWTz6rs5;TGqLFW!j942P<*I(dZ*}|vf>@ZV>?IZ zlrcHrHU-7o$-a*khImK&=_Z3ejqw`COw-g^Mo;^4^zp8jo@x+s&bBc;L6(1>{ zd2)1qMe(lU@ux)R6cleLK2$t?NOXQ#@s8rLL!)!@iq{n%D4u$1biS{6TX8QHos(0% zrg&fRigy){9~+%hP`sh|Q1SF}(fMV?JBr6L(K&g=>xvH) zPaPkf?==`eUJ;f8xiOwl1-c)>~c;&g# z`CY~1Cq~y76mKX#R6L!H&Mzz8Q9Sm%=$yRbb;SpYr=B03?nYv zb4rT06dx;|eL-}7Rq>wUiBqC;ii$TCA1R(WH9EgS{tfwi(Oq(U{WG3(bG|S21#;9k z$bTgDP4eVn_Uo=~^7MrDAvyBXFLe9)h2&?*y_eaX9663#CjXV>RLD_ZBS(IR{L32- z3BR7)C9kDzKYj96$$IQX?zpabjm?RZBPU6YoILrbrJn*ha!TaLsgoZ+Y5QrABd0}< zoB{cZUT|2&h)Ir0g332 zlB1tK`L2>PAVnFGOOAKCJ6`N>adPBj$)6)RIdb${8Y)Qk)xjmIdXdBXG%_=9Q_Q*k&}3- zI}fjwoFqB=Ns}X|NdC?VyHA$LYqEaIxa0k-f&5hY zIjJH!a(r^+G{}D`{WQsu(gCb%lO{(_mK-@gIp(KKj+`nva@ypWpAI>4dgRFQUg3`Ga9KYw za?~ftk)IItvSP1`6ghG- z%KNQa=PTm8IvQ&d!;*G=kl!G$qpRe|sgon8Oa4=NUEd=|&VU>_ z@mIU!ZAw20a^$4Qky9YQOZq92BgZF4PJxZ>oK@0IgaCPz+{ z964?B8R@4(j+`Dja=f$MaUCPi!(-(5d$S4h-lpxtug~PjalZ28=%+}Iern|Jl-Cz^ za^y708#&ugpB()R$dNN5M?c94cYZQ5KPhs|Plgg5+2uN|ke@F(RdVFi$+51QhRKaa`p3rdh9 zKShrG3_0=(`$mx(Hr%R4`=#yhyv1xa_$cdBVxJh#4Y@dG?$dOYb zM^2p_{WQpt(;`RCfc#>)euw0^PRHb!x71AZ{G`c|lO;!=KKa+>@v%&foGLkT+T@s@ z4momq7G>64?M0XcF;j2t=6dyV`w`8}w0 za(o@BNsiY|ZN>ZKpO*dyIp(cR zj&XI#F|IK==Eqxd$BUdeIdZb(Uye){Cd8ImI> zvFy$R=D(VS&8naLVnq{`-EQyu9A1;c|x6hbc8&AAiwqp z&kl3qtL|};pCCtmiX8a`a*V5?_)zimX1AZIS^K;{GRhNIr^!Rqn|c8`tjZzecYVlHS&MT<4&C%{WQtZPoEt9Brl2Xr=)m` z{EPB>p-ql{y5#tJ>6je-WZx3qPgU_A`PK6G-1_9`XGo6uNxU`sxJAXAijT64?M-O_p=_f;uesbjKr%aB1I*P~M=N=dJdGb@Fp8`4hDUqX}Iyv$Oil;7* z?#CzJEd7+p(NC2e{j|x^kN5uQSu=x0cdeiBzkAGfG@Q}Ge`m*nsHkIB(btnMCn ztsioH{WC+3d8;ViRXl!`d)(`!&jdO8Op&9{3_1ENkfYCr;zPyLA9VY?P5R7`qt6^U z`Ye#6&oVjs>?j`lklPRH^W-;6p9OOCSt3WDWpeacC&zIIil;st-KS4}A-?WJjy|j8 z=(A3aKHKEz)B8wtKRLx~w)t$W-%CqGjj#|z}>r$mna%jD>@PLAUa6iCO;Y7XG!rE`6uM( z=-cGzvrCTo?31Iz06~()X$3Np9_o#F1zLX$GpDA+mSs+J04f3!4%l;mfCON(@ z+$P8RACej>*wy_Vdww zRu%7&pCo3>L${4qK5Q#VHU?~^0HOpg32Ir7`&=-+EcA2+9Xjr{2;`}e`>cA6y0Y@@fP{s@^zIqIr{99qt7up`pJGJx}U1zJ@Vb8pFTPI8Iq%)#8;z_TU5NM z_=tR+^fM+$Ke4a5$3;IGa?DRf@vh?Wue*DU+k0j^eRf+~cA?Pkx}BuL3#xDUq-BLyqgNPLAUa$RCjB?L%@rj~$a& z6V~v9Ci@_|r`89InH^`CGBFAxie)GaojXHj+-UNaf{?QZd37*;+gNbeI6ivX35cKo_wtza_ol{@`EM6N{*Z^ zImQ*g)$J2rP`sh|Q1SF_(fMV?JLHGTxVq#RSDzg76YILiU6PzQIdYQZ$jOr*E;$8q zPK+Em33BA*$Uh)Cd2-|w$&phdKV5R_Lv3~mG$QhC&C-DP!9(TC7CCY{C+o0Ej+{C< z)?t_YJjv;iBWFO4ocNvYcsEN(cp-+ySAvtmq_qy|d{UJ$?oHRLdisaZIO615XlOv}|j{TuUj+_oT zaz^A>51AjkKk<%c@{?H^xPMaJ#Lvrj7BXZ<;KXK;)IcajNhcY?#hYC6JYvjo9 zkYj)7k|U>2j-1#}-TtvZ#L1D9Bu7r39Q#9o962R&Z%KWM9Q7sgZ%VySj`|k)ccs2fj`}fqSL(fAxP78N zOMZvc=g3iCAjf&BlH zf0O>ZwgNQXeNreV+WkQePmiookV5K^rM^s#@wUlve!XA2$K73WV&r+5haCAnQlBSBeU1D8sjrix&p!FVQa>O^ zeeySM|A$C@iX8PN^24OwCr5pY9P>6-Jo{U>pJzyZjy!*kd>xhi1gWo)_cqx29{CHU zzE6(dgO?b({k%fzljOCc%`cKqN_~kO=eJ3Y`5BRKlAJL)<~j3_+vl9rXUTgLw$BPV zj@wl{{yR4x^##Qnig1#*nH zN{&8ziYNZ!9vAgR#hZ$c6wmxMI=`ZLSMm76ZqCi}?+7Hw@$W>W$T2?!a*Ve@{&vZ4 zk|V!Oj{G4x^3#8F`+u$EXULJCBS(Ij9Qhr^V}Ey#i~2lyUHU1Iqn{Ev`l*v6e?UGZ z`9pH#kI9jr`bTvCKKZedUnWO>l^pqPa`f;0Gy1qW#cSmMC;imP(NB{c{q)Iek~1Jj z&WId2$#Hc5CB<9h&ynM{$#L8+IgUFfKT>kMf4Ti2Cr*x>EcvzA56Q6~R>?ml^)+(T z_sHKYKL^?;M}6WExBnUWd50u9>Wk!9S55NIOP?+BxIAu-$lFpsCP#hdQMb=qq&`cI z`U?4NQeP#igseN&SEv^~r7A{tuJ-6glckYL=K zZ;_*ZM2`9~IqEapMfaa2M}39-ESaAw`PrCf@)_|u`GR%m5i~P^xZSsf3JLF%M_0uK)r_}ezACUS!Ij*k( z`J<9EB*%3+BFFVUCdc*e?clEe|4M$0e7kMz<7}LKNAU#tc!&Httbg*0rM^dgiFlv9Dn1~;OngZGKJgLxmEvRa4~u(` zbJzbh;xY1%i^s`7C7vL^UOY+uMe!7QTRctvb@2@OH^sB$w~6P-e;}SGzgxUO{uA*c z`MI(`l*pIGee(0g%j6e{SIFNeUM0UoyheVhc%A&6;tlfmh&RbE7jKbYDc)ARL%#m_ zCx_1;y5z@+_sFl7{66`$;sf$eh!4p>BR(Q;iI2&@B<{uB_5T&|82K&Yaq@4AC&<4i zo+R&yr^xRWPm|v#o*{ogJWG!G&yi#P^W>QS0y*ZtNIsO~mdO7g?vszi%jAC(uaF-s z>!eCPmiij`0a9Nl$Mw}9e@t?kA16OrJVBljPm(`JJVpL|@ih6V;u-SO#IxjQ zi08=963>&rM!Y~iDPAPsBwiw)7x&4R#mnRuh*!v;j`dIeMyaonzeT)G{&w*O`MbrN z8_mg?+kmLID_HgqLlKL2VPyU`*g1mLKd>x(~ zIeBvA6v?xaQzJ)CogBw)k|V!Qj+_BGjyobpPI6Coex4%plOjibhJ3W%&QFOP>!d}F z{5Cmqy5ul`pz1;qvCjIBgvCa$RxGt*X$f=Q|zCn)s%06yCY3Zj+eyn&r;ntrZ zULb$Ic!NAAJ|sUwJiV`*k9jLA9@{UvzOMK{@znm&Ilkg;#k~XEoL9-XV&r(7O^{_Pju(!i_%Yu961?snxL`R62OM2;NqN$zoxlP14Wax&z|$&n+cO#WrbsgNV5Mvk2RA#R`Vm*2}hAjj*1 z5jkELBoB3S4wBzzn<7U}h8#I1@@GkoPmY`lIdWR$DamP*Bd1G_oH2P*=Er-gJ6`0( z$&r&KpOfD^nj=R}fgCwi@^?#4jT|`*a^&>L&zGD&IdX>N$VsH!d3cA+Pm&xtX>#O@ z$d8bJPijn#oY>Rc<6_@v9OnA3<=;JNk|U>0j(&#Zo~)k{IdZ(i-QyxBP5vk8Cqs^$ z9655zO^ zeexM@|941ziX8PN@*hgQPmcN)`H!W(O^*68`F&FFrQJSJpCunieU2RURq_X=zDADv z9{EF3-zP_X;wZPzKT3U)9Q8%=ze;_H9Q94|e@K0c9Q7meN2PvDj{3}cw|`Imz56UV z>MP{iNqvaNPdvikH}G< zKF00;5UJ0QqrOajxYSq3QQsjy{nzsMPRUUpd#2k@TJq!MsLzuhEA<6()Yr+ME%gm@ z)DOs?C-p;e)TfSh`#(kM)8wf4$xoB|GCAtoKX`bgRT$zLb+ zd2-a($g>CA^V=uiAUOka^f@BO^ZaDS?SDpcQsl_VkRvBYj=!H@B1gVYj{FKa@@wSy zeFfR$-TpEEIdaTrl^pY5BS(FYd`{*k@htbazIc)x<4TibTt)I#$tjT|r%aBVDmmuA zNsjy$Ir2N?$nTM3{xi>Z=iwshKTD3BJUNbAA%BzPRLN0aCr5sl{2h|hBS+4F969k5 z+;LqlISF#qr^xa1i3Rey&lkRzu@jvO!R_VX9XiIJl|L5}N6)t z_n#$4eU}_RU)&?d`W%qs>ksi4xW~oM)h5W1lOjh>fgC?iS|mq~PmY`hc}KqP)Fekv zn;bbq^4H0~8$KdOj(3VX56CH#Z?|Z_FHj-Jby_3Gb=o2So&20>mmK}{$`*X0W2$SIK{r%ryLd>yPoj+_=bat7o-mh(O&N6wfWIjNjGKLg20 zlOrcfjvSx-Y?+@jIdZDx$Z3$bBd1A@oId&CGOhtRaz^CHNxsOPpA+PIPmv=hLynvh`3oe+Cr3_&968w+ zyZ!uBuG1Vjath?gsgge+IW=$dMC2-R%>iu!BspbrAW-DKL3i>#pHOMm>|dV$sG9@uZzi%QzS=DjU4^d$x+`VM}D6i{S3&FGa^S$@@3KU zlOkXH`W`u6hnC3Ek57)A3ORCGAGa^ThS8(S6Icaj7_Y67Cdzl>jK!qIj9r7dO zJa)-3KYen{XY5sO|LY|uPL7-;Idbyk=%+xA`Vu+v>*VOCL5`djIdTT%=x0cd^Ef8Q zc}%@JdVbR6$jOo;$0x^nD3jy-w#l&`I^?MLUgP$8oSerPIp!xpj`_@ypCCDTa^w`r zky9f_KXr1{H_4ITCr3X6a^#H2k&}FF^!%jAaUL_|IFBWA^y8Bwr$UaL7CH9$G5Pak z{?ynS-aPi%wR&!XhS$&r&JM^2s`{S?SiUm{0-ogDo%$dS_`N6vs8{S3)* zUdH4&FR7{M`AL%_CrgeTpB(46O}--Y?`?FCdy#mI9FNyIa@6O^QC}lReVrWjeR9+f z$Wfo1j_yB2j`|Wg@_lmDx5!c7CP)349QEFe+b8O?Wk#4FOj3ZNsjs!IqFB`s2`J~K2wVBKTD4K3i&%_{;TAu?~YL=KACjYfM2`COInn)R$WdP=M}CDI^&Rr}%KUf9Q6D?k z?FaR7a@6O^QC}cOeVrWj4RX{E$WcEeM}2B8y8kpe>V0zLm&sAzCP#gT9QDZsw;w!S zloW3%K2|)t=;p8ey-;#IepSiw_ZVyBcwBCf<8iq~j{F`u`stJ7-}z1WZvW^fNsfNf zWAdWPh8;61NuplBPUIcoFe&8Wn3ll{JHYD zqj;12my**WM?W2M{5|#&Ir{X%$dS__ ze^Az8>XsLxOy8hV4H`j(sCfj+`Pna%$x0r%sOgCOPu^}0J3kHbTg6-C-xKeU-yz;3|DpJR{9f@9`A@~Y54-adzyERe-{~Xo z>~B3z9zVc(g1j@fo+Qt|Wydf-Mc&)S{!684a{m|7&qv%o{nI3$y!T@38S=Dvp1k%V zTVEm{z0i7vJTG1+Z{=)#lRPfoBKJ?V^|@=^arI8IUL;S8m&t1{kmHh%PL}+SyZL$X zB6;g1TVE!Ri`U5g=iB=9=iU6?^Q>oYa6NvW^g~|ptry5gi`I+eMRA|Jw_xkbjUz<_>jC)wDn{1w77Sp+fQS{*2l@?;tBG~*|t7MK6;(?B6(4~Ox}B~t*?=1#hc`< z*Vy_Fc~U&qcE?qFwXIK*d*XR=|5diWOFk%AACTw8$K;(?+WPoSZa-=96nWz;Tc0J5 zi~Ho2SJ?V0`RL`=8{|dtHhJ%5w!TN66(5qf&b0O3SKM(W#S`SUGi-gD+!N1{`*~Ym zBp?~u2Sv-Jb=r1*#&{fx&N7yr&{;!ao0~#JVxF-)Yiwzv*HQz)*-e&NuCr>k=LGL>(k_(c!u16vaQdOCzq|~ z$Qw)6^W;hK0(nE8pBKrKt2U=Z-dM5jlPAT?lE=k!-^Wq)y zPG9;bPm2%kbMqTNw)I2uxVZOox4v?(t&fq9?y;V|->ol-=g50Mvh{iLtayREb+@fA zk|)J|^4bq=eTCc;uaWzA+4=_gpl7|Qc!xYM^WPZCl?VAAQSum%J$6Bkz6F*7wP? z;sf&5H*EcoJSjdRuXSwwnA{Whe&w!P{}x*xBOlysJx-n%Pmp)MZtIieY4H?!<7>7) zOCA@`kypNI>x<;0uUIdUV}GuY7p1;R-n+@>G|02!P4ZUT)_2I0;$8CEjkbP3?uie{ z{V&^k??Lx`4ZdVOMxGZ>l6P*f^(peSc$U2JMO&XEkBb+{D_^knC2~C8Rmex5xAj%> zqIi=WkEdO7JZ>g`?aoiHCF3Q}il@n2pR@HD@}zi!ymq~Z?|jDA=YHdkH!WTuZ+u$%B#(=?$t$0dKFLQ->wWT~c=69}KX{#6BFF1opB%4q z%j9^SJ0S0U()KeX&x)u1;`Y<}gssnzC&jbmcpjc3$Mf(aIi4q%$niY6LXPLLRdPI! zZIIVKZu@VNd*U5(|6{hkOFsCh_2ggO`N@l?$UE2C`Yd@`JV)NR#?}|f&oA9bUfu~oPd(1O}+rP&w~QG?mtxE_WnZ%xAzC?aC?6shTG@$O}Kqt zpTNf#Xg=YgJcSR=*L=bQc?R#Er}>0?a_3{r=Z`g?@a$aU9=s|q!PBP62XJ3rfhXsf zd`O8_Y9No!##NhZ=G)PBe)~?KFQDjMaP-< zy_Dc>xeq_~Sd%ZquTegL-y*NTd-5v$A$bk{lstsn@z>$cD<8paz5&makKu32n{eCH zf`6=h8}1yh_ZPtzkays=zYDkX--FwHIvelL#s{Z^IXt zC-9}@9r&{HE_{Z(2VYH|!q=Af;p@u>@IXF3o)7&%wG2nD?W? zlXZ-{a7SK*hu<-I51xM8cnR*weR#aK$(P~TTE+u-u%Yn^JlVi_74FDu@Nj*T58;8f zTZbpU$wzQU-hhW|ntTjT%f_2zRBTp6NIy@N^B6@4!8I7ao7hjoL zJsug{?x*v4e!V$12lwPIJd}IzL|%mtHZ|j{!2^vygh!X@JOj5qO?a}J>2JYpPY2!# zOuh@Z{XMv&{uFNe2lP!$e+IYxBY3!~$&cZ--~A$gysDdwHe+!mKYd)PX zaa`0>fF~=Neiv?gO7LV?llS3voMpJ9{s3=}cr}-SiZT|!wuVBXM{3m~WZNCdoY9{Z&ZGQ=#EpPHZ z-1b-D1FfqXJWx*vZ`t!F-1ao#$#P~qEx7IJz&)*>F5LF_;EwuJxa}XnJ8J^d+=n2$!Bm^K7z+9nEV(%Uf%cw9?DD3X68Eg>;HG0!Ez?= z!vlF4-t|mAgnRNjytS;!M{q~pfJf6!z5~yeG2Vq&YecjXg! zyp+k8=E%?IcuC_vJd~H=gQCfY@IYRNcfV=!5!{nE;H@Q0z5{pUU3j#($@k#dRO1=E zDj&hq#Y}z-_vI6KvZ%?IrsU_-mHY5`5tA>&$FA`Z9?I+R!8c4kf(P;jyt}Z;ci^79 z3vVrC@;$gC&*0I5CO?8_3m6~6tMUmvo!{h3bLQvMm;3N!K9euQU3mzP3npKOkLNWW z!9#fiKA6YkJMci>g?HyR`5xSpXYkfsCO?8Z@-aM`)8r@cY>M&HT>1H|%6)h`hsl@W zzC45{j>*^It~`RrUzxv~4fyy!#yjv(-h~goH2EGpkZ17j7bZV~d-5^7^|{GU;EudB zcYZ#j&rIHjXP+7`!>jTTo=!}@4)^5|Jo&`r8*o?NfyW=4d>20c$aoJP$}{-jLz5rD z1Nj)<{lMfWa8F*ECqJL9_f6i1JMuC-de7uTcs4d(hganhJpH%HH{ia!15f^C@?E$q z@4@4DO+JH<-!VRdhw?Fe@V3cM;DNj}Z+rImn;f}lxk47dR!LxrF zZ@{ba4m^FsWO;8*jsHe=-{{EtntAyL*|}AN|>QA8yApfZLw>Lizr;HJ=gO z_B3YWW4P^^z-^DSaDKc0(srA0+tY&Ep7w0q{YJju_7vf^$AjDPbl^7MosIY4$F4T> z>-CeuXRg1(*WQO;x`#QwgV}h+&F_~p#jO7-{AHa7YH*tm;dZ}9aGM{)ZT|#r_t#k@ zKTf;91-RXAb2i?Zjkn=;-V(U&@4)RiON-{md8NK@$A{aVGTd&r55Mbpb6f^+yWJt& z=Ie{)$78n}!EH|iZu5ny|GUS9+x_jpZBG|&w_92KfA?46c78^1+dqce{t5g|orjxC z{O|3yX5($RJ>QnT`M-Po*?1ZLp3YZ&xZSS--0s&9{-Jv6#s58?2yS~Ca68Y1CI5Gi zI~y;;ZBJ)5-i6!!tt^$_uAS#9+~!B{dvtuqaGP%~o$t5anvIvH{qKAV-+GJX%y}3- z(Qz5TzmyN*Q}jJl8GK&(2)>Yf3|~w>fiEd{mdWqe^M~mA1ip;&E_``;5x%nAgI_Sq z^q1hPDeuF5c?G_Xyb9kyUW0#EUWacckKh$~4BuAXgzqG8!53Y|oVVNXz5lS`*XtyK z&r(kZzPCJu2l77ry{*i3!T|om2hDhf@OyVM$0dXBr~VQAAh|O=zkb{!7XEs_3UK@P zs0g3(x*3lL?;dV?N^tx4r3|+{0o;pBPX)f=N#?q!2LJaz%y>e0bfoF2!#7n=1OBdh zVt9Ch>1o2JY;TTN8~(m}5_odF>FL0qnA_}E4{m!>c=r_3(}#aD#qlez@f6|z(Re&~be8EU!5=xotn)H_ZjC2^JDQ&g ze9h0zI;p|!It=0CGt7AE@Y{DcA9qYo55BN^ zQh4hU)6<8avA;RKL%2P@89cts^o-!o>Urn{zKC5XUVfc~IzJTP>;B&CR}pTHiwEys zY{pZ9|61#<3_sqk6ZrV2rl$fA|E+ZbxAPOiozqQE9e%lvR}A0z1oOCP!pFZdJuSHZ zYvXPBVjq}sCh%6rS@6TKQj3ad@JP#@a^S8cuhWncMmfC z6Zk&Ld&}qN=TLbGew4fdZ^$F~+Pj!}i{a7vW;|{98R|*lPn>Vo;Q;RLZF+|As`hsT zKS%vz_=R$Jh5S6UcMK)%17aSKIl7 zk2Rhl{08Mm@Zb>B@65>0&mDH&;ED1@cyy%cDZw97PZ@4|0=Vs|!kf8- zHxD+~8*R89e-G}fCxx#g&*0yckKmij3oGUKcL%u(x8w2Qdn;dt+k6dvnDR0FczFkY zrhEWz$tUoi%e|HJ`*owd1h?a?z;9E&3b*qd!S7W*hI>CY>$VMlSosw0tY*%)efU$# zkKlijkKs1&mh$uPvhp50(s`^5A1NQg-ktw;mKa6rv{I7JywUGqMjD~NxeSOhM%r{4}Olk50AB9 z8T=CE3#;Yl;VO9%9%#RO_;t#c;Wx``@Vn$8+#atOexLG9_+#=8{EzY;Jl)?MmjV0* z!2oHa~(d zqx=|d^X|9u^DsmCGJFkr1>V(lS{3eS{zG_KJxzE;-i7ZfAHw&OXK?%c!dWA~Uxz4P zfZKT}!H-tnhueG=exmXX_-XPOZhI2=xypCom&*I_U&_buo8-kc^Yd`0Jb*taufv~^ zx8N_zd+@)@Q@A}pWbn6?FZlU!ek}LlbL?y$M`idT@)~@)JcQf%kKrpS--3_#H0P5x zJUrKU2fns?x^TPQ0elagH!`@R^Zx{Xpgmu$m7lluwcQH*`|=20leggq$y0cu`xPVj z3cAlTfxCyA`El0HkF$G`aTk7qwp)guE)U@s$(!)YjaC`lh!Qat5jN!It0=GSdb@KE4 ziF$naJi31szy~Lpd8ok`Q@#%OcQ^SMzNGRU_zLnKJl1vx@Kuzbz}J#{>*n`&J$VJb zsXT&jFK@&5lBe(kYv2 z;MIodZ^F+}z6HNP-i5d2L-^J55j@?_Y}YC0=V8Z(&3z;n?(6Se5pM5`mf#(2w+g>q z9>UWT%y=5`yOmGiduyCsc)Ew_>BApU&j9|cd<=g@K7rfkLB;j+^Y*6lCAhEs3gGW4 zUxClDt2wXM;Zx-i+~!;G<&;n0zV6fZ;HxR0!Pk}-HpuVq`f?v0$ZPPe(0#Y?=`=L@NdZr z-_4I_1Gx{s{>vr4e*Liwx33G-;G3x@hHoox!2_+U4t!VTd+_vFvknLFcQnrv_`d4# zg8cs0zZz49jfad{VRuYZQ{7nC2tt9qT(*)%`?e<)vqzb`MtyXvXJ=ct*#gCV@7*Vh{G zS9Ly5;ESlI4_{V3hOa4iHp}lhckMJ-NGOew=~44F8@ygzq44!fWy_{2+M`Ztq(T;YTW;!EN5zDnI^)@&&lf zm*78Az6w8A-hf{!Z^GSk%<*c$BY6V9T0MQZKc`tIWB4t~PvCdTi z=j0LmWqAw!w!96u`5ycu!N>M>8TdBJdt2xC_fq}+t-yCuK7`xnrxAR2<=gO3p2823kKpI6 zVb-Vn{ror&SH1}U@B4h@sMjT(ZSv!{@6&VP z_I^Z^yC-D2^UAU*~`9Az%<%jT=Ue_4IpHRNIV}5_1kq7V> z%(onJR1+-_VXPT_{Arfan|6e_BVvzxQuz-tq%W%dLp>}JbnXykMd1;a=Dr37X0PM z&3(u=ymFKoe*(9k2kF3{&~|%pSNog7cdVHG?ZX?|?f|~ivSwWk;V){tBY1M9+21jI z4Si4h1fFQS&Mx_NIL$HhP=LRt?H1wj&&+l`_`j7e!BcJ5hi|a8*=`yBsd_5#@fBvf zRd_|8v#7zx+HMH9pBJvf3k@@#20Yffis6eX--LTdo1PZjem=JiUs63Cc&P1m;r8!u z4_?)FQ}}+LnESMS_zZ1#2zT3No-_D*x*i+BV{LZ~KTg}7z`vvI7Iw|A!%UAO7j7R% zMR-Ts_2Bk<7)tOhwB0g1)Z-|CZ>M|(o@u*P`0mQr;QPw!@T%rHf*+xL1714Dtg9Gq zKX=xIpQN5PJkWL%`1#6r;8kt63%8$(?7=TlPap1Ro(J$%19P4n!eebWgI}Zm5&RbU z1U|UTtY>Gp{Ccy02Mh4~)#JjSlNaHCm6zaSZP$m}?Uv#HR8Ii!tZe=+RNxckYw%Rt z4dHgXb$Cbf9KjdfP3J@SyeArO!X2%v7Tj*P4IgN`34BrYci_v&d+$rNtSSABTl zSTp_s{9EcD!Z(nQ;HkDdhTH8<;AL&sspZ$`!BcHFg#TUNCs2o9qU|=|p0*pq?RJ~+k+z${Pt|o%AD*3U)f}3~t{CSlBb)X-#K<@07-!I!=fS2wu^X9?rc71qv z6|>zky<|Lu+wIojcDpg$Znp*Z)Zd2tE1T`6@Mos!`4QZ%=M3K6#Pp2d>E_1A@OU%h zg}w9h>~Ct^g|{{`?!leo%=!=DuWCJ4;C4Ny@OvLH$FUE$`Cy;?I5&LI9 zH{^x=^6MdzyYRZ)gC8U>!9#f&?#ToA_)RmPRd_0|!DD$H9>^oOBah+f$ZWR>kL7K6 zC{N&lybJf_J-8$9!?S;y@eJUJJcCE_5j>Dj;I7=+KY!dExeFh^Va8L0XYvw!Aot;^ zJb-uQ75EME8oVtJ;Z1o2Z^#?)y1WSw>>A-ho%;J@_{A6kd@J;G4^b@R{-v zd;|FyzOLLkAb;GyBQL<$mKWi^+=G|oK71v489rTJfxGf5+>wXy^mVg7>+qJm0T1Od zJdn5Gp1cirO)FJe8;LMBazT@*zBwXYfEihI{e}yeKamm|qW$+=XXs^)Rd^(?!9#f+UX@4iKpw+=c@yr*+i+K&z~_;7;gi?Qy!GIjybtfo z2k@>ugD3J4yd|H&W4Uus{w$oIma@c>x~F zi||11!9BSTcjRSwmYMmlzz6awyekjkiM$SvXQz zg?HtBcuPKn$MOsw%Exd|K7l*(LOs78vX{;HU3gdS!4r829?Q${NFKne@&hHt1ln>!m`3Sz$baS6%0(Vw1Uid+No>wjzFT!up z>oO(yF7h(`RQoy(Jk&UAa7Wv%!>7p`@Tc@W?M?W>>TknunPK*;1K&aO(1S-Be;@wc z6;00&e#o81NASZmo(cRIdEt=!{BNfIBK#!fOYl?WW%yin{^6_0Yw$DFQ-_}`Z@{N% z-kR`|`rGh})YE})Vdo!yneu)3&*Ve+EcJ}wS1CV%cjSdb^YeeFya>NrUV`5*FT)>_ zSKyD!Yw%k&Z*};i$~WLIuVmI+6TZN2jJM$P%G>ZciFtoT7ygvS*@HhL zPvIBrr~BdX8}~Ek&jI`{`4GPTz9yf+f4PV85&U}P$M9R_6ZneSU+1v=I@$gUGY(p(!KdtL#$SRjEcf9{$jk8Q@&NwfQ)Zo1;EU+IT7`dCJvI1N@(}*u zK4$!N_!II7{<7w?0e?~R8N*+ZH{t)3x8U!{+wk}03H(!e2Y!_Hw+lZ>-h-bZPvIBH z`|$s0y94+?)7m#Q0MdTxRQ9gz*BcH%mkUNLx*Z(T=0(?!m3tv}WgwK?F@Xh2U z_||eCUX_>OJIe$3f^%v8!)wY{;d{ty@O|VVe1CZzzR-Wnc_V_`>&6EBVD-fCL*-5Q zk@6P&$2yO-;eWR0fB4+J@v%e|)HC^ZQ;V*w|@&oun+U^j3 ztoAp9zd45){|Nqo{?3l!`|A2|0-rRy(ZXMp2NAN$&8}NZVhVP^8HsPn* z^$&klJ#F|8l~3R&s=ou@UEYPq@*eylZ8wD;EUp7vM{F&Ew96U#@%+ezwjJ9{d-_>-qGs55Gb_fd5)Pguf)u;PdM`a|B;bK8Am^qj}s- z;5X_xI#GW8FQ&hn1^74RF8pl$T`R)BwAcUew$@t-ex}Cn!_SkK;lEH%06$sR+ZFg) zpP9c?RrpWUUxQyE58;<$CyEd zj-zu_e*M3%aTee&X*@1`ivHde;cqGL!9TX^A70iv@!@-D{AKvQ@&Nvs#$SQIWY<4@ zvD?kxwHo|J^@Q+i<#qUP{i{JcfUwd27NyxXp~S1wTtYZTK6?C-7I~9r#P? z>B9e`@$}$RbR1Lo+v@4VpO+8d)73MCdpa%|e4gFR<7)(8Nj+ouLds9zODOLgonQY` zl`p`r(D+^WuXLSLgilkC2fs%75_~1)efZzC-7a>xga1|@ z!XJ~@;eV7z@E7F`_(}R+h#3B=@=f@g@)rDfod??R)8z?#S9u5SJ*d~w;OlDMdhiY8 zDST6TA0F%9I~%}{I>^lb5PqYsCo_0cJtKHqK8D-Zp(gOHHBRT4{QCd-+~zz}fWK?6 zf8Y~6A1}gJTGRA+@Wbr-hu^R3C?9@-p4XS*XRAMe+t=4B@a?s~RroIQ8vGoslMsHP zybixu9>Moee*?acJcjQlZ^93fx8OgJx8X;~6Zo<64*Udp7k;w52R~Jw!cUj?;b+MQ z@N?xu_yzI|{uB8KeyMy6Z_6j}pUa(N^XvbY@&f!ixeLEhUW9k$9{etO34Wj4hd(SY z!~608{*1f=e_mdN|4m+l|3ei=~zKeVU-&^h+mtX$} z%M0*G?!r%$7vZPNJ@|R@68sXm5C6Hm48KMmz<(vLz`OD){BC&-{;)iRKP9ijpOr`O z7v&9jCXeC&lsDn;%3JUc;ScG$t`C3IUjM_d zk`Lk6=sGro&!vB-bp-$Zp5}S_7+z3*0>4$)AWKvhotVB=_MP$jk7Zbe}nZ-z2ZVZalVhF>O6;8)2z@VSmO^U#H_9U1SzXUbFf7V@0% z{&F9FuDlFy%LDk;@(TO`c@_Ssyas<-9>V`5ufzW$kKnJ#8}PT}G5iC06aIy~1wZT< za~#|76XXf}LU{*%g}e*DPTqsxD^KCi$@}mh=sIcuKU1%h4&i}g?jL6Gr~1Z6@T1i~ zhR>;gXL$l&K<=EBU;p>&c}D@hnDQ?CIK4lr2tQoz!Piw!34W2TyL|ZJ%9r8C%LDkS z@(TQHc@=(TAd*WtgCNATO_4fy@?7=EITV-x$DU#*@3 ze17F!_`Z7nUWCt3-h;0uFTvNA`|$PUW%%_PX8;d1o(g;yJs+vUi+WwS2ES4LA^ZZJ zuj=qym5<;b==IYE{B6BYJBEKLZ^F0LJhb3D$=mSt^m*oo4s@ypxzy8;f7vP)8UHEqL zBK&d9n+N}<-X~mwFDm!p8|u2f4ByON|HHp;um9oi>U%7!@PEo{@Evr#Lih}M9lo19 zf=`h*;H%1G_}cO&e13TgzK^^O|3Ledz(13B;AQo6;Rnila95tfH<9<@OUMWCt>r`b zba@6}RL`eI@K?28WB6a?6ZnqmaekOz|Fh%;_+mz=5&Qyq1Ae(YhW|_6giqux_!K=)Y{M6n zC-5cY9k?g&!dI2|;74eErtoc*@58@;tGR9;z@O21atQykJcDnd_XCXJe^q`A&*T&M z8*=B={QCd4K5tlnKd$?(F8ojOBK&2!2Y*vuf`2IY;qPdiW%w6*z8}En((}X$d?9%i zzJ|O8Z^=XW@jC8x_t)CX$nWg8y@OkA4d|`P9K2_d@ zFD>uEmzSsTEsr(h>BF~`58x}QX9!y^N&iPS({cozC z0(@^h?p*i{@*;eu#_7Q~lb7J@YP&xCF1;_P4BuKk0lX@&!0qSstMHwbufcu2z81pI z-O9YqScgxupZ|j2tmEE*-zksb56GMF$K@^fVfy#K+VIkGW*sK*wd5W6EX_j~zQ4Q& zugg>TVe&rwDER<>oO}pBNuI&amXF{+mXF~r`2>EM+==t+{}=KCe5&Tzg|8+r!dH}g z@NdXV@TKHFd`o#5zN0*VZzHe3H<4H2%gSr;`Q#ydLwOx;{~fvrK9}+h_`32KevRh8 z3E%E@bN$eQ-=usSew#dj|5o0Cr}8fR33(6xCwU70o4gMn$p`Qc?x70YR@Ktm^tig9xK7_BWd>!s;zascw^uC1#d_H~tG=`t2`xs65i<+Mnd=d4u z;Rk8_34C?UPY1rXybCYOd+?3rDcpX4bszqP=4SwRbpLG#_w4guct_voFoNGN9cZ_b9#RL&(L@Z@IUH%DqQ$q+oIV5&R%s&o|(EDj&ly(Y!U``zzmq|3Kb`|J<&B z_;vCQ{7Ch5;csf)_TcNuQ}_Zpj(zyt@&Wt=ZFdMiS)RdvBp<=gkdNW#%O~(#G!M=h z`St%>c>(^A+=V|SFTyX@c0Kr&@)G?2-To-hpo{@4~l~_u$*fQ}{0OK73F4 z0Dh2s2#@3${6zT(e!6@NKVLq9UnY0X%&-6JO`|!E+ zx$iQ3syu+tkXPW}kyqiH%WLqRQ|`gHl9%8+$bI-8@-qBDc>q6BUV)z^ufori*Wee*LwH+WhhHO) z;J3&d@SZ$||4!b7KO=9!|0-|8-;gKp_v9V;m+~%rKE2*%PgSX`+_|{ z{*t^1AIUxVhw>8K(erm7zM#AeFUkY>40#3qEqN8buDk~St~`WS z9>dR+H{ln{Tkvb-ZTPR{3H%;;2mYA63x8JLgTE|K;cvvK_!Rs7pYVm{8GI@E z2)>eh3}0J5fp08#n)&s=wY&h|UGBo`@*?~Mxd%U6UV>jL_u<#c%kZu|fZr#tz#o-Y z;eU|V;4jEScqXsI-<3!3&*TmGy!)H`$uWGYya``U-h!_oZ^Ji~C-92A1K&m7h3_Zt z!H<-u@KfY{_}TISyd@vPe(j% z!52Kh9QP7@Nx2VSNnVD3TOPmzc?G_;yb9l0UW4x~58-ur9e#{Ff}bjHz|WP(@Jr-P z_%Gxw_;vC&{APIqzeC=EKPd0QpOp9DLwO2+L*9pfC?CM5=zCp;a95ter^`q1)#YRO z2J#7f3%T>-{Q9rT3-DQT7k+@e2tPvZ!5i`t{0zAdzffL=Um*|R*U2mJyW~~)qw*Sj zAP?a~c^#g~Blt+(fRE)d{0n&#KA%1x(Sk1_Z^KuRCvachfzOn8;op<@;JeCG`2O-f zJdzLKC&`EK)8rZa$MO;UGWi((OZfzTv)nl^zy5ER7vT5EUHC)tBD^p6;LpfQ@cULb zukZWt7nCoqqZ;sQ^*O8k$X7v$If$?^jHbh!&ZS6+l)Ecf77 z%1iM7ll$;n5=zSKyDytMEU_Yw+jgA^a729X^sr@PEr2@QFNzeN@Xh4|_;&Ikye7}!`^iV}!{lT5aqE|1|W%bRdt-h!_$Z^Jj0C-80M9e7RNh3_Np!4H{73R3{A_s!KVLqAUo0QPe@&|3CQ zhwmtl;Irfn_<`~m9?6^VljSY=+4460B6$LD%RBI2%DeCz0B77IQ2j5Fxf*&aN;fKr1@Z;nG{D<-i{49ADeu2COzf>N=uawu}*UKaL zE%FBZPI(N!U*3d2CU3!?k+|JMhKj zUHG!{9()yf3SUd!hi@n!z&Do<;oHbF_|EbXd{6lpet>)eKSu6cl3)L)$P4hZf5;o~cjPhr zLwOVaxx59RqR+#&;q%E8xGV3#i}EgfhP(&&kqTm*&_1$?^jHOt}lcKwgAjCimc1$xHC-RTufTsNufm^} z*WiDahwzu>b@&_d2>yY*0smYc!{^lZt~B8b%3JWoefam~1NctzA$$*c2H#IUg8x81hDY)Vydifk%dh`fUVt~{F8m^S5q`PcgI^^t z!LOJ5@LS|%_?_|qe!si|e?(q|KP9ihpOuI37v**MtMUl`rn~`vUmn9hmp9>4^f}@d zd?9%oUX&;B<>Vdss`4)U+wvZKraXmjDeuFp@&SC7dV9* z_wdx=-;hV}qPziLP9DQol{ewvmbc(DdgI_Hl!Ecg};djU<@cZRXJHP%PlNaF6$X)nfO=1*WugBBlvFe27F(63_o1ngr6vH!JG0n{Bn5$ zzd_!C-zo3HACdRqf0C#0*W`Wpd-4JNOZgB!pT1WigHM%@;LFO#@Kxm#_&Rduiv0TD zNM3+%A$Q^1%8T$_I^?fxNe5Ss)a0D-$U|!!J!xxrM;8W$!&+_YkX?X#@yxfJaA}_+%mV5B^ zODe9lo-TO9Veo`38LMr_KAl zWB8x79-8o%Tknu)cZ~o_%gehdG5f^R8JRvzPty&R{be_W%c*rd#$SPzlC3> z{vmv479!_>Ia>;A`0L|NMD={a5twZWQ3#$zAwWdb}6mw`;p)_((k! z_}c5Q;Qasp)!@s?>+lcN(|}(iZ^BQIx8XO*JMfd`J@`BFK72ima|k~}`4M~~pTNiR z!Y}g2pZ_o9Mfe;I^Y^p_pGRJXFCeeLU3m>YRbGb|~} z;l6wb|BieFFUu$JjpT(SKmVJ`i}0=FCHOY-GJFSl1-`4i2A?Ib!}pdq;QPs&@Pp)S z_#yHR{0Mmuezd#~KVCkBpC})}f3b^sUNeE8qI}_(`T75mya+#CUV@(`FT7T;7CVDR0BCm3QF3lK0@hk@w+&#y^DL zsr(3jk9-1uP+qt;KmU)&i|{ApCHNoYW%!@u75GqIga1ulhrcFoz(?{X{9p1m{6l#M z{+YZ7cTO~a@A~k01{UWUIRufX4t*WmBV>+p%Z0soJ@37_jEbKYpf=a+ZjuDl0dLf(f@lMms` z$w%;&pqzO%dupCxa@LwN^& zkh}*!Ox}kdEg!-g@)7)0`2>EZywJ(d|BvNGcuQV_UnVcZe<82HuaVc_9eEx8Yk32H zhr9{DN8W~~@(%nlc@O@yybr%e&+CWq2jwI9Bk~FSNqON{`S~Bni}2^=CHPD7GW<1p z1-^r>OKb35<#qTT@&k;r(uP~m$otw<@us`ki7yRRfU++ml|K0jpOPPNs z%Z1x<7U6b09z4+R_NVRt&4){Tz1<4(E8JtA*H_`L##w_eE)U^L$?NcC>_ya8WD z9>ZJu-Tt)ub(Qw3jlA981ips)yYN)M+n=`I&QBkCyWIiYZa0IE^}GFP`>)scqD+v# zR~!3ol{30&gs8yaqpbXY+Uo z;T`28_~Pnszz51V;nS3F!6(Wm@CT=v@pRzcVrKk3`0|gK$8QR+DnEelw5;hL!kfyE z;4iOa@?&_aymL!_JzS*m7vL*s9*Xdd?$3?Ww^ZQBMfBJ$3l=%17`w zjtUzMXsww>=ZM?QwpSU;n$SrvSG-F5LDM;dS+R@P@nuKTYn#&z6_rwkLpJu6zZ4 zrMwEaJvF%P3E^9BvE0|!b#?dx6Z3Z@f*(^c=eGuY&Cg9fhHrS1$v5GjOflYqPjieX za68Tpe8cU{Brx|BK|K0j@xeH%W zUWBhD_uzqkw?A$F`Hz{uzZK+Xs;3HHTd&jA;9Dvm!Xy1|f7w%dgtqW%<~>393n_8+bM2>H|GWB7&g3H%pw=hpmr=@xkbexKZh zd-~n}wBww=Z;o#X`KQ(6!-w)R{B?N%5B0nKX}9~^Uz>TZBmbnXKO^|V@&^1dc?|!9 zya|6+-hw|bZ^Qp0PvEJ3w?FNEEus1BBmbV}X8`~2^*Q`wS-dsk-P=pOx}iXB~Ren$UE?zpzs}8OyDj)LJD7YMzGKCB0-yRr;~n^` z19Lslg)gam3QuM&@b&fo0Pf%Ce(ghewxP*8cjk{{cjE=W&KKZLXxxQ+>Mz0v_nPf` z^kvOz6|%|Aw1d1cZj7M-+<8Q!IjX#FBmN7j|xFc`DqiH7JhG$C~ zPvN1)-=}N*1Gr<4FTAVqXYi`FJA$W6nemL_zI*~tmNa?quKfD(H2xA@XvR~8kH2ZW1`p*iJlNCZTk!O5v!2`Vf!1vY-nq@aQpZw!fk&E9_Tz!hTHxCZXc&rxa|+&**<3eBY2|uZ@~Zi zc!YN~|84kqZ`0p_+x{-xKCV-^?H|H}@0jys2DkkqxII5i;I_Ze%OA(_j;6l|xBVX6 zo@acx?GNC|Ofx@Kc&z!a!R`5^4)1CH8}PCEn{eCTg4^>_0=NA=c|wm*ee_4j=MAK#;O4(~2x#-G6ln*T98QNMG4{8ZkPPaQtcd`58F-++7SkKwkz1rIcz3EcK~;PG5$ z{9U;1AHY-1=MbLFX?imFSo7(m`Qu`H3h+QZF5LE%;9bq954ZhgcrwL|Gl1LvD%{h2 zhH%?ohdb(z;I_X7Z|S~A8y?PK#+kq;n$Hw&d;0LyF+Bsg?HR$n-A#TBxBU~ir+(+5 z{PDH@F5J_7OAl`QOYr!s`MBDV*6&`6mYw+wprYD3umz(=GG2Hev;emQuaNE;? zS2s2D*@fHw9z6Ndj5CGX{sBDE@yg(~e*|~bKc;IQ+=uhWG1hz*;o%o%oF2TOo&aup zD)97k(^G}po(OJx8gNfNG2HefaNE=@;_SE5ydLp>(X~C=Z{00v{G2=<#rM7uIrf}QSho>K# zo&ntUjNr+RI={hf{{-%--+3f|yllS<&vf70gWLWRJpRax--p}&D%|$e;Ms?!CxqLc z7;bx-@IXB+xb5k{?R<9Ow!a5YJ}~1<;kJJOxAU37ZT|@FsDBK%{m!HLeV1NFOb+h2m)`S{@(4NoXwtoWm)bBi=Uk|q5g=czw(}Uao5#r@(p;Pdj5NZF8p5QJ^1(|lP|#^RlW>wC1yPY@INYFg$KVh`5OEMbCgfvE#>?0gLQm|@JxRnGx(eOK8GG93kDfN~ zzm4Hl<(u%UwZCn6wz?Tl0zYXDbAIl^{co9k4}RtzD}H_5(uYUN58xLlpTXTVO#cXe zjXvK!fmfAx{*XWJH)wxdxO1nO&m#P0CJ^#bk)%k4*_jUfs;CJbBLt}VV z`3Zbe^%n;D^)OK0h0j#pgQv=u;P&6GD8rML&3X&q8)`nQaKB{oHTV|F*Wm-@Blz~p z$MAR+)8B;eu6!HbRX%~A@Q^vaU3jK^55Bkh`*2t5X8_+{`3xQ?KY|~u`~)5;@BA@; z+z(UUg;(#@`5zuBUxKGP|M>6|ln>yU@)h_`l&`^E~cA`%jf&g=XIHDx6k7|&-0v_*Sr?*7yK3VJ$PMx48LD} z0{7Jq;18=G!dvPy_!H{K@Id_p{*3x5JXBx6Ur_HnT)ys+`U?DZ^;LMZ39YefR_F2k=yV3V&RE z2KSaW^GERK)KB1%`W*g>`U0M-pTYmFzVb-iHH<;JM2E6_W zGv9|VrM?AE)VJX)sqetuWzGB$zNY#J9;xraH&EY)7wQxE7uBcm)^cV&L-;o8NAOVn z7{0yw9Gke-~~B&(u4QmaqF<^)5VDUxn|e_0-_b^5%T&@ZHt>a8G>`et`Nm zyrn*XAE7>kN9wzD^*wm1K8By6K7kkN2k;Bk58+|e>`w+Cs2{@%^%M9l>ZkB%1v9^Z z-=*GptbETB>L)HmRcXXg9xqt&{4(_^y#6V({vrH2^&@zqehmMe`W)`AX68@fcdDPkBlXVT%Gdob>RouMz6yU_ zeGNWVUxz=d-iH_JoA8&^x8cbe=6nPA({s(|yAVEB--W-X`8{~9K87Ez_rnR?Thpv( z0ROk<58t5f<^cDD6eHHG1-t->) zarJe$x1s49@Tb)`;l;M5Z^2(sAHd`7P2YjPs=f2vt1>I-*>Kg^?i8!UDGG<%k=%s6doR6`XT&k z^&_}J6!^i3q_+0fVJpR7f&mnxFo6OgzBY1eY>BsP;)aP*L2-8pDtEivB zbM?;O%h$c8-i7-|n)y}uhU#nZvHCjvOX_{N_X9J(3Ex3|8=k5U;Jd01;r<*mzYE`2 zeGfiXAHxq(pTNBzn)w6x57iIhq52Gdy!tUbQa^$JRQ(j5s4w8>t9PC$U-wjf1%8qG zDm+u~!LL+bhx=W#KMnYe>YMOHeG7hv`T#yw-+|w!z6)>t$gC%VKdwH8r|SFg=hYA3 zx%w3Ty7~<69c9)tg1@JJ0*}<^@Q>YWUQY`6Sp5vXl={lE<@>?=v00A`Us=5ePu17p zYpQR+oukcsA6{4Ag1hS5@XgeB;4Sqbys18dJI9#y^x)rA--ie46Zk&rQ+TL;2tQ2y z2p*{)!;ev)!^i5U@Sge^ynd`XUnehL_w&`eaOXJFSK*hbuffOa>+s*G_u;L`%x}W~ zpuP={)d%qV)Q9j?eHT7a--BoBWB5zz6S(^mv;G16E%igVuRep%e~Wqkj^Qo!6ZmuQ zny+7`@K}8Te@eacT=}|>)mPxJsjtG_}=ozMK924?IvG!RJ>W!&CKr_&Vwb@IrkG-#~o^_fItIAHf$@KY_>UbND{$3;0<5 z4E}Fjugde~`@uWOtjC4VRqw$=^)s+pCY@ zk@_Bdj`}{lP@lkEJ%3ZUcZym65WbT75!_clhOenUho?O=e+u6~{S2O~cU~x8_s!M2 zaOYGrzY1@vufb#Wb@tq5 z56?8~X}}*+--PGtTkt2;2k_u5Grt3WL46kHXsgL2Q`ab+!^#gdWK84TsJM;NH zgS)X={|NqZ^%JzJRCdXK+t_<$uffgL}4Fj|*Q{y$6rg*WeqeZ@_c)KKx7S zTX5$bvz|75JM|s7r#^&#Q+)(a)c4?fsqe!J^$Gky^(ovv*Q|dC|GxSWysmx>|FQZU zo}6dqPvNJipTWZmOz*r@zV7F!cj3{`OkagxtiA?s{oM3*_|@utxcdv!H{my{Z^OL{ zO&`EB^&vb}--SP*z6Z~KY39f9zo}2)ih5=)DPg%ugv@uzMJ|CUccP*Blv#mC-6vp4*#C|0$!+}!H-g3 znU?Q|_zJTg7k;vO4_?30^fma|>KpLZRi^jh7piZ;3-xXI73w>1cVOm+@Eg=ea9@27 ze%0;f`O=3cSDX0>{C3Sx;m$RtAHx4f{Rr-r zR`0@N^;P)GT2Bo=R$qs|sosZszc%~Rg#Sl<8y>0;;J19+oNovptM9@u)qhXdgBR*! z_%)iJ!0XqV^$*~i?5m&u!UOdg{3i8dc&2^=e^Tq2!gKWne4zQxE9L9%U1!!`f&W$W ztMHb3558{4oNpbTs&BwIQQw4*)wkfQs}JDr^=ADY_y+2`a9@1{|LHzv{V_aI--rKP z{Q#b+PvIx3&*08)%=$<0bJS1Zb@e&?S^d620UxWM!CzBfd9{2$xT#r>3!kX>;F0JR>Pt=F->(obZ_eL|n2mgxJ--oxhr7Qu>z~4RwXgs1x_alIH!YL-jp)tUiYS>jU%s_5?mwKY%Z&zyCgj zyEmKlWbl)8zGHY@{RIAF^;39DeF0ze_j>>TTKT$%>MQVN)K}rLdJn#+)>DV4x0v&7 zz#q{5H{rqWOy7bpul)?*?$God_&Vyl@JxLK|GCy5!`Id*ZeF|S$`L+mTR0&?CU=~_@kLWgn!Py{=+l% zWB9g5n%CPL?q_EH6#g~!GkB!ld82&Yx6=8#@JxLbzPO(wDK3Reb~AQt!i`R^NiB>f7*_)py{z`Vju6`UoEWk6C{Y z{=WJ?JW-#(7rfQHze?fGeP;d;zPS1kysmx>Urv1vPt;H0pHx4CkJUSGmaltFy$i43 zZ`NOhe@=Z39;mOwH&yS$3-wL-*6Q1E?*X%(0RC0=Av{vwh3}%i2T#?<@V(V1aQ`o6 zJp=fG>WA=9eMVP5hL6=x;6GA7g*y+L^%U^S_4T0h@A7r`)mPwGsIS5!^&b2}^>uir zz5)NG`X=0Y$gIBwzxqJ)`8|Nw)py{RsPDpC>Ld8^+RqqXADi{`;b*BIz>B|{K862M zeFh&tV)_yMYV{NN_)*j6@LSXu@bEFy&)|2due?>hA2RhW{9*MTyii|*=jt2q=y9_i zAO5=f7JRI}4gWxW2cBy^A$-x>%PPUp z`Z0V9^*OwF(#)U2w^u)dx1KV+^LF{V@1ov?N9wEaKz$7!KW*mM;fJXA;mO}k--ORm z--i3om_C3<>O*+&gz3BR)71Cik>V6o&uTVdP`_G#7Wbjn|7#==v`U(6t z^;5X}qUj6xJ?foz%GW(rUxEKseHHG$Z039LzpJmqc}2|QGv!?#jjz+?3@ z_>Sr;@8W(?@51*~@4=man*FK44^rQNd+L4o9Q7@@uf7dGL45}vs1M<>`UoDW@4+up z--jpa6ZkdiQ}|f@5Pq}z5!`vr?B^K%C-pfzS3iY6sD1`7UN`fdS^2s@q27hp-!Od@ z{-XLCJX2qXzoFiTJB69wgwIvqhP(eVeE|QMeqJBKTk5-TSA7qjtB>I;sZZeEn`S)& z_?qg6@Rs@v{yFtyxc6@}e**W_PvP!crZ3=MRqwo4zV2i76?j{H6&}8A=6mq{)z{(f zJEm{IL-kF%`WF0X^#MFp-+}kkcj587W<3%7JoPa=pP9Z7zexQ69=~V$6n>@p44%z3 z{Rn=e`U$*HpTqA^U%PFK2om z{J_u-i43f{MfwvrwZRweGOjx+w^t#zUqCr zulY^*0qWcEK=T9m!RkYJ_?B5um#)4CFVx5IAE-~@q5l5G0DhGEA>4b%tS5u-y_UH@ z$ME>prRVMc1nymH`YF8rJJT2NhfgxS^FjH#`|2z37t~kbE%hG!N%eJjpuPcrReckl zsBgiiraXfQRZ+_!{anc%*&=Ur+r69;?sc z8>uhgh58x%qEpTKD<78chg`i2KUcj6cZTMEtHC$Xem3BqdLO=}`WC#dz77AT*3*Hv z)Q9jr)JO18eGh(|p1*x~tUiICsy>CM>WA>{biO0_Sp68jpVptl3-wd@xmwQ*9_sbP z`A_+}U#i}PyEmA>4_<{|r@jWy{$=_){C4#|Jp8NaoA9yvHoUdL;`3e)1NaN-LwL5d z>AUcE)c4@^!%ZK<7x|-kz9jHqC({q$E2|&Eqnph6X7JCdAH(xUO+SHeseTG~mo)1s z;Jd1KJ}O`L`f{ePzzN9w@k?BWpH#7I!1Rm{X`W(K7`U0N* z!1Octrs^xsHuIiOm3jZyaf;uW^L61ns`ucniRo+bKz#%5zH53Pex&*qJXyi4zYXuH z@4%xkm_CI6QhfyXcQ<_xo~rM|!ylSHf#0P*g{Qwa=R1T?)Q{lqlcpcTUs0dK^O@8b;K52}{my*l>;4J-{Q?*6Z({l?d~NkLxU+}p>+mhq`|$dYOy7imQ+*pA-f7M^ zfOph~@a%chci}%)--8z)nLdV}tv-RftDE%?;8&?1!hPTL8T@wjV|cos=_l|<)KB5{ z$n*vLAL^a?%h$bimpR`G{3G>Mc=Dp@J^0dhn%Ap3d_137PXk_4--PFDn7#$yLVW;t zw=#VPzO(u+Jo=95BlvgK$8hI()A!+#`T@LtkJ+CTeu4T79=&Ax5&UZP6L_|OSx*lC zqxu4#u4Vce{4w>F1NK7${iehg3NntlTRvHB@| zysBA$0gu%?A1hyX=ZmJVz^_nWg@=2a-hxaD6L@;G>2vt* z>I-<9ne&~&4^>}T2=~Ktrg!1TsrTUZ|Cqi8KTmxF-dfG9--lnVz6FoAFnt?-hx!h@ z*w^$S{1NpLJUrI)J@{08A71>AIo|~Sq52f=zhe3!+`Y%#ZzFiTuvyO-zNY#d-dfl6 zQ}|};XYlANrgs)DU-zBVyYS+>rmw;~>TB@$6w}w?$Ex??r!QJ&tAHyF}pTJw&nSKEOhx#ErJH+%E{3G>aczCMmC-7zOHTTaH zp8mz`PXS+7y;CV)cjtA}SKwQyufkg&H|z1>JFBn5$LpKE0spT0CfwV>^euR#K7hxE znZ5(RKz$e9`l;z7__gX|c>IvrpFaFf^#geRhUruIW9l<_eQ~p%5&Tv46L?%VeGZ>* zWbTIoUVPQ`Gx+lAD~puxhafb)3tvyY2d|%D`Wk#|^$qy=MzcRYe0TLNc>NL6x8Vn< z@4(}Kn?8ikQ6IshPnh-h;3uo^!`%%{pTI9rpTfPJO+SQRseS}^jx_xkevA4XUO(CV z{ACKiNBs<*o??1u(eibFNWBY>)mPz9sIS32^>z4*>V0^s^*7;fsBgpTU9uLfEVh0_!H_|@ZcwAJ#F}l>O1hU`Vd~IkKq3CW_}O;f%-l?y2|tk zymFs;JxSrt!1P1-C)AJNvHCImQ|fcLceR;6g|DxE2G7(xiO1gUeF*QWkKnCa%zAq86V>Syp!z2laz`)%r7c&@$*zgv9`-a5&wrw$*h_u-lPCj2S&ZTMJUUj*=%)Q51t zXV%k&zoEVdPu0ipnfe6oooeO};LZc)^<)T-)o1WU)Q{oLX=eTezKr@QJXBx6S61&V zRle@I`U-qa^;LN5r)E7K{Ilxo@JxLJ-ca9!*H1U|Tkx&a2k=CF2mV#{UATLOnIFM- zQ6Ix2^?i7tegH4jr|?76XYk-mvz`&Wt9}9>tIy#lsxRRFS!VtWewO;m(&hUhRqw(t zRPVvP*vzlNuTtND$Lf9fZ`HTp&e>*u8-9oS4m?yJ!tYZb!E^OJ_~YvP@YXqIJqi4I z^(j14KZL)oegvOgI}8UjNtF9pTNiJ zb9m)J^L#Jh;U#AN48D~5%ChDA!M)7%E_@~R9z4C=^fmYz>KkzXBGdcu&#G_1bFIG( zZ>aCUBdtG#Z>2thdsmqC^x$7r--oAKe*)iCeF|?SX8sVqkNOe3(E7*lgVg8nSnHp{ zk5E5@dl#GaILno<`!VWWc&7DN;U}xF!Gm9!`E_`#-iNzan!XADh59x;*7^hZc{Xz>z}}%P(OwHdOjBL7u7q< zm#@2{?`KxvZ>X=rGkv}6!RM;4!<~!WdB0iSfG_lrdA(}F>*`zZ<60`n3{444Q@JxLQ-(7tMj}kL~1V32)1nysI`W$|Ydaqi(zV1~^%-jDO z+`H0v9qyiE<_GYdnw!m=6T!pPmzn1=Jm1K8AMVRD_G~b11pEdKVa8K^T-_ZO3Zuc{U7wen(U3g2Lz;D<53~tvyhNqt~^CxuO zC(cUceX#eN2e<35!|nanfaf}H0AEJ;a|E~ZV|b)FeR#gEIj;=9hJC)k?fe3sYR(Mq z%Dt7#`}1YZ_u;PYpBCKSpKZ9kKS%KT_2#_BaOXG1E31~*lfJv`yyuq-k3MPsUG6G8 z?Hc#s@$$xN@FF%|hlfiTZ^2W~cpKjOk?{Z?SB-bz(b>jBc&6jJpDpi$yN&t%mMT2m z+IS6~eYD%Wee>b#{K>q|x8Q~5bm87XW_|?Ewp`o1?!hDV34Eya4B>(L4DQQw_*m<# z)XV#0A6Mb79((XbM_-(`{u(^r!@NER@H5}ib%b9k58=t4W={6y@;Z0>qlc z!cWuu3~u++X_nXXGxZg?vzNJEP55Q%TX5UQ@L#L%!)-r-|5kktx4pMrdHsJ-UxVAe z1HW5+2)BI-AFCh2Z9juQrQX@TyiVKu@E6rL;kNI=UsoT)Z9j&8sD1*seRYTOI$h7) zZywzC0eofk9k}fW@HNz@aN8H~�flZQuB6dHtKK_u=+^Y6Ra+eGhJ*UnBSd>c?=~ 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

  • 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 VyHpG6)Y~irSG~JVKPL>&8H)G1i?Gmk(saiHh8r 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

    S=u; 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%0D5?wF+0qJyatAWgu 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

    Ka)tN6@HxPwg3;6FPuI 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 Ax&gY>(m^@G{~s_`^-4XI$Y(7&=yP@lz0jP{ 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�=#_VNur=;90fdyf+vZB$44>8|6LS= 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

    >FDRg*IkCM(_Ke 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