• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Chik-Network / klvm_rs / 9969783315

17 Jul 2024 07:09AM UTC coverage: 91.834% (-2.2%) from 94.072%
9969783315

push

github

Chik-Network
update 0.2.5

1191 of 1284 new or added lines in 29 files covered. (92.76%)

66 existing lines in 11 files now uncovered.

4071 of 4433 relevant lines covered (91.83%)

3553.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.78
/src/serde/parse_atom.rs
1
use std::io::{Cursor, Read, Result, Seek, SeekFrom};
2

3
use crate::allocator::{Allocator, NodePtr};
4

5
use super::errors::{bad_encoding, internal_error};
6

7
const MAX_SINGLE_BYTE: u8 = 0x7f;
8

9
/// decode the length prefix for an atom, returning both the offset to the start
10
/// of the atom and the full length of the atom.
11
/// Atoms whose value fit in 7 bits don't have a length prefix, so those should
12
/// be handled specially and never passed to this function.
13
pub fn decode_size_with_offset<R: Read>(f: &mut R, initial_b: u8) -> Result<(u8, u64)> {
346✔
14
    debug_assert!((initial_b & 0x80) != 0);
346✔
15
    if (initial_b & 0x80) == 0 {
346✔
16
        return Err(internal_error());
×
17
    }
346✔
18

346✔
19
    let mut atom_start_offset = 0;
346✔
20
    let mut bit_mask: u8 = 0x80;
346✔
21
    let mut b = initial_b;
346✔
22
    while b & bit_mask != 0 {
732✔
23
        atom_start_offset += 1;
386✔
24
        b &= 0xff ^ bit_mask;
386✔
25
        bit_mask >>= 1;
386✔
26
    }
386✔
27
    let mut stack_allocation = [0_u8; 8];
346✔
28
    let size_blob = &mut stack_allocation[..atom_start_offset];
346✔
29
    size_blob[0] = b;
346✔
30
    if atom_start_offset > 1 {
346✔
31
        let remaining_buffer = &mut size_blob[1..];
14✔
32
        f.read_exact(remaining_buffer)?;
14✔
33
    }
332✔
34
    // need to convert size_blob to an int
35
    let mut atom_size: u64 = 0;
343✔
36
    if size_blob.len() > 6 {
343✔
37
        return Err(bad_encoding());
1✔
38
    }
342✔
39
    for b in size_blob {
703✔
40
        atom_size <<= 8;
361✔
41
        atom_size += *b as u64;
361✔
42
    }
361✔
43
    if atom_size >= 0x400000000 {
342✔
44
        return Err(bad_encoding());
1✔
45
    }
341✔
46
    Ok((atom_start_offset as u8, atom_size))
341✔
47
}
346✔
48

49
pub fn decode_size<R: Read>(f: &mut R, initial_b: u8) -> Result<u64> {
325✔
50
    decode_size_with_offset(f, initial_b).map(|v| v.1)
325✔
51
}
325✔
52

53
/// parse an atom from the stream and return a pointer to it
54
/// the first byte has already been read
55

56
fn parse_atom_ptr<'a>(f: &'a mut Cursor<&[u8]>, first_byte: u8) -> Result<&'a [u8]> {
661✔
57
    let blob = if first_byte <= MAX_SINGLE_BYTE {
661✔
58
        let pos = f.position() as usize;
344✔
59
        &f.get_ref()[pos - 1..pos]
344✔
60
    } else {
61
        let blob_size = decode_size(f, first_byte)?;
317✔
62
        let pos = f.position() as usize;
316✔
63
        if f.get_ref().len() < pos + blob_size as usize {
316✔
UNCOV
64
            return Err(bad_encoding());
×
65
        }
316✔
66
        f.seek(SeekFrom::Current(blob_size as i64))?;
316✔
67
        &f.get_ref()[pos..(pos + blob_size as usize)]
316✔
68
    };
