Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/executor/host/zstd.rs
Line
Count
Source (jump to first uncovered line)
1
// Smoldot
2
// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
15
// You should have received a copy of the GNU General Public License
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
use alloc::{borrow::Cow, vec::Vec};
19
20
mod tests;
21
22
/// A runtime blob beginning with this prefix should first be decompressed with `zstandard`
23
/// compression.
24
///
25
/// This differs from the Wasm magic bytes, so real Wasm blobs will not have this prefix.
26
pub(super) const ZSTD_PREFIX: [u8; 8] = [82, 188, 83, 118, 70, 219, 142, 5];
27
28
/// If the given blob starts with [`ZSTD_PREFIX`], decompresses it. Otherwise, passes it through.
29
///
30
/// The output data shall not be larger than `max_allowed`, to avoid potential zip bombs.
31
155
pub(super) fn zstd_decode_if_necessary(
32
155
    data: &[u8],
33
155
    max_allowed: usize,
34
155
) -> Result<Cow<[u8]>, Error> {
35
155
    if data.starts_with(&ZSTD_PREFIX) {
36
98
        Ok(Cow::Owned(zstd_decode(
37
98
            &data[ZSTD_PREFIX.len()..],
38
98
            max_allowed,
39
98
        )
?2
))
40
57
    } else if data.len() > max_allowed {
41
0
        Err(Error::TooLarge)
42
    } else {
43
57
        Ok(Cow::Borrowed(data))
44
    }
45
155
}
_RNvNtNtNtCsN16ciHI6Qf_7smoldot8executor4host4zstd24zstd_decode_if_necessary
Line
Count
Source
31
69
pub(super) fn zstd_decode_if_necessary(
32
69
    data: &[u8],
33
69
    max_allowed: usize,
34
69
) -> Result<Cow<[u8]>, Error> {
35
69
    if data.starts_with(&ZSTD_PREFIX) {
36
12
        Ok(Cow::Owned(zstd_decode(
37
12
            &data[ZSTD_PREFIX.len()..],
38
12
            max_allowed,
39
12
        )
?2
))
40
57
    } else if data.len() > max_allowed {
41
0
        Err(Error::TooLarge)
42
    } else {
43
57
        Ok(Cow::Borrowed(data))
44
    }
45
69
}
_RNvNtNtNtCseuYC0Zibziv_7smoldot8executor4host4zstd24zstd_decode_if_necessary
Line
Count
Source
31
86
pub(super) fn zstd_decode_if_necessary(
32
86
    data: &[u8],
33
86
    max_allowed: usize,
34
86
) -> Result<Cow<[u8]>, Error> {
35
86
    if data.starts_with(&ZSTD_PREFIX) {
36
86
        Ok(Cow::Owned(zstd_decode(
37
86
            &data[ZSTD_PREFIX.len()..],
38
86
            max_allowed,
39
86
        )
?0
))
40
0
    } else if data.len() > max_allowed {
41
0
        Err(Error::TooLarge)
42
    } else {
43
0
        Ok(Cow::Borrowed(data))
44
    }
45
86
}
46
47
/// Decompresses the given blob of zstd-compressed data.
48
///
49
/// The output data shall not be larger than `max_allowed`, to avoid potential zip bombs.
50
101
fn zstd_decode(mut data: &[u8], max_allowed: usize) -> Result<Vec<u8>, Error> {
51
101
    let mut decoder = ruzstd::frame_decoder::FrameDecoder::new();
52
101
    decoder.init(&mut data).map_err(|_| 
Error::InvalidZstd3
)
?3
;
_RNCNvNtNtNtCsN16ciHI6Qf_7smoldot8executor4host4zstd11zstd_decode0B9_
Line
Count
Source
52
3
    decoder.init(&mut data).map_err(|_| Error::InvalidZstd)?;
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot8executor4host4zstd11zstd_decode0B9_
53
54
98
    match decoder.decode_blocks(
55
98
        &mut data,
56
98
        ruzstd::frame_decoder::BlockDecodingStrategy::UptoBytes(max_allowed),
57
98
    ) {
58
97
        Ok(true) => {}
59
1
        Ok(false) => return Err(Error::TooLarge),
60
0
        Err(_) => return Err(Error::InvalidZstd),
61
    }
62
97
    debug_assert!(decoder.is_finished());
63
64
    // When the decoding is finished, `Some` is always guaranteed to be returned.
65
97
    let out_buf = decoder.collect().unwrap();
66
97
    debug_assert!(out_buf.len() <= max_allowed);
67
97
    Ok(out_buf)
68
101
}
_RNvNtNtNtCsN16ciHI6Qf_7smoldot8executor4host4zstd11zstd_decode
Line
Count
Source
50
15
fn zstd_decode(mut data: &[u8], max_allowed: usize) -> Result<Vec<u8>, Error> {
51
15
    let mut decoder = ruzstd::frame_decoder::FrameDecoder::new();
52
15
    decoder.init(&mut data).map_err(|_| Error::InvalidZstd)
?3
;
53
54
12
    match decoder.decode_blocks(
55
12
        &mut data,
56
12
        ruzstd::frame_decoder::BlockDecodingStrategy::UptoBytes(max_allowed),
57
12
    ) {
58
11
        Ok(true) => {}
59
1
        Ok(false) => return Err(Error::TooLarge),
60
0
        Err(_) => return Err(Error::InvalidZstd),
61
    }
62
11
    debug_assert!(decoder.is_finished());
63
64
    // When the decoding is finished, `Some` is always guaranteed to be returned.
65
11
    let out_buf = decoder.collect().unwrap();
66
11
    debug_assert!(out_buf.len() <= max_allowed);
67
11
    Ok(out_buf)
68
15
}
_RNvNtNtNtCseuYC0Zibziv_7smoldot8executor4host4zstd11zstd_decode
Line
Count
Source
50
86
fn zstd_decode(mut data: &[u8], max_allowed: usize) -> Result<Vec<u8>, Error> {
51
86
    let mut decoder = ruzstd::frame_decoder::FrameDecoder::new();
52
86
    decoder.init(&mut data).map_err(|_| Error::InvalidZstd)
?0
;
53
54
86
    match decoder.decode_blocks(
55
86
        &mut data,
56
86
        ruzstd::frame_decoder::BlockDecodingStrategy::UptoBytes(max_allowed),
57
86
    ) {
58
86
        Ok(true) => {}
59
0
        Ok(false) => return Err(Error::TooLarge),
60
0
        Err(_) => return Err(Error::InvalidZstd),
61
    }
62
86
    debug_assert!(decoder.is_finished());
63
64
    // When the decoding is finished, `Some` is always guaranteed to be returned.
65
86
    let out_buf = decoder.collect().unwrap();
66
86
    debug_assert!(out_buf.len() <= max_allowed);
67
86
    Ok(out_buf)
68
86
}
69
70
/// Error possibly returned when decoding a zstd-compressed Wasm blob.
71
0
#[derive(Debug, derive_more::Display, Clone)]
Unexecuted instantiation: _RNvXs_NtNtNtCsN16ciHI6Qf_7smoldot8executor4host4zstdNtB4_5ErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXs_NtNtNtCseuYC0Zibziv_7smoldot8executor4host4zstdNtB4_5ErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
72
pub enum Error {
73
    /// The data is zstandard-compressed, but the data is in an invalid format.
74
    InvalidZstd,
75
    /// The size of the code exceeds the maximum allowed length.
76
    TooLarge,
77
}