Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/rust/htp/src/c_api/bstr.rs
Line
Count
Source
1
use crate::bstr::Bstr;
2
use core::cmp::Ordering;
3
use std::{boxed::Box, ffi::CStr};
4
5
/// Allocate a zero-length bstring, reserving space for at least size bytes.
6
0
fn bstr_alloc(len: libc::size_t) -> *mut Bstr {
7
0
    let b = Bstr::with_capacity(len);
8
0
    let boxed = Box::new(b);
9
0
    Box::into_raw(boxed)
10
0
}
11
12
/// Deallocate the supplied bstring instance. Allows NULL on input.
13
/// # Safety
14
/// This function is unsafe because improper use may lead to memory problems. For example, a double-free may occur if the function is called twice on the same raw pointer.
15
#[no_mangle]
16
0
pub unsafe extern "C" fn bstr_free(b: *mut Bstr) {
17
0
    if !b.is_null() {
18
0
        drop(Box::from_raw(b));
19
0
    }
20
0
}
21
22
/// Return the length of the string
23
/// # Safety
24
/// x must be properly intialized: not NULL, dangling, or misaligned
25
#[no_mangle]
26
1.16M
pub unsafe extern "C" fn bstr_len(x: *const Bstr) -> libc::size_t {
27
1.16M
    (*x).len()
28
1.16M
}
29
30
/// Return a pointer to the bstr payload
31
/// # Safety
32
/// x must be properly intialized: not NULL, dangling, or misaligned
33
#[no_mangle]
34
2.39M
pub unsafe extern "C" fn bstr_ptr(x: *const Bstr) -> *mut libc::c_uchar {
35
2.39M
    (*x).as_ptr() as *mut u8
36
2.39M
}
37
38
/// Return the capacity of the string
39
/// # Safety
40
/// x must be properly intialized: not NULL, dangling, or misaligned
41
#[no_mangle]
42
2.53k
pub unsafe extern "C" fn bstr_size(x: *const Bstr) -> libc::size_t {
43
2.53k
    (*x).capacity()
44
2.53k
}
45
46
/// Case-sensitive comparison of a bstring and a NUL-terminated string.
47
/// returns -1 if b is less than c
48
///          0 if b is equal to c
49
///          1 if b is greater than c
50
/// # Safety
51
/// b and c must be properly intialized: not NULL, dangling, or misaligned.
52
/// c must point to memory that contains a valid nul terminator byte at the end of the string
53
#[no_mangle]
54
3.72k
pub unsafe extern "C" fn bstr_cmp_c(b: *const Bstr, c: *const libc::c_char) -> libc::c_int {
55
3.72k
    let cs = CStr::from_ptr(c);
56
3.72k
    match (*b).cmp_slice(cs.to_bytes()) {
57
1.63k
        Ordering::Less => -1,
58
1.51k
        Ordering::Equal => 0,
59
581
        Ordering::Greater => 1,
60
    }
61
3.72k
}
62
63
/// Case-indensitive comparison of a bstring and a NUL-terminated string.
64
/// returns -1 if b is less than c
65
///          0 if b is equal to c
66
///          1 if b is greater than c
67
/// # Safety
68
/// b and c must be properly intialized: not NULL, dangling, or misaligned.
69
/// c must point to memory that contains a valid nul terminator byte at the end of the string
70
#[no_mangle]
71
2.21k
pub unsafe extern "C" fn bstr_cmp_c_nocase(b: *const Bstr, c: *const libc::c_char) -> bool {
72
2.21k
    let cs = CStr::from_ptr(c);
73
2.21k
    (*b).cmp_nocase(cs.to_bytes())
74
2.21k
}
75
76
/// Create a new bstring by copying the provided NUL-terminated string
77
/// # Safety
78
/// cstr must be properly intialized: not NULL, dangling, or misaligned.
79
/// cstr must point to memory that contains a valid nul terminator byte at the end of the string
80
#[no_mangle]
81
0
pub unsafe extern "C" fn bstr_dup_c(cstr: *const libc::c_char) -> *mut Bstr {
82
0
    let cs = CStr::from_ptr(cstr).to_bytes();
83
0
    let new = bstr_alloc(cs.len());
84
0
    (*new).add(cs);
85
0
    new
86
0
}
87
88
/// Create a new NUL-terminated string out of the provided bstring. If NUL bytes
89
/// are contained in the bstring, each will be replaced with "\0" (two characters).
90
/// The caller is responsible to keep track of the allocated memory area and free
91
/// it once it is no longer needed.
92
/// returns The newly created NUL-terminated string, or NULL in case of memory
93
///         allocation failure.
94
/// # Safety
95
/// b must be properly intialized and not dangling nor misaligned.
96
#[no_mangle]
97
0
pub unsafe extern "C" fn bstr_util_strdup_to_c(b: *const Bstr) -> *mut libc::c_char {
98
0
    if b.is_null() {
99
0
        return std::ptr::null_mut();
100
0
    }
101
0
    let src = std::slice::from_raw_parts(bstr_ptr(b), bstr_len(b));
102
103
    // Since the memory returned here is just a char* and the caller will
104
    // free() it we have to use malloc() here.
105
    // So we allocate enough space for doubled NULL bytes plus the trailing NULL.
106
0
    let mut null_count = 1;
107
0
    for byte in src {
108
0
        if *byte == 0 {
109
0
            null_count += 1;
110
0
        }
111
    }
112
0
    let newlen = bstr_len(b) + null_count;
113
0
    let mem = libc::malloc(newlen) as *mut libc::c_char;
114
0
    if mem.is_null() {
115
0
        return std::ptr::null_mut();
116
0
    }
117
0
    let dst: &mut [libc::c_char] = std::slice::from_raw_parts_mut(mem, newlen);
118
0
    let mut dst_idx = 0;
119
0
    for byte in src {
120
0
        if *byte == 0 {
121
0
            dst[dst_idx] = '\\' as libc::c_char;
122
0
            dst_idx += 1;
123
0
            dst[dst_idx] = '0' as libc::c_char;
124
0
        } else {
125
0
            dst[dst_idx] = *byte as libc::c_char;
126
0
        }
127
0
        dst_idx += 1;
128
    }
129
0
    dst[dst_idx] = 0;
130
131
0
    mem
132
0
}
133
134
#[cfg(test)]
135
mod test {
136
    use super::*;
137
    use std::ffi::CString;
138
139
    macro_rules! cstr {
140
        ( $x:expr ) => {{
141
            CString::new($x).unwrap()
142
        }};
143
    }
144
145
    #[test]
146
    fn Bstr_Alloc() {
147
        unsafe {
148
            let p1 = bstr_alloc(10);
149
            assert_eq!(10, bstr_size(p1));
150
            assert_eq!(0, bstr_len(p1));
151
            bstr_free(p1);
152
        }
153
    }
154
155
    #[test]
156
    fn Bstr_DupC() {
157
        unsafe {
158
            let p1 = bstr_dup_c(cstr!("arfarf").as_ptr());
159
160
            assert_eq!(6, bstr_size(p1));
161
            assert_eq!(6, bstr_len(p1));
162
            assert_eq!(
163
                0,
164
                libc::memcmp(
165
                    cstr!("arfarf").as_ptr() as *const core::ffi::c_void,
166
                    bstr_ptr(p1) as *const core::ffi::c_void,
167
                    6
168
                )
169
            );
170
            bstr_free(p1);
171
        }
172
    }
173
174
    #[test]
175
    fn Bstr_UtilDupToC() {
176
        unsafe {
177
            let s = Bstr::from(b"ABCDEFGHIJKL\x00NOPQRST" as &[u8]);
178
            let c = bstr_util_strdup_to_c(&s);
179
            let e = CString::new("ABCDEFGHIJKL\\0NOPQRST").unwrap();
180
            assert_eq!(0, libc::strcmp(e.as_ptr(), c));
181
182
            libc::free(c as *mut core::ffi::c_void);
183
        }
184
    }
185
186
    #[test]
187
    fn Bstr_CmpC() {
188
        unsafe {
189
            let p1 = Bstr::from("arfarf");
190
            assert_eq!(0, bstr_cmp_c(&p1, cstr!("arfarf").as_ptr()));
191
            assert_eq!(-1, bstr_cmp_c(&p1, cstr!("arfarf2").as_ptr()));
192
            assert_eq!(1, bstr_cmp_c(&p1, cstr!("arf").as_ptr()));
193
            assert_eq!(-1, bstr_cmp_c(&p1, cstr!("not equal").as_ptr()));
194
        }
195
    }
196
}