69
    Ok(blob)
660✔
70
}
661✔
71

72
/// parse an atom from the stream into the allocator
73
/// At this point, the first byte has already been read to ensure it's
74
/// not a special code like `CONS_BOX_MARKER` = 0xff, so it must be
75
/// passed in too
76

77
pub fn parse_atom(
8,841✔
78
    allocator: &mut Allocator,
8,841✔
79
    first_byte: u8,
8,841✔
80
    f: &mut Cursor<&[u8]>,
8,841✔
81
) -> Result<NodePtr> {
8,841✔
82
    if first_byte == 0x01 {
8,841✔
83
        Ok(allocator.one())
8,036✔
84
    } else if first_byte == 0x80 {
805✔
85
        Ok(allocator.null())
156✔
86
    } else {
87
        let blob = parse_atom_ptr(f, first_byte)?;
649✔
88
        Ok(allocator.new_atom(blob)?)
648✔
89
    }
90
}
8,841✔
91

92
/// parse an atom from the stream and return a pointer to it
93

94
pub fn parse_path<'a>(f: &'a mut Cursor<&[u8]>) -> Result<&'a [u8]> {
12✔
95
    let mut buf1: [u8; 1] = [0];
12✔
96
    f.read_exact(&mut buf1)?;
12✔
97
    parse_atom_ptr(f, buf1[0])
12✔
98
}
12✔
99

100
#[cfg(test)]
101
use std::io::ErrorKind;
102

103
#[cfg(test)]
104
use hex;
105

106
#[cfg(test)]
107
use super::write_atom::write_atom;
108

109
#[test]
110
fn test_decode_size() {
1✔
111
    // single-byte length prefix
1✔
112
    let mut buffer = Cursor::new(&[]);
1✔
113
    assert_eq!(
1✔
114
        decode_size_with_offset(&mut buffer, 0x80 | 0x20).unwrap(),
1✔
115
        (1, 0x20)
1✔
116
    );
1✔
117

118
    // two-byte length prefix
119
    let first = 0b11001111;
1✔
120
    let mut buffer = Cursor::new(&[0xaa]);
1✔
121
    assert_eq!(
1✔
122
        decode_size_with_offset(&mut buffer, first).unwrap(),
1✔
123
        (2, 0xfaa)
1✔
124
    );
1✔
125
}
1✔
126

