Skip to content

Commit f8d1b72

Browse files
committed
Merge branch 'cstr'
2 parents 466a016 + 9a5d108 commit f8d1b72

File tree

8 files changed

+84
-311
lines changed

8 files changed

+84
-311
lines changed

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

Lines changed: 33 additions & 258 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
///
@@ -26,29 +25,22 @@ pub extern "C" fn rust_util_zero(mut dst: BytesMut) {
2625
util::zero(dst.as_mut())
2726
}
2827

29-
#[no_mangle]
30-
pub extern "C" fn rust_util_u64_be(v: u64, mut dst: BytesMut) {
31-
dst.as_mut().copy_from_slice(&v.to_be_bytes())
32-
}
33-
3428
/// Convert bytes to hex representation
3529
///
3630
/// * `buf` - bytes to convert to hex.
3731
/// * `out` - hex will be written here. out len must be at least 2*buf.len+1.
3832
#[no_mangle]
39-
pub extern "C" fn rust_util_uint8_to_hex(buf: Bytes, mut out: CStrMut) {
40-
let min_len = buf.len * 2;
41-
match out.write(min_len, |out| {
42-
// Avoid .unwrap() here until the following compiler regression is fixed:
43-
// https://github.com/rust-lang/rust/issues/83925
44-
match hex::encode_to_slice(&buf, out) {
45-
Ok(()) => {}
46-
Err(err) => panic!("{:?}", err),
47-
}
48-
}) {
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]) {
4939
Ok(()) => {}
50-
Err(_) => panic!("couldn't write to buffer"),
40+
Err(err) => panic!("{:?}", err),
5141
}
42+
// Null terminator.
43+
out.as_mut()[hexlen] = 0;
5244
}
5345

5446
#[repr(C)]
@@ -105,133 +97,6 @@ impl AsMut<[u8]> for BytesMut {
10597
}
10698
}
10799

108-
/// CStr is a null-terminated string. Null pointers are interpreted as empty strings.
109-
#[repr(C)]
110-
pub struct CStr {
111-
buf: *const c_char,
112-
len: usize,
113-
}
114-
115-
impl CStr {
116-
/// Create a CStr from a null-terminated string or null pointer. Unsafe because it will read
117-
/// until it finds a null character.
118-
pub unsafe fn new(buf: *const c_char) -> Self {
119-
if buf.is_null() {
120-
CStr {
121-
buf: core::ptr::NonNull::dangling().as_ptr(),
122-
len: 0,
123-
}
124-
} else {
125-
let mut len = 0;
126-
let mut b = buf;
127-
while b.read() != 0 {
128-
len += 1;
129-
b = b.offset(1);
130-
}
131-
CStr { buf, len }
132-
}
133-
}
134-
}
135-
136-
impl AsRef<str> for CStr {
137-
fn as_ref(&self) -> &str {
138-
unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.buf, self.len)) }
139-
}
140-
}
141-
142-
/// CStrMut is a "growable" container which keeps track of some array allocated by C with a length
143-
/// and a capacity state. It always contains a null-terminated string. The string (excluding null
144-
/// terminator) can therefore be maximally `capacity-1` long.
145-
#[repr(C)]
146-
pub struct CStrMut {
147-
buf: *mut c_char,
148-
len: usize,
149-
cap: usize,
150-
}
151-
152-
impl CStrMut {
153-
/// Create a new growable string with capacity `cap`. Only allowed for non-null pointers with
154-
/// length or null pointers with 0 length due to limitation in `core::slice`. Unsafe because it
155-
/// will read until it finds a null character.
156-
pub unsafe fn new(buf: *mut c_char, cap: usize) -> Self {
157-
let mut len = 0;
158-
let mut buf = buf;
159-
if buf.is_null() {
160-
if cap != 0 {
161-
panic!("Null pointer can't have capacity");
162-
}
163-
buf = core::ptr::NonNull::dangling().as_ptr();
164-
} else {
165-
let mut b = buf;
166-
while b.read() != 0 {
167-
len += 1;
168-
b = b.offset(1);
169-
if len == cap {
170-
panic!("CStrMut not null terminated");
171-
}
172-
}
173-
}
174-
175-
CStrMut { buf, len, cap }
176-
}
177-
178-
/// Provide a mutable slice to an unused range of the buffer. The provided function `f` must
179-
/// fill the requested buffer with utf-8 valid characters and it must not write a null
180-
/// character in the buffer.
181-
///
182-
/// # Panics
183-
///
184-
/// This function returns an error in case the provided buffer contains NULL or non-valid utf-8
185-
/// characters after function `f` is applied. It will also return an error if more bytes are
186-
/// requested then are available.
187-
pub fn write<F>(&mut self, req: usize, f: F) -> Result<(), core::fmt::Error>
188-
where
189-
F: FnOnce(&mut [u8]),
190-
{
191-
// Must be room for requested amount of bytes and null terminator.
192-
if self.cap - self.len < req + 1 {
193-
// Not enough bytes left in buffer
194-
return Err(core::fmt::Error);
195-
}
196-
let len = self.len;
197-
let slice = unsafe { self.as_bytes_mut() };
198-
let slice = &mut slice[len..len + req + 1];
199-
let write_slice = &mut slice[0..req];
200-
f(write_slice);
201-
if write_slice.iter().any(|&c| c == 0) {
202-
// null terminated strings can't contain null
203-
return Err(core::fmt::Error);
204-
}
205-
if core::str::from_utf8(write_slice).is_err() {
206-
// strings must be valid utf-8
207-
return Err(core::fmt::Error);
208-
}
209-
slice[req] = 0;
210-
self.len += req;
211-
Ok(())
212-
}
213-
214-
/// Get slice of underlying byte array. Unsafe because you have to ensure that length is up to
215-
/// date and that there is a null character at `buf[len]`.
216-
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
217-
core::slice::from_raw_parts_mut(self.buf, self.cap)
218-
}
219-
}
220-
221-
impl AsRef<str> for CStrMut {
222-
fn as_ref(&self) -> &str {
223-
unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.buf, self.len)) }
224-
}
225-
}
226-
227-
impl core::fmt::Write for CStrMut {
228-
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
229-
self.write(s.len(), |buf| {
230-
buf.copy_from_slice(s.as_bytes());
231-
})
232-
}
233-
}
234-
235100
/// Convert buffer to slice
236101
///
237102
/// * `buf` - Must be a valid pointer to an array of bytes
@@ -246,41 +111,24 @@ pub extern "C" fn rust_util_bytes(buf: *const c_uchar, len: usize) -> Bytes {
246111
/// * `buf` - Must be a valid pointer to an array of bytes
247112
/// * `len` - Length of buffer, `buf[len-1]` must be a valid dereference
248113
#[no_mangle]
249-
pub extern "C" fn rust_util_bytes_mut(buf: *mut c_uchar, len: usize) -> BytesMut {
114+
pub unsafe extern "C" fn rust_util_bytes_mut(buf: *mut c_uchar, len: usize) -> BytesMut {
250115
BytesMut { buf, len }
251116
}
252117

253-
/// Convert buffer to str.
254-
///
255-
/// * `buf` - Must be a valid pointer to a null terminated array of bytes.
256-
#[no_mangle]
257-
pub unsafe extern "C" fn rust_util_cstr(buf: *const c_char) -> CStr {
258-
CStr::new(buf)
259-
}
260-
261-
/// Convert buffer to mutable str. The whole buffer is considered empty from start.
262-
///
263-
/// * `buf` - Must be a valid pointer to an array of bytes
264-
/// * `cap` - Length of buffer, `buf_ptr[cap-1]` must be a valid dereference
265-
#[no_mangle]
266-
pub unsafe extern "C" fn rust_util_cstr_mut(buf: *mut c_char, cap: usize) -> CStrMut {
267-
if !buf.is_null() {
268-
buf.write(0);
269-
}
270-
CStrMut::new(buf, cap)
271-
}
272-
273118
/// Base58Check-encode the input.
274119
///
275120
/// #Safety
276121
/// buf and out must not be NULL and point to valid memory areas.
277122
#[no_mangle]
278-
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 {
279124
if buf.len == 0 {
280125
return false;
281126
}
282127
let encoded = bs58::encode(buf.as_ref()).with_check().into_string();
283-
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
284132
}
285133

286134
#[cfg(test)]
@@ -291,37 +139,28 @@ mod tests {
291139
#[test]
292140
fn zeroing() {
293141
let mut buf = [1u8, 2, 3, 4];
294-
rust_util_zero(rust_util_bytes_mut(buf.as_mut_ptr(), buf.len() - 1));
142+
rust_util_zero(unsafe { rust_util_bytes_mut(buf.as_mut_ptr(), buf.len() - 1) });
295143
assert_eq!(&buf[..], &[0, 0, 0, 4]);
296144
}
297145

298146
#[test]
299147
fn zeroing_empty() {
300148
let mut buf = [];
301-
rust_util_zero(rust_util_bytes_mut(buf.as_mut_ptr(), 0));
149+
rust_util_zero(unsafe { rust_util_bytes_mut(buf.as_mut_ptr(), 0) });
302150
}
303151

304152
#[test]
305153
fn zeroing_null() {
306-
rust_util_zero(rust_util_bytes_mut(core::ptr::null_mut(), 0));
307-
}
308-
309-
#[test]
310-
fn test_rust_util_cstr() {
311-
let cstr = unsafe { rust_util_cstr(b"\0".as_ptr()) };
312-
assert_eq!(cstr.as_ref(), "");
313-
assert_eq!(cstr.len, 0);
314-
315-
let cstr = unsafe { rust_util_cstr(b"foo\0bar".as_ptr()) };
316-
assert_eq!(cstr.as_ref(), "foo");
317-
assert_eq!(cstr.len, 3);
154+
rust_util_zero(unsafe { rust_util_bytes_mut(core::ptr::null_mut(), 0) });
318155
}
319156

320157
#[test]
321158
#[should_panic]
322159
fn create_invalid_bytes_mut() {
323160
// Calling `as_mut()` will panic because it tries to create an invalid rust slice.
324-
rust_util_bytes_mut(core::ptr::null_mut(), 1).as_mut();
161+
unsafe {
162+
rust_util_bytes_mut(core::ptr::null_mut(), 1).as_mut();
163+
}
325164
}
326165

327166
#[test]
@@ -332,80 +171,18 @@ mod tests {
332171
}
333172

334173
#[test]
335-
fn test_cstr_mut() {
336-
let mut start = String::from("foo\0bar");
337-
let mut cstr_mut = unsafe { rust_util_cstr_mut(start.as_mut_ptr(), start.len()) };
338-
assert_eq!(cstr_mut.len, 0);
339-
assert_eq!(cstr_mut.as_ref(), "");
340-
cstr_mut.write(1, |buf| buf[0] = b'g').unwrap();
341-
assert_eq!(cstr_mut.as_ref(), "g");
342-
}
343-
344-
#[test]
345-
fn test_cstr_mut_new() {
346-
let mut start = String::from("foo\0bar");
347-
let mut cstr_mut = unsafe { CStrMut::new(start.as_mut_ptr(), start.len()) };
348-
assert_eq!(cstr_mut.len, 3);
349-
assert_eq!(cstr_mut.as_ref(), "foo");
350-
cstr_mut.write(1, |buf| buf[0] = b'g').unwrap();
351-
assert_eq!(cstr_mut.as_ref(), "foog");
352-
}
353-
354-
#[test]
355-
#[should_panic]
356-
fn test_invalid_cstr_mut() {
357-
let mut buf = [1, 2, 3];
358-
let cstr_mut = unsafe { CStrMut::new(buf.as_mut_ptr(), buf.len()) };
359-
// panics as there is no null terminator.
360-
cstr_mut.as_ref();
361-
}
362-
363-
#[test]
364-
fn test_invalid_cstr_mut_write_null() {
365-
let mut s = String::from("abc\0xxx");
366-
let mut cstr_mut = unsafe { CStrMut::new(s.as_mut_ptr(), s.len()) };
367-
assert!(cstr_mut.write(1, |buf| buf[0] = 0).is_err());
368-
}
369-
370-
#[test]
371-
fn test_invalid_cstr_mut_out_of_buffer() {
372-
let mut s = String::from("abc\0");
373-
let mut cstr_mut = unsafe { CStrMut::new(s.as_mut_ptr(), s.len()) };
374-
assert!(cstr_mut.write(1, |buf| buf[0] = b'd').is_err());
375-
}
376-
377-
#[test]
378-
fn test_cstr_mut_write() {
379-
let mut buf = vec![0; 9];
380-
let mut cstr_mut = unsafe { CStrMut::new(buf.as_mut_ptr(), buf.len()) };
381-
use std::fmt::Write;
382-
assert!(write!(&mut cstr_mut, "test").is_ok());
383-
assert!(buf.starts_with(b"test\0"));
384-
assert!(write!(&mut cstr_mut, " foo").is_ok());
385-
assert!(buf.starts_with(b"test foo\0"));
386-
}
387-
388-
#[test]
389-
fn test_cstr_mut_write_too_much() {
390-
let mut buf = vec![0; 9];
391-
let mut cstr_mut = unsafe { CStrMut::new(buf.as_mut_ptr(), buf.len()) };
392-
use std::fmt::Write;
393-
assert!(write!(&mut cstr_mut, "test foo ").is_err());
394-
}
395-
396-
#[test]
397-
fn u8_to_hexing() {
398-
let buf = [1u8, 2, 3, 14, 15, 255, 1];
399-
let mut string = String::from("\0xxxxxxxxxxxxx");
400-
rust_util_uint8_to_hex(rust_util_bytes(buf.as_ptr(), buf.len() - 1), unsafe {
401-
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())
402179
});
403-
assert_eq!(string, "0102030e0fff\0x");
180+
assert_eq!(string, "0102030e0fff\0");
404181

405182
// Bigger buffer also works.
406183
let mut string = String::from("\0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
407-
rust_util_uint8_to_hex(rust_util_bytes(buf.as_ptr(), buf.len() - 1), unsafe {
408-
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())
409186
});
410187
assert_eq!(string, "0102030e0fff\0xxxxxxxxxxxxxxxxxxxxxxx");
411188
}
@@ -417,12 +194,10 @@ mod tests {
417194
assert!(unsafe {
418195
rust_base58_encode_check(
419196
rust_util_bytes(buf.as_ptr(), buf.len()),
420-
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()),
421198
)
422199
});
423-
assert_eq!(
424-
(unsafe { rust_util_cstr(result_buf.as_ptr()) }).as_ref(),
425-
"LUC1eAJa5jW"
426-
);
200+
let expected = b"LUC1eAJa5jW\0";
201+
assert_eq!(&result_buf[..expected.len()], expected);
427202
}
428203
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,11 @@ pub unsafe extern "C" fn rust_workflow_spawn_unlock() {
4444

4545
#[no_mangle]
4646
pub unsafe extern "C" fn rust_workflow_spawn_confirm(
47-
title: crate::util::CStr,
48-
body: crate::util::CStr,
47+
title: *const core::ffi::c_char,
48+
body: *const core::ffi::c_char,
4949
) {
50-
CONFIRM_TITLE = Some(title.as_ref().into());
51-
CONFIRM_BODY = Some(body.as_ref().into());
52-
50+
CONFIRM_TITLE = Some(core::ffi::CStr::from_ptr(title).to_str().unwrap().into());
51+
CONFIRM_BODY = Some(core::ffi::CStr::from_ptr(body).to_str().unwrap().into());
5352
CONFIRM_PARAMS = Some(confirm::Params {
5453
title: CONFIRM_TITLE.as_ref().unwrap(),
5554
body: CONFIRM_BODY.as_ref().unwrap(),

0 commit comments

Comments
 (0)