Hi! I'm currently developing a fuzzer to test safe abstractions in Rust crates. I noticed two edge cases on Linux/X11 that related to how char_to_key_code(character: char) -> XKeyCode converts a Rust char to the C string passed into XStringToKeysym.
Environment
- OS: Linux x86_64 (Ubuntu)
- Rust toolchain:
nightly-2024-12-15
- Sanitizer: AddressSanitizer
- autopilot version: 0.4.1
Case 1:
#[test]
fn test1() {
let s = "Hello, world! \u{04E2} DONE";
crate::key::type_string(s, &[], 200.0, 0.0);
let _ = crate::alert::alert(s, None, None, None);
}
Run:
ASAN_OPTIONS="detect_leaks=0" RUSTFLAGS="-Zsanitizer=address" cargo test test1 --lib --target x86_64-unknown-linux-gnu -Zbuild-std=std
When the input contains \u{04E2}, AddressSanitizer reports a stack-buffer-overflow (out-of-bounds read).
ASan output (trimmed):
ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7d7efef16b62 at pc 0x63a1e9620713 bp 0x7d7effbfce60 sp 0x7d7effbfc628
READ of size 3 at 0x7d7efef16b62 thread T1
Case 2:
extern crate autopilot;
fn main() {
let s = "Hello, world! \u{F2B2C} DONE";
autopilot::key::type_string(s, &[], 200.0, 0.0);
let _ = autopilot::alert::alert(s, None, None, None);
}
Run:
When the input contains a character like \u{F2B2C}, it panics with:
thread 'main' panicked at .../core/src/char/methods.rs:1814:13:
encode_utf8: need 4 bytes to encode U+F2B2C but buffer has just 2
Potential Relevant code
#[cfg(target_os = "linux")]
fn char_to_key_code(character: char) -> XKeyCode {
match character {
// ... many ASCII mappings ...
_ => unsafe {
let mut buf = [0; 2];
x11::xlib::XStringToKeysym(
character.encode_utf8(&mut buf).as_ptr() as *const libc::c_char
) as XKeyCode
},
}
}
The code uses a fixed [u8; 2] buffer and passes character.encode_utf8(&mut buf) directly to XStringToKeysym. This can panic for characters that require 3–4 UTF-8 bytes, and even for 2-byte UTF-8 characters the produced bytes are not NUL-terminated, so XStringToKeysym (which expects a C string) may read past the stack buffer and trigger an ASan out-of-bounds read. A small robustness improvement could be to use a buffer large enough for any UTF-8 char plus \0 and explicitly NUL-terminate it (or use CString/CStr) before calling XStringToKeysym.
Thanks for taking a look!
Hi! I'm currently developing a fuzzer to test safe abstractions in Rust crates. I noticed two edge cases on Linux/X11 that related to how
char_to_key_code(character: char) -> XKeyCodeconverts a Rustcharto the C string passed intoXStringToKeysym.Environment
nightly-2024-12-15Case 1:
Run:
When the input contains
\u{04E2}, AddressSanitizer reports a stack-buffer-overflow (out-of-bounds read).ASan output (trimmed):
Case 2:
Run:
When the input contains a character like
\u{F2B2C}, it panics with:Potential Relevant code
The code uses a fixed
[u8; 2]buffer and passescharacter.encode_utf8(&mut buf)directly toXStringToKeysym. This can panic for characters that require 3–4 UTF-8 bytes, and even for 2-byte UTF-8 characters the produced bytes are not NUL-terminated, soXStringToKeysym(which expects a C string) may read past the stack buffer and trigger an ASan out-of-bounds read. A small robustness improvement could be to use a buffer large enough for any UTF-8charplus\0and explicitly NUL-terminate it (or useCString/CStr) before callingXStringToKeysym.Thanks for taking a look!