127
#[test]
128
fn test_large_decode_size() {
1✔
129
    // this is an atom length-prefix 0xffffffffffff, or (2^48 - 1).
1✔
130
    // We don't support atoms this large and we should fail before attempting to
1✔
131
    // allocate this much memory
1✔
132
    let first = 0b11111110;
1✔
133
    let mut buffer = Cursor::new(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
1✔
134
    let ret = decode_size_with_offset(&mut buffer, first);
1✔
135
    let e = ret.unwrap_err();
1✔
136
    assert_eq!(e.kind(), bad_encoding().kind());
1✔
137
    assert_eq!(e.to_string(), "bad encoding");
1✔
138

139
    // this is still too large
140
    let first = 0b11111100;
1✔
141
    let mut buffer = Cursor::new(&[0x4, 0, 0, 0, 0]);
1✔
142
    let ret = decode_size_with_offset(&mut buffer, first);
1✔
143
    let e = ret.unwrap_err();
1✔
144
    assert_eq!(e.kind(), bad_encoding().kind());
1✔
145
    assert_eq!(e.to_string(), "bad encoding");
1✔
146

147
    // But this is *just* within what we support
148
    // Still a very large blob, probably enough for a DoS attack
149
    let first = 0b11111100;
1✔
150
    let mut buffer = Cursor::new(&[0x3, 0xff, 0xff, 0xff, 0xff]);
1✔
151
    assert_eq!(
1✔
152
        decode_size_with_offset(&mut buffer, first).unwrap(),
1✔
153
        (6, 0x3ffffffff)
1✔
154
    );
1✔
155

156
    // this ensures a fuzzer-found bug doesn't reoccur
157
    let mut buffer = Cursor::new(&[0xff, 0xfe]);
1✔
158
    let ret = decode_size_with_offset(&mut buffer, first);
1✔
159
    let e = ret.unwrap_err();
1✔
160
    assert_eq!(e.kind(), ErrorKind::UnexpectedEof);
1✔
161
    assert_eq!(e.to_string(), "failed to fill whole buffer");
1✔
162
}
1✔
163

164
#[test]
165
fn test_truncated_decode_size() {
1✔
166
    // the stream is truncated
1✔
167
    let first = 0b11111100;
1✔
168
    let mut cursor = Cursor::new(&[0x4, 0, 0, 0]);
1✔
169
    let ret = decode_size_with_offset(&mut cursor, first);
1✔
170
    let e = ret.unwrap_err();
1✔
171
    assert_eq!(e.kind(), ErrorKind::UnexpectedEof);
1✔
172
}
1✔
173

174
#[cfg(test)]
175
fn check_parse_atom(blob: &[u8], expected_atom: &[u8]) {
130✔
176
    let mut cursor = Cursor::<&[u8]>::new(&blob);
130✔
177
    let mut first: [u8; 1] = [0];
130✔
178
    cursor.read(&mut first).unwrap();
130✔
179
    let first = first[0];
130✔
180

130✔
181
    let mut allocator = Allocator::new();
130✔
182
    let atom_node = parse_atom(&mut allocator, first, &mut cursor).unwrap();
130✔
183

130✔
184
    let atom_ptr = allocator.atom(atom_node);
130✔
185

130✔
186
    assert_eq!(expected_atom, atom_ptr);
130✔
187
}
130✔
188

189
#[cfg(test)]
190
fn check_parse_atom_str(blob_hex: &str, expected_atom_hex: &str) {
2✔
191
    let blob = hex::decode(blob_hex).unwrap();
2✔
192
    let expected_atom: &[u8] = &hex::decode(expected_atom_hex).unwrap();
2✔
193
    check_parse_atom(&blob, &expected_atom);
2✔
194
}
2✔
195

196
#[test]
197
fn test_parse_atom() {
1✔
198
    check_parse_atom_str("80", "");
1✔
199
    // try "00", "01", "02", ..., "7f"
200
    for idx in 0..128 {
129✔
201
        check_parse_atom(&[idx], &[idx]);
128✔
202
    }
128✔
203

204
    // check a short atom
205
    check_parse_atom_str("83666f6f", "666f6f");
1✔
206

1✔
207
    // check long atoms near boundary conditions
1✔
208
    let n = 3;
1✔
209
    let base_lengths = [0, 0x40 - n, 0x2000 - n, 0x100000 - n, 0x08000000 - n];
1✔
210
    let mut atom_vec = vec![];
1✔
211
    for base_length in base_lengths.iter() {
5✔
212
        for size_offset in 0..6 {
35✔
213
            let size = base_length + size_offset;
30✔
214
            atom_vec.resize(size, 0x66);
30✔
215
            let mut buffer: Vec<u8> = vec![];
30✔
216
            let mut cursor = Cursor::new(&mut buffer);
30✔
217
            write_atom(&mut cursor, &atom_vec).unwrap();
30✔
218
        }
30✔
219
    }
220
}
1✔
221

222
#[test]
223
fn test_truncated_parse_atom() {
1✔
224
    // the stream is truncated
1✔
225
    let first = 0b11111100;
1✔
226
    let mut cursor = Cursor::<&[u8]>::new(&[0x4, 0, 0, 0]);
1✔
227
    let mut allocator = Allocator::new();
1✔
228
    let ret = parse_atom(&mut allocator, first, &mut cursor);
1✔
229
    let err = ret.unwrap_err();
1✔
230
    assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
1✔
231
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc