From 9b84c5993e9a47287831ad62aad7d25440dc1993 Mon Sep 17 00:00:00 2001 From: markhansen Date: Fri, 3 Jul 2026 16:29:20 +1000 Subject: [PATCH 1/2] Add reproduction test case for /proc/cpuinfo parser bug Added a reproduction test case `repro_cpuinfo` which uses a mock filesystem with an overly long 'Hardware' value to trigger the issue. The test is added to the CMake build (guarded for ARM). Marked the test as WILL_FAIL in CMake because the bug is not yet fixed in this commit. TAG=agy CONV=54bab573-9944-4cb7-9b45-7b056f7d9c47 --- CMakeLists.txt | 8 ++++++++ test/mock/repro_cpuinfo.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 test/mock/repro_cpuinfo.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 072c9873..6dbd72e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -805,6 +805,14 @@ IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_MOCK_TESTS) TARGET_LINK_LIBRARIES(zenfone-2e-test PRIVATE cpuinfo_mock gtest) ADD_TEST(NAME zenfone-2e-test COMMAND zenfone-2e-test) ENDIF() + + IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(armv[5-8].*|aarch64|arm64.*)$") + ADD_EXECUTABLE(repro-cpuinfo-test test/mock/repro_cpuinfo.c) + TARGET_INCLUDE_DIRECTORIES(repro-cpuinfo-test BEFORE PRIVATE src) + TARGET_LINK_LIBRARIES(repro-cpuinfo-test PRIVATE cpuinfo_mock) + ADD_TEST(NAME repro-cpuinfo-test COMMAND repro-cpuinfo-test) + SET_TESTS_PROPERTIES(repro-cpuinfo-test PROPERTIES WILL_FAIL TRUE) + ENDIF() ENDIF() # ---[ cpuinfo unit tests diff --git a/test/mock/repro_cpuinfo.c b/test/mock/repro_cpuinfo.c new file mode 100644 index 00000000..22d71c37 --- /dev/null +++ b/test/mock/repro_cpuinfo.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +static struct cpuinfo_mock_file mock_files[] = { + { + .path = "/proc/cpuinfo", + .size = 0, // Will be initialized in main + .content = + "Hardware\t: " + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n", + }, + {.path = NULL}}; + +int main() { + mock_files[0].size = strlen(mock_files[0].content); + cpuinfo_mock_filesystem(mock_files); + + char hardware[64]; + char revision[9]; + struct cpuinfo_arm_linux_processor processors[1]; + + printf("Calling cpuinfo_arm_linux_parse_proc_cpuinfo...\n"); + cpuinfo_arm_linux_parse_proc_cpuinfo(hardware, revision, 1, processors); + + printf("Hardware parsed: %s\n", hardware); + + char expected[64]; + memset(expected, 'A', 63); + expected[63] = '\0'; + + if (strcmp(hardware, expected) != 0) { + fprintf(stderr, "FAIL: expected length 63, got %s (length %zu)\n", hardware, strlen(hardware)); + return 1; + } + printf("PASS\n"); + return 0; +} From 0d702a84f92122018e42dd922daa3e36bfe4316c Mon Sep 17 00:00:00 2001 From: markhansen Date: Fri, 3 Jul 2026 16:29:20 +1000 Subject: [PATCH 2/2] Fix out-of-bounds write and lack of null-termination in /proc/cpuinfo parser The parser for 'Hardware' and 'Revision' fields in src/arm/linux/cpuinfo.c had logic errors when handling values with length equal to or greater than the maximum buffer size. 1. If value_length == LIMIT, it would write '\0' at index LIMIT, which is out of bounds (buffer size is LIMIT). 2. If value_length > LIMIT, it would truncate to LIMIT, skip writing the null terminator, and copy LIMIT bytes, leaving the buffer non-null-terminated. Fixed by checking if value_length >= LIMIT, truncating to LIMIT - 1 if so, and always writing the null terminator at the end of the (potentially truncated) copied value. Removed the WILL_FAIL property from the repro test in CMakeLists.txt as the test now passes. TAG=agy CONV=54bab573-9944-4cb7-9b45-7b056f7d9c47 --- CMakeLists.txt | 1 - src/arm/linux/cpuinfo.c | 18 ++++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dbd72e4..68527d05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -811,7 +811,6 @@ IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_MOCK_TESTS) TARGET_INCLUDE_DIRECTORIES(repro-cpuinfo-test BEFORE PRIVATE src) TARGET_LINK_LIBRARIES(repro-cpuinfo-test PRIVATE cpuinfo_mock) ADD_TEST(NAME repro-cpuinfo-test COMMAND repro-cpuinfo-test) - SET_TESTS_PROPERTIES(repro-cpuinfo-test PROPERTIES WILL_FAIL TRUE) ENDIF() ENDIF() diff --git a/src/arm/linux/cpuinfo.c b/src/arm/linux/cpuinfo.c index 081d9aab..6ba5bdfc 100644 --- a/src/arm/linux/cpuinfo.c +++ b/src/arm/linux/cpuinfo.c @@ -872,31 +872,29 @@ static bool parse_line( /* BogoMIPS is useless, don't parse */ } else if (memcmp(line_start, "Hardware", key_length) == 0) { size_t value_length = value_end - value_start; - if (value_length > CPUINFO_HARDWARE_VALUE_MAX) { + if (value_length >= CPUINFO_HARDWARE_VALUE_MAX) { cpuinfo_log_warning( "length of Hardware value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit", (int)value_length, value_start, - CPUINFO_HARDWARE_VALUE_MAX); - value_length = CPUINFO_HARDWARE_VALUE_MAX; - } else { - state->hardware[value_length] = '\0'; + CPUINFO_HARDWARE_VALUE_MAX - 1); + value_length = CPUINFO_HARDWARE_VALUE_MAX - 1; } + state->hardware[value_length] = '\0'; memcpy(state->hardware, value_start, value_length); cpuinfo_log_debug( "parsed /proc/cpuinfo Hardware = \"%.*s\"", (int)value_length, value_start); } else if (memcmp(line_start, "Revision", key_length) == 0) { size_t value_length = value_end - value_start; - if (value_length > CPUINFO_REVISION_VALUE_MAX) { + if (value_length >= CPUINFO_REVISION_VALUE_MAX) { cpuinfo_log_warning( "length of Revision value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit", (int)value_length, value_start, - CPUINFO_REVISION_VALUE_MAX); - value_length = CPUINFO_REVISION_VALUE_MAX; - } else { - state->revision[value_length] = '\0'; + CPUINFO_REVISION_VALUE_MAX - 1); + value_length = CPUINFO_REVISION_VALUE_MAX - 1; } + state->revision[value_length] = '\0'; memcpy(state->revision, value_start, value_length); cpuinfo_log_debug( "parsed /proc/cpuinfo Revision = \"%.*s\"", (int)value_length, value_start);