Skip to content

Commit afed0d8

Browse files
committed
Replace CStr::from_ptr() with CStr::from_bytes_with_nul()
`CStr::from_ptr()` is `unsafe` because it reads a raw pointer, and searches for a terminating nul character in the pointed region of memory. This is unnecessary as both calls already initialize a given number of characters and terminate with a nul, allowing us to pass a sized and initialized slice (without casting `*const MaybeUninit<u8>` to `*const u8`) directly to `CStr::from_bytes_with_nul()` (available since Rust 1.10, unlike `CStr::from_bytes_until_nul()` which was only stabilized in 1.69). Unfortunately all `std` helper APIs to initialize slices of `MaybeUninit` are still unstable, making this less ideal to write at the moment.
1 parent d51b7ff commit afed0d8

File tree

1 file changed

+48
-38
lines changed

1 file changed

+48
-38
lines changed

src/lib.rs

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,7 @@ impl Log for AndroidLogger {
224224
.unwrap_or_else(|| module_path.as_bytes());
225225

226226
// truncate the tag here to fit into LOGGING_TAG_MAX_LEN
227-
self.fill_tag_bytes(&mut tag_bytes, tag);
228-
// use stack array as C string
229-
let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };
227+
let tag = fill_tag_bytes(&mut tag_bytes, tag);
230228

231229
// message must not exceed LOGGING_MSG_MAX_LEN
232230
// therefore split log message into multiple log calls
@@ -250,23 +248,32 @@ impl Log for AndroidLogger {
250248
fn flush(&self) {}
251249
}
252250

253-
impl AndroidLogger {
254-
fn fill_tag_bytes(&self, array: &mut [MaybeUninit<u8>], tag: &[u8]) {
255-
if tag.len() > LOGGING_TAG_MAX_LEN {
256-
for (input, output) in tag
257-
.iter()
258-
.take(LOGGING_TAG_MAX_LEN - 2)
259-
.chain(b"..\0".iter())
260-
.zip(array.iter_mut())
261-
{
262-
output.write(*input);
263-
}
264-
} else {
265-
for (input, output) in tag.iter().chain(b"\0".iter()).zip(array.iter_mut()) {
266-
output.write(*input);
267-
}
251+
fn fill_tag_bytes<'a>(
252+
array: &'a mut [MaybeUninit<u8>; LOGGING_TAG_MAX_LEN + 1],
253+
tag: &[u8],
254+
) -> &'a CStr {
255+
// FIXME: Simplify when maybe_uninit_fill with MaybeUninit::fill_from() is stabilized
256+
let initialized;
257+
if tag.len() > LOGGING_TAG_MAX_LEN {
258+
for (input, output) in tag
259+
.iter()
260+
.take(LOGGING_TAG_MAX_LEN - 2)
261+
.chain(b"..\0")
262+
.zip(array.iter_mut())
263+
{
264+
output.write(*input);
265+
}
266+
initialized = array.as_slice();
267+
} else {
268+
for (input, output) in tag.iter().chain(b"\0".iter()).zip(array.iter_mut()) {
269+
output.write(*input);
268270
}
271+
initialized = &array[..tag.len() + 1];
269272
}
273+
274+
// SAFETY: The above code initialized the length of the tag plus a nul terminator, or the whole length of the slice
275+
let initialized = unsafe { slice_assume_init_ref(initialized) };
276+
CStr::from_bytes_with_nul(initialized).expect("Unreachable: we wrote a nul terminator")
270277
}
271278

272279
/// Filter for android logger.
@@ -449,7 +456,11 @@ impl<'a> PlatformLogWriter<'a> {
449456
self.buffer.get_unchecked_mut(len)
450457
});
451458

452-
let msg: &CStr = unsafe { CStr::from_ptr(self.buffer.as_ptr().cast()) };
459+
// TODO: This function must be `unsafe` - or receive an initialized slice or CStr directly -
460+
// as we can't otherwise guarantee safety here.
461+
let initialized = unsafe { slice_assume_init_ref(&self.buffer[..len + 1]) };
462+
let msg = CStr::from_bytes_with_nul(initialized)
463+
.expect("Unreachable: nul terminator was placed at `len`");
453464
android_log(self.buf_id, self.priority, self.tag, msg);
454465

455466
unsafe { *self.buffer.get_unchecked_mut(len) = last_byte };
@@ -544,6 +555,11 @@ fn uninit_array<const N: usize, T>() -> [MaybeUninit<T>; N] {
544555
unsafe { MaybeUninit::uninit().assume_init() }
545556
}
546557

