Skip to content

Commit 276bce1

Browse files
committed
rust: remove CStrMut
Its only uses are in rust_util_uint8_to_hex and rust_base58_encode_check (which is only used in tests in test_keystore_functional.c). We can simplify the code a lot by removing the rather complicated CStrMut struct and simply use BytesMut instead. The unit test for uint8_to_hex is simplified a bit (the strange -1 offsets etc. didn't really test anything useful).
1 parent 7cff075 commit 276bce1

File tree

3 files changed

+28
-197
lines changed

3 files changed

+28
-197
lines changed

src/rust/bitbox02-rust-c/src/util.rs

Lines changed: 26 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use core::fmt::Write;
16-
use util::c_types::{c_char, c_uchar};
15+
use util::c_types::c_uchar;
1716

1817
/// Zero a buffer using volatile writes. Accepts null-ptr and 0-length buffers and does nothing.
1918
///
@@ -31,19 +30,17 @@ pub extern "C" fn rust_util_zero(mut dst: BytesMut) {
3130
/// * `buf` - bytes to convert to hex.
3231
/// * `out` - hex will be written here. out len must be at least 2*buf.len+1.
3332
#[no_mangle]
34-
pub extern "C" fn rust_util_uint8_to_hex(buf: Bytes, mut out: CStrMut) {
35-
let min_len = buf.len * 2;
36-
match out.write(min_len, |out| {
37-
// Avoid .unwrap() here until the following compiler regression is fixed:
38-
// https://github.com/rust-lang/rust/issues/83925
39-
match hex::encode_to_slice(&buf, out) {
40-
Ok(()) => {}
41-
Err(err) => panic!("{:?}", err),
42-
}
43-
}) {
33+
pub extern "C" fn rust_util_uint8_to_hex(buf: Bytes, mut out: BytesMut) {
34+
let bytes = buf.as_ref();
35+
let hexlen = bytes.len() * 2;
36+
// Avoid .unwrap() here until the following compiler regression is fixed:
37+
// https://github.com/rust-lang/rust/issues/83925
38+
match hex::encode_to_slice(bytes, &mut out.as_mut()[..hexlen]) {
4439
Ok(()) => {}
45-
Err(_) => panic!("couldn't write to buffer"),
40+
Err(err) => panic!("{:?}", err),
4641
}
42+
// Null terminator.
43+
out.as_mut()[hexlen] = 0;
4744
}
4845

4946
#[repr(C)]
@@ -100,99 +97,6 @@ impl AsMut<[u8]> for BytesMut {
10097
}
10198
}
10299

103-
/// CStrMut is a "growable" container which keeps track of some array allocated by C with a length
104-
/// and a capacity state. It always contains a null-terminated string. The string (excluding null
105-
/// terminator) can therefore be maximally `capacity-1` long.
106-
#[repr(C)]
107-
pub struct CStrMut {
108-
buf: *mut c_char,
109-
len: usize,
110-
cap: usize,
111-
}
112-
113-
impl CStrMut {
114-
/// Create a new growable string with capacity `cap`. Only allowed for non-null pointers with
115-
/// length or null pointers with 0 length due to limitation in `core::slice`. Unsafe because it
116-
/// will read until it finds a null character.
117-
pub unsafe fn new(buf: *mut c_char, cap: usize) -> Self {
118-
let mut len = 0;
119-
let mut buf = buf;
120-
if buf.is_null() {
121-
if cap != 0 {
122-
panic!("Null pointer can't have capacity");
123-
}
124-
buf = core::ptr::NonNull::dangling().as_ptr();
125-
} else {
126-
let mut b = buf;
127-
while b.read() != 0 {
128-
len += 1;
129-
b = b.offset(1);
130-
if len == cap {
131-
panic!("CStrMut not null terminated");
132-
}
133-
}
134-
}
135-
136-
CStrMut { buf, len, cap }
137-
}
138-
139-
/// Provide a mutable slice to an unused range of the buffer. The provided function `f` must
140-
/// fill the requested buffer with utf-8 valid characters and it must not write a null
141-
/// character in the buffer.
142-
///
143-
/// # Panics
144-
///
145-
/// This function returns an error in case the provided buffer contains NULL or non-valid utf-8
146-
/// characters after function `f` is applied. It will also return an error if more bytes are
147-
/// requested then are available.
148-
pub fn write<F>(&mut self, req: usize, f: F) -> Result<(), core::fmt::Error>
149-
where
150-
F: FnOnce(&mut [u8]),
151-
{
152-
// Must be room for requested amount of bytes and null terminator.
153-
if self.cap - self.len < req + 1 {
154-
// Not enough bytes left in buffer
155-
return Err(core::fmt::Error);
156-
}
157-
let len = self.len;
158-
let slice = unsafe { self.as_bytes_mut() };
159-
let slice = &mut slice[len..len + req + 1];
160-
let write_slice = &mut slice[0..req];
161-
f(write_slice);
162-
if write_slice.iter().any(|&c| c == 0) {
163-
// null terminated strings can't contain null
164-
return Err(core::fmt::Error);
165-
}
166-
if core::str::from_utf8(write_slice).is_err() {
167-
// strings must be valid utf-8
168-
return Err(core::fmt::Error);
169-
}
170-
slice[req] = 0;
171-
self.len += req;
172-
Ok(())
173-
}
174-
175-
/// Get slice of underlying byte array. Unsafe because you have to ensure that length is up to
176-
/// date and that there is a null character at `buf[len]`.
177-
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
178-
core::slice::from_raw_parts_mut(self.buf, self.cap)
179-
}
180-
}
181-
182-
impl AsRef<str> for CStrMut {
183-
fn as_ref(&self) -> &str {
184-
unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.buf, self.len)) }
185-
}
186-
}
187-
188-
impl core::fmt::Write for CStrMut {
189-
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
190-
self.write(s.len(), |buf| {
191-
buf.copy_from_slice(s.as_bytes());
192-
})
193-
}
194-
}
195-
196100
/// Convert buffer to slice
197101
///
198102
/// * `buf` - Must be a valid pointer to an array of bytes
@@ -211,29 +115,20 @@ pub unsafe extern "C" fn rust_util_bytes_mut(buf: *mut c_uchar, len: usize) -> B
211115
BytesMut { buf, len }
212116
}
213117

