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}