558+
// FIXME: Remove when maybe_uninit_slice is stabilized to provide MaybeUninit::slice_assume_init_ref()
559+
unsafe fn slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
560+
&*(slice as *const [MaybeUninit<T>] as *const [T])
561+
}
562+
547563
#[cfg(test)]
548564
mod tests {
549565
use super::*;
@@ -604,28 +620,26 @@ mod tests {
604620

605621
#[test]
606622
fn fill_tag_bytes_truncates_long_tag() {
607-
let logger = AndroidLogger::new(Config::default());
608-
let too_long_tag: [u8; LOGGING_TAG_MAX_LEN + 20] = [b'a'; LOGGING_TAG_MAX_LEN + 20];
623+
let too_long_tag = [b'a'; LOGGING_TAG_MAX_LEN + 20];
609624

610-
let mut result: [MaybeUninit<u8>; LOGGING_TAG_MAX_LEN + 1] = uninit_array();
611-
logger.fill_tag_bytes(&mut result, &too_long_tag);
625+
let mut result = uninit_array();
626+
let tag = fill_tag_bytes(&mut result, &too_long_tag);
612627

613-
let mut expected_result = [b'a'; LOGGING_TAG_MAX_LEN - 2].to_vec();
628+
let mut expected_result = vec![b'a'; LOGGING_TAG_MAX_LEN - 2];
614629
expected_result.extend("..\0".as_bytes());
615-
assert_eq!(unsafe { assume_init_slice(&result) }, expected_result);
630+
assert_eq!(tag.to_bytes_with_nul(), expected_result);
616631
}
617632

618633
#[test]
619634
fn fill_tag_bytes_keeps_short_tag() {
620-
let logger = AndroidLogger::new(Config::default());
621-
let short_tag: [u8; 3] = [b'a'; 3];
635+
let short_tag = [b'a'; 3];
622636

623-
let mut result: [MaybeUninit<u8>; LOGGING_TAG_MAX_LEN + 1] = uninit_array();
624-
logger.fill_tag_bytes(&mut result, &short_tag);
637+
let mut result = uninit_array();
638+
let tag = fill_tag_bytes(&mut result, &short_tag);
625639

626640
let mut expected_result = short_tag.to_vec();
627641
expected_result.push(0);
628-
assert_eq!(unsafe { assume_init_slice(&result[..4]) }, expected_result);
642+
assert_eq!(tag.to_bytes_with_nul(), expected_result);
629643
}
630644

631645
#[test]
@@ -654,7 +668,7 @@ mod tests {
654668
assert_eq!(writer.len, 3);
655669
assert_eq!(writer.last_newline_index, 0);
656670
assert_eq!(
657-
unsafe { assume_init_slice(&writer.buffer[..writer.len]) },
671+
unsafe { slice_assume_init_ref(&writer.buffer[..writer.len]) },
658672
"\n90".as_bytes()
659673
);
660674

@@ -699,7 +713,7 @@ mod tests {
699713
writer.output_specified_len(5);
700714

701715
assert_eq!(
702-
unsafe { assume_init_slice(&writer.buffer[..log_string.len()]) },
716+
unsafe { slice_assume_init_ref(&writer.buffer[..log_string.len()]) },
703717
log_string.as_bytes()
704718
);
705719
}
@@ -714,7 +728,7 @@ mod tests {
714728
writer.copy_bytes_to_start(3, 2);
715729

716730
assert_eq!(
717-
unsafe { assume_init_slice(&writer.buffer[..10]) },
731+
unsafe { slice_assume_init_ref(&writer.buffer[..10]) },
718732
"3423456789".as_bytes()
719733
);
720734
}
@@ -731,7 +745,7 @@ mod tests {
731745
writer.copy_bytes_to_start(10, 0);
732746

733747
assert_eq!(
734-
unsafe { assume_init_slice(&writer.buffer[..test_string.len()]) },
748+
unsafe { slice_assume_init_ref(&writer.buffer[..test_string.len()]) },
735749
test_string.as_bytes()
736750
);
737751
}
@@ -743,8 +757,4 @@ mod tests {
743757
CStr::from_bytes_with_nul(b"tag\0").unwrap(),
744758
)
745759
}
746-
747-
unsafe fn assume_init_slice<T>(slice: &[MaybeUninit<T>]) -> &[T] {
748-
&*(slice as *const [MaybeUninit<T>] as *const [T])
749-
}
750760
}

0 commit comments

Comments
 (0)