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