From 1d5179eb7180841dea36bd14a1c7b26d94220f95 Mon Sep 17 00:00:00 2001 From: Josh Megnauth Date: Fri, 18 Apr 2025 01:39:16 -0400 Subject: [PATCH] Const calculate buffer size for strerror The current buffer size is hard set to 256. Using const evaluation, the maximum buf size can be calculated at compile time. --- src/header/errno/mod.rs | 32 +++++++++++++++++++++++++++++++- src/header/string/mod.rs | 12 +++++------- tests/error.c | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/header/errno/mod.rs b/src/header/errno/mod.rs index b9c6f1510d..5ad3493e2d 100644 --- a/src/header/errno/mod.rs +++ b/src/header/errno/mod.rs @@ -178,7 +178,7 @@ pub const EOWNERDEAD: c_int = 130; /* Owner died */ pub const ENOTRECOVERABLE: c_int = 131; /* State not recoverable */ /// String representations for the respective `errno` values. -pub static STR_ERROR: [&'static str; 132] = [ +pub const STR_ERROR: [&str; 132] = [ "Success", "Operation not permitted", "No such file or directory", @@ -312,3 +312,33 @@ pub static STR_ERROR: [&'static str; 132] = [ "Owner died", "State not recoverable", ]; + +/// Longest error message length +pub const STRERROR_MAX: usize = { + // Number of digits of the max value of this platform's c_int + let digits = { + let mut len = 0; + let mut i = c_int::MAX; + + while i > 0 { + i /= 10; + len += 1; + } + + len + }; + + let mut longest: usize = "Unknown error: ".len() + digits + 1; + + let mut i = 0; + while i < STR_ERROR.len() { + let len = STR_ERROR[i].len() + 1; + if len > longest { + longest = len; + } + + i += 1; + } + + longest +}; diff --git a/src/header/string/mod.rs b/src/header/string/mod.rs index 852108e708..1afb20c0a5 100644 --- a/src/header/string/mod.rs +++ b/src/header/string/mod.rs @@ -447,15 +447,13 @@ pub unsafe extern "C" fn strdup(s1: *const c_char) -> *mut c_char { pub unsafe extern "C" fn strerror(errnum: c_int) -> *mut c_char { use core::fmt::Write; - static mut strerror_buf: [u8; 256] = [0; 256]; - + static mut strerror_buf: [u8; STRERROR_MAX] = [0; STRERROR_MAX]; let mut w = platform::StringWriter(strerror_buf.as_mut_ptr(), strerror_buf.len()); - if errnum >= 0 && errnum < STR_ERROR.len() as c_int { - let _ = w.write_str(STR_ERROR[errnum as usize]); - } else { - let _ = w.write_fmt(format_args!("Unknown error {}", errnum)); - } + let _ = match STR_ERROR.get(errnum as usize) { + Some(e) => w.write_str(e), + None => w.write_fmt(format_args!("Unknown error {}", errnum)), + }; strerror_buf.as_mut_ptr() as *mut c_char } diff --git a/tests/error.c b/tests/error.c index 4170e1f4be..1dbf13703d 100644 --- a/tests/error.c +++ b/tests/error.c @@ -1,7 +1,10 @@ -#include -#include -#include +#include #include +#include +#include +#include +#include +#include #include "test_helpers.h" @@ -12,15 +15,38 @@ int main(void) { printf("errno: %d = %s\n", err, strerror(errno)); perror("perror"); - char buf1[256]; + char buf1[256] = {0}; int ret1 = strerror_r(err, buf1, 256); printf("errno: %d = %s, return: %d\n", err, buf1, ret1); - char buf2[3]; + char buf2[3] = {0}; int ret2 = strerror_r(err, buf2, 3); printf("errno: %d = %s, return: %d\n", err, buf2, ret2); char buf3[256] = {0}; int ret3 = strerror_r(err, buf3, 0); printf("errno: %d = %s, return: %d\n", err, buf3, ret3); + + // Test that the statically allocated buffer doesn't overflow + for (int code = EPERM; code < ENOTRECOVERABLE; ++code) { + const char* message = strerror(code); + if (!message) { + errx(EXIT_FAILURE, + "Expected message for error code: %d", + code + ); + } + } + + const char* actual_error = strerror(INT_MAX); + char expected_error[256] = {0}; + sprintf(expected_error, "Unknown error %d", INT_MAX); + if (strncmp(expected_error, actual_error, 25) != 0) { + errx(EXIT_FAILURE, + "Expected: %s\nGot: %s", + expected_error, actual_error + ); + } + + return EXIT_SUCCESS; }