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);
    }
}