Skip to content

Commit fc21385

Browse files
committed
memory: validate name when setting/getting
While the BitBox02 API memory_setup() and SetDeviceName API checks should make sure that `memory_get_device_name()` always returns a valid ASCII-encoded string, we add explicit checks to make sure it is so. The reason for this is that the name is sent over to the Bluetooth firmware in the BB02+, and an invalid string there could cause a crash.
1 parent d15449d commit fc21385

File tree

4 files changed

+67
-5
lines changed

4 files changed

+67
-5
lines changed

src/memory/memory.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ bool memory_set_device_name(const char* name)
250250
_read_chunk(CHUNK_1, chunk_bytes);
251251
util_zero(chunk.fields.device_name, sizeof(chunk.fields.device_name));
252252
snprintf((char*)&chunk.fields.device_name, MEMORY_DEVICE_NAME_MAX_LEN, "%s", name);
253+
254+
if (!rust_util_is_name_valid(chunk.fields.device_name, MEMORY_DEVICE_NAME_MAX_LEN)) {
255+
return false;
256+
}
253257
return _write_chunk(CHUNK_1, chunk.bytes);
254258
}
255259

@@ -286,7 +290,8 @@ void memory_get_device_name(char* name_out)
286290
chunk_1_t chunk = {0};
287291
CLEANUP_CHUNK(chunk);
288292
_read_chunk(CHUNK_1, chunk_bytes);
289-
if (chunk.fields.device_name[0] == 0xFF) {
293+
if (chunk.fields.device_name[0] == 0xFF ||
294+
!rust_util_is_name_valid(chunk.fields.device_name, MEMORY_DEVICE_NAME_MAX_LEN)) {
290295
if (memory_get_platform() == MEMORY_PLATFORM_BITBOX02_PLUS) {
291296
// For Bluetooth, we want to use an unambiguous default name so this BitBox can be
292297
// identified if multiple BitBoxes are advertising at the same time.

src/memory/memory.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,14 @@ extern const char* MEMORY_DEFAULT_DEVICE_NAME;
6666
// memory.c)
6767
#define MEMORY_DEVICE_NAME_MAX_LEN (64)
6868

69-
// set device name. name is an utf8-encoded string, and null terminated. The max
70-
// size (including the null terminator) is MEMORY_DEVICE_NAME_MAX_LEN bytes.
69+
// set device name. name is null terminated. The name must be smaller or equal to
70+
// MEMORY_DEVICE_NAME_MAX_LEN (including the null terminator) and larger than 0 in size, consist of
71+
// printable ASCII characters only (and space), not start or end with whitespace, and contain no
72+
// whitespace other than space.
7173
USE_RESULT bool memory_set_device_name(const char* name);
7274

73-
// name_out must have MEMORY_DEVICE_NAME_MAX_LEN bytes in size. If no device name is set, we return:
75+
// name_out must have MEMORY_DEVICE_NAME_MAX_LEN bytes in size. If no device name is set, or if it
76+
// is invalid, we return:
7477
// - `MEMORY_DEFAULT_DEVICE_NAME` for non-bluetooth enabled BitBoxes
7578
// - "BitBox ABCD" for Bluetooth-enabled BitBoxes, where ABCD are four random uppercase letters.
7679
// The name is cached in RAM, so the same random name is returned until reboot.

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ pub extern "C" fn rust_util_zero(mut dst: BytesMut) {
2525
util::zero(dst.as_mut())
2626
}
2727

28+
/// Calls `util::name::validate()` on the provided C string.
29+
/// SAFETY:
30+
/// `buf` must point to a valid buffer of size `max_len`.
31+
#[no_mangle]
32+
pub unsafe extern "C" fn rust_util_is_name_valid(buf: *const u8, max_len: usize) -> bool {
33+
if max_len == 0 {
34+
return false;
35+
}
36+
let slice = core::slice::from_raw_parts(buf, max_len);
37+
match core::ffi::CStr::from_bytes_until_nul(slice) {
38+
Ok(cstr) => match cstr.to_str() {
39+
Ok(s) => util::name::validate(s, max_len - 1),
40+
Err(_) => false,
41+
},
42+
Err(_) => false,
43+
}
44+
}
45+
2846
/// Convert bytes to hex representation
2947
///
3048
/// * `buf` - bytes to convert to hex.
@@ -202,4 +220,22 @@ mod tests {
202220
let expected = b"LUC1eAJa5jW\0";
203221
assert_eq!(&result_buf[..expected.len()], expected);
204222
}
223+
224+
#[test]
225+
// For clarity we want explicit null terminators in the strings below, not CStr literals
226+
// `c"..."`.
227+
#[allow(clippy::manual_c_str_literals)]
228+
fn test_rust_util_is_name_valid() {
229+
unsafe {
230+
// Valid
231+
assert!(rust_util_is_name_valid("foo\0".as_ptr(), 4));
232+
assert!(rust_util_is_name_valid("foo\0........".as_ptr(), 12));
233+
234+
// Invalid
235+
assert!(!rust_util_is_name_valid("fo\no\0".as_ptr(), 5));
236+
assert!(!rust_util_is_name_valid("".as_ptr(), 0));
237+
assert!(!rust_util_is_name_valid("foo\0".as_ptr(), 3));
238+
assert!(!rust_util_is_name_valid("foo".as_ptr(), 3));
239+
}
240+
}
205241
}

test/unit-test/test_memory.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,14 +423,31 @@ static void _test_memory_get_device_name_default_bluetooth(void** state)
423423
assert_string_equal("BitBox AZUV", name_out);
424424
}
425425

426-
static void _test_memory_get_device_name(void** state)
426+
static void _test_memory_get_device_name_invalid(void** state)
427427
{
428428
char name_out[MEMORY_DEVICE_NAME_MAX_LEN] = {0};
429429
EMPTYCHUNK(chunk);
430430
memset(chunk + _addr_device_name, 0, MEMORY_DEVICE_NAME_MAX_LEN);
431431
const char* device_name = "Äxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxx 漢字xxxxxxxxxxxxxxxxx";
432432
snprintf((char*)chunk + _addr_device_name, MEMORY_DEVICE_NAME_MAX_LEN, "%s", device_name);
433433

434+
EMPTYCHUNK(empty_shared_chunk);
435+
will_return(__wrap_memory_read_shared_bootdata_mock, empty_shared_chunk);
436+
437+
expect_value(__wrap_memory_read_chunk_mock, chunk_num, 1);
438+
will_return(__wrap_memory_read_chunk_mock, chunk);
439+
memory_get_device_name(name_out);
440+
assert_string_equal(MEMORY_DEFAULT_DEVICE_NAME, name_out);
441+
}
442+
443+
static void _test_memory_get_device_name(void** state)
444+
{
445+
char name_out[MEMORY_DEVICE_NAME_MAX_LEN] = {0};
446+
EMPTYCHUNK(chunk);
447+
memset(chunk + _addr_device_name, 0, MEMORY_DEVICE_NAME_MAX_LEN);
448+
const char* device_name = "foo bar";
449+
snprintf((char*)chunk + _addr_device_name, MEMORY_DEVICE_NAME_MAX_LEN, "%s", device_name);
450+
434451
expect_value(__wrap_memory_read_chunk_mock, chunk_num, 1);
435452
will_return(__wrap_memory_read_chunk_mock, chunk);
436453
memory_get_device_name(name_out);
@@ -561,6 +578,7 @@ int main(void)
561578
cmocka_unit_test(_test_memory_reset_hww),
562579
cmocka_unit_test(_test_memory_get_device_name_default),
563580
cmocka_unit_test(_test_memory_get_device_name_default_bluetooth),
581+
cmocka_unit_test(_test_memory_get_device_name_invalid),
564582
cmocka_unit_test(_test_memory_get_device_name),
565583
cmocka_unit_test(_test_memory_device_name),
566584
cmocka_unit_test(_test_memory_set_seed_birthdate),

0 commit comments

Comments
 (0)