214-
/// Convert buffer to mutable str. The whole buffer is considered empty from start.
215-
///
216-
/// * `buf` - Must be a valid pointer to an array of bytes
217-
/// * `cap` - Length of buffer, `buf_ptr[cap-1]` must be a valid dereference
218-
#[no_mangle]
219-
pub unsafe extern "C" fn rust_util_cstr_mut(buf: *mut c_char, cap: usize) -> CStrMut {
220-
if !buf.is_null() {
221-
buf.write(0);
222-
}
223-
CStrMut::new(buf, cap)
224-
}
225-
226118
/// Base58Check-encode the input.
227119
///
228120
/// #Safety
229121
/// buf and out must not be NULL and point to valid memory areas.
230122
#[no_mangle]
231-
pub unsafe extern "C" fn rust_base58_encode_check(buf: Bytes, mut out: CStrMut) -> bool {
123+
pub unsafe extern "C" fn rust_base58_encode_check(buf: Bytes, mut out: BytesMut) -> bool {
232124
if buf.len == 0 {
233125
return false;
234126
}
235127
let encoded = bs58::encode(buf.as_ref()).with_check().into_string();
236-
write!(&mut out, "{}", encoded).is_ok()
128+
out.as_mut()[..encoded.len()].copy_from_slice(encoded.as_bytes());
129+
// Null-terminator.
130+
out.as_mut()[encoded.len()] = 0;
131+
true
237132
}
238133

239134
#[cfg(test)]
@@ -276,80 +171,18 @@ mod tests {
276171
}
277172

278173
#[test]
279-
fn test_cstr_mut() {
280-
let mut start = String::from("foo\0bar");
281-
let mut cstr_mut = unsafe { rust_util_cstr_mut(start.as_mut_ptr(), start.len()) };
282-
assert_eq!(cstr_mut.len, 0);
283-
assert_eq!(cstr_mut.as_ref(), "");
284-
cstr_mut.write(1, |buf| buf[0] = b'g').unwrap();
285-
assert_eq!(cstr_mut.as_ref(), "g");
286-
}
287-
288-
#[test]
289-
fn test_cstr_mut_new() {
290-
let mut start = String::from("foo\0bar");
291-
let mut cstr_mut = unsafe { CStrMut::new(start.as_mut_ptr(), start.len()) };
292-
assert_eq!(cstr_mut.len, 3);
293-
assert_eq!(cstr_mut.as_ref(), "foo");
294-
cstr_mut.write(1, |buf| buf[0] = b'g').unwrap();
295-
assert_eq!(cstr_mut.as_ref(), "foog");
296-
}
297-
298-
#[test]
299-
#[should_panic]
300-
fn test_invalid_cstr_mut() {
301-
let mut buf = [1, 2, 3];
302-
let cstr_mut = unsafe { CStrMut::new(buf.as_mut_ptr(), buf.len()) };
303-
// panics as there is no null terminator.
304-
cstr_mut.as_ref();
305-
}
306-
307-
#[test]
308-
fn test_invalid_cstr_mut_write_null() {
309-
let mut s = String::from("abc\0xxx");
310-
let mut cstr_mut = unsafe { CStrMut::new(s.as_mut_ptr(), s.len()) };
311-
assert!(cstr_mut.write(1, |buf| buf[0] = 0).is_err());
312-
}
313-
314-
#[test]
315-
fn test_invalid_cstr_mut_out_of_buffer() {
316-
let mut s = String::from("abc\0");
317-
let mut cstr_mut = unsafe { CStrMut::new(s.as_mut_ptr(), s.len()) };
318-
assert!(cstr_mut.write(1, |buf| buf[0] = b'd').is_err());
319-
}
320-
321-
#[test]
322-
fn test_cstr_mut_write() {
323-
let mut buf = vec![0; 9];
324-
let mut cstr_mut = unsafe { CStrMut::new(buf.as_mut_ptr(), buf.len()) };
325-
use std::fmt::Write;
326-
assert!(write!(&mut cstr_mut, "test").is_ok());
327-
assert!(buf.starts_with(b"test\0"));
328-
assert!(write!(&mut cstr_mut, " foo").is_ok());
329-
assert!(buf.starts_with(b"test foo\0"));
330-
}
331-
332-
#[test]
333-
fn test_cstr_mut_write_too_much() {
334-
let mut buf = vec![0; 9];
335-
let mut cstr_mut = unsafe { CStrMut::new(buf.as_mut_ptr(), buf.len()) };
336-
use std::fmt::Write;
337-
assert!(write!(&mut cstr_mut, "test foo ").is_err());
338-
}
339-
340-
#[test]
341-
fn u8_to_hexing() {
342-
let buf = [1u8, 2, 3, 14, 15, 255, 1];
343-
let mut string = String::from("\0xxxxxxxxxxxxx");
344-
rust_util_uint8_to_hex(rust_util_bytes(buf.as_ptr(), buf.len() - 1), unsafe {
345-
rust_util_cstr_mut(string.as_mut_ptr(), string.len() - 1)
174+
fn test_uint8_to_hex() {
175+
let buf = [1u8, 2, 3, 14, 15, 255];
176+
let mut string = String::from("xxxxxxxxxxxxx");
177+
rust_util_uint8_to_hex(rust_util_bytes(buf.as_ptr(), buf.len()), unsafe {
178+
rust_util_bytes_mut(string.as_mut_ptr(), string.len())
346179
});
347-
assert_eq!(string, "0102030e0fff\0x");
180+
assert_eq!(string, "0102030e0fff\0");
348181

349182
// Bigger buffer also works.
350183
let mut string = String::from("\0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
351-
rust_util_uint8_to_hex(rust_util_bytes(buf.as_ptr(), buf.len() - 1), unsafe {
352-
rust_util_cstr_mut(string.as_mut_ptr(), string.len())
184+
rust_util_uint8_to_hex(rust_util_bytes(buf.as_ptr(), buf.len()), unsafe {
185+
rust_util_bytes_mut(string.as_mut_ptr(), string.len())
353186
});
354187
assert_eq!(string, "0102030e0fff\0xxxxxxxxxxxxxxxxxxxxxxx");
355188
}
@@ -361,12 +194,10 @@ mod tests {
361194
assert!(unsafe {
362195
rust_base58_encode_check(
363196
rust_util_bytes(buf.as_ptr(), buf.len()),
364-
rust_util_cstr_mut(result_buf.as_mut_ptr(), result_buf.len()),
197+
rust_util_bytes_mut(result_buf.as_mut_ptr(), result_buf.len()),
365198
)
366199
});
367-
assert_eq!(
368-
(unsafe { rust_util_cstr(result_buf.as_ptr()) }).as_ref(),
369-
"LUC1eAJa5jW"
370-
);
200+
let expected = b"LUC1eAJa5jW\0";
201+
assert_eq!(&result_buf[..expected.len()], expected);
371202
}
372203
}

src/util.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void util_zero(volatile void* dst, size_t len)
3636
void util_uint8_to_hex(const uint8_t* in_bin, const size_t in_len, char* out)
3737
{
3838
rust_util_uint8_to_hex(
39-
rust_util_bytes(in_bin, in_len), rust_util_cstr_mut(out, in_len * 2 + 1));
39+
rust_util_bytes(in_bin, in_len), rust_util_bytes_mut((uint8_t*)out, in_len * 2 + 1));
4040
}
4141

4242
void util_cleanup_str(char** str)

test/unit-test/test_keystore_functional.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ static bool _encode_xpub(const struct ext_key* xpub, char* out, size_t out_len)
115115
return false;
116116
}
117117
return rust_base58_encode_check(
118-
rust_util_bytes(bytes, sizeof(bytes)), rust_util_cstr_mut(out, out_len));
118+
rust_util_bytes(bytes, sizeof(bytes)), rust_util_bytes_mut((uint8_t*)out, out_len));
119119
}
120120

121121
static void _check_pubs(const char* expected_xpub, const char* expected_pubkey_uncompressed_hex)

0 commit comments

Comments
 (0)