1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
//! ktest-parser is a utility to parse `.ktest` binaries which is the output of KLEE, //! into a Rust friendly struct instead. //! //! ## KTest File Format Description //! The KTest binary is structured as follows, //! 1. A header //! 2. KLEE arguments //! 3. Symbolic arguments //! 4. KTest objects. //! //! The following sections describes the detailed structure. Each new section starts at //! byte 0 here, but since they follow each other the arguments start at byte 8 where //! the header left off. But it is easier to describe the structure this way. //! //! ### Header //! The header describes the magic number which is either "KTEST" or "BOUT/n". Then followed //! by a version of the file format. //! //! | BYTE | NAME | DESCRIPTION | LENGTH | //! |------|---------|-----------------------------|---------| //! | 0..5 | HDR | File format (default: KTEST)| 4 bytes | //! | 5..8 | VERSION | File format version | 4 bytes | //! //! ## Arguments //! The arguments section describes the number of arguments and then a repeated section of //! arguments where each argument is first described by a size and then its content of size length. //! //! ### Information //! | BYTE | NAME | DESCRIPTION | LENGTH | //! |------|--------|-----------------------------|---------| //! | 0..4 | NUMARGS| Number of arguments | 4 bytes | //! //! ### Argument //! This is repeated for (NUMARGS) times. //! //! | BYTE | NAME | DESCRIPTION | LENGTH | //! |-----------|--------|------------------|--------------| //! | 0..4 | SIZE | Size of argument | 4 bytes | //! | 4..(SIZE) | ARG | An argument | (SIZE) bytes | //! //! ## Symbolic arguments //! Describes symbolic arguments. //! //! | BYTE | NAME | DESCRIPTION | LENGTH | //! |------|---------|-------------|---------| //! | 0..4 | ARGVS | none | 4 bytes | //! | 4..8 | ARGVLEN | none | 4 bytes | //! //! ## Objects //! Like the arguments section, the first item is the number of objects. Then followed by //! a repeated section of objects where each object is described by a size and then its content //! of size length. //! ### Information //! | BYTE | NAME | DESCRIPTION | LENGTH | //! |------|-----------|-------------------|---------| //! | 0..4 | NUMOBJECTS| Number of objects | 4 bytes | //! //! ### Object //! This is repeated for (NUMOBJECTS) times. //! //! | BYTE | NAME | DESCRIPTION | LENGTH | //! |-----------|--------|----------------|--------------| //! | 0..4 | SIZE | Size of object | 4 bytes | //! | 4..(SIZE) | OBJECT | An object | (SIZE) bytes | extern crate nom; use anyhow::{anyhow, Result}; use nom::{ branch::alt, bytes::complete::{tag, take}, combinator::map, multi::{count, many0}, number::complete::be_u32, sequence::tuple, IResult, }; use serde::{Deserialize, Serialize}; type KTestTuple = (u32, Vec<String>, u32, u32, u32, Vec<KTestObject>); /// Contains information about the generated test vector on a symbolic object. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct KTestObject { /// The name given to the symbolic object when calling `klee_make_symbolic` pub name: String, /// The size of the generated test vector pub num_bytes: u32, /// The test vector data generated by KLEE pub bytes: Vec<u8>, } /// A representation of the KTest file format. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct KTest { /// KTest file format version pub version: u32, /// KLEE arguments pub args: Vec<String>, /// Symbolic arguments pub sym_argvs: u32, pub sym_argv_len: u32, /// The number of KTestObjects pub num_objects: u32, /// A list of the KTestObjects pub objects: Vec<KTestObject>, } /// Parses a .ktest file and returns a KTest. Will return an error if any /// parts of the binary is illegal. pub fn parse_ktest(input: &[u8]) -> Result<KTest> { let (_, (version, args, sym_argvs, sym_argv_len, num_objects, objects)) = match parse_ktest_binary(input) { Ok(res) => res, Err(e) => { return Err(anyhow!("Could not parse binary {:}", e)); } }; Ok(KTest { version, args, sym_argvs, sym_argv_len, num_objects, objects, }) } fn parse_ktest_binary(input: &[u8]) -> IResult<&[u8], KTestTuple> { let (input, (_magic, version, args)) = tuple((magic_number, extract_be_u32, extract_arguments))(input)?; let (input, (sym_argvs, sym_argv_len)) = if version > 2 { extract_sym_args(input)? } else { (input, (0, 0)) }; let (input, objects) = extract_objects(input)?; Ok(( input, ( version, args, sym_argvs, sym_argv_len, objects.len() as u32, objects, ), )) } /// Parses the KTest magic number. fn magic_number(input: &[u8]) -> IResult<&[u8], &[u8]> { alt((tag(b"KTEST"), tag(b"BOUT\n")))(input) } fn extract_be_u32(input: &[u8]) -> IResult<&[u8], u32> { be_u32(input) } // Parses the arguments in the KTest file fn extract_arguments(input: &[u8]) -> IResult<&[u8], Vec<String>> { let (input, num) = extract_be_u32(input)?; count(extract_argument, num as usize)(input) } fn extract_argument(input: &[u8]) -> IResult<&[u8], String> { let (input, size) = extract_be_u32(input)?; map(take(size), |arg: &[u8]| { String::from_utf8(arg.to_owned()).unwrap() })(input) } fn extract_sym_args(input: &[u8]) -> IResult<&[u8], (u32, u32)> { tuple((extract_be_u32, extract_be_u32))(input) } fn extract_objects(input: &[u8]) -> IResult<&[u8], Vec<KTestObject>> { let (input, _num) = extract_be_u32(input)?; many0(extract_object)(input) } // Does not work yet fn extract_object(input: &[u8]) -> IResult<&[u8], KTestObject> { let (input, size_name) = extract_be_u32(input)?; let (input, name) = map(take(size_name), |name: &[u8]| { String::from_utf8(name.to_owned()).unwrap() })(input)?; let (input, size_bytes) = extract_be_u32(input)?; let (input, bytes) = take(size_bytes)(input)?; Ok(( input, KTestObject { name: name, num_bytes: size_bytes, bytes: bytes.to_vec(), }, )) } #[cfg(test)] mod parser_tests { use super::*; #[test] fn invalid_magic_number() { let magic = [0, 0, 0, 0, 0]; let res = magic_number(&magic); assert_eq!(res.is_err(), true); } #[test] fn valid_magic_numbers() { let ktest = b"KTEST"; let bout = b"BOUT\n"; assert_eq!(magic_number(ktest).is_ok(), true); assert_eq!(magic_number(bout).is_ok(), true); } }