From ed4197f2bb13b3385d30dd77e8e1b55cedbaf046 Mon Sep 17 00:00:00 2001 From: Wojciech Kaczmarski Date: Tue, 4 Aug 2026 19:52:54 +0300 Subject: [PATCH 1/2] utils.c: fix parsing of addr:size and addr:size:path arguments --- cskburn/src/utils.c | 52 +++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/cskburn/src/utils.c b/cskburn/src/utils.c index 9d9fd80..3e133ae 100644 --- a/cskburn/src/utils.c +++ b/cskburn/src/utils.c @@ -41,39 +41,55 @@ scan_int(const char *str, uint32_t *out) bool scan_addr_size(const char *str, uint32_t *addr, uint32_t *size) { - char *split = strstr(str, ":"); - if (split == NULL) { + if (str == NULL || addr == NULL || size == NULL) + return false; + + char buf[64]; + if (strlen(str) >= sizeof(buf)) return false; - } - const char *str_addr = str; - const char *str_size = split + 1; + strcpy(buf, str); - return scan_int(str_addr, addr) && scan_int(str_size, size); + char *addr_str = buf; + char *size_str = strchr(addr_str, ':'); + if (size_str == NULL) + return false; + *size_str++ = '\0'; + + return scan_int(addr_str, addr) && scan_int(size_str, size); } bool -scan_addr_size_name(const char *str, uint32_t *addr, uint32_t *size, const char **name) +scan_addr_size_name(const char *str, + uint32_t *addr, + uint32_t *size, + const char **name) { - char *split; + if (str == NULL || addr == NULL || size == NULL || name == NULL) + return false; - if (name == NULL) { + char buf[512]; + if (strlen(str) >= sizeof(buf)) return false; - } - if ((split = strstr(str, ":")) == NULL) { + strcpy(buf, str); + + char *addr_str = buf; + char *size_str = strchr(addr_str, ':'); + if (size_str == NULL) return false; - } + *size_str++ = '\0'; - const char *str_addr = str; - const char *str_size = split + 1; + char *name_str = strchr(size_str, ':'); + if (name_str == NULL) + return false; + *name_str++ = '\0'; - if ((split = strstr(str_size, ":")) == NULL) { + if (!scan_int(addr_str, addr) || !scan_int(size_str, size)) return false; - } - *name = split + 1; - return scan_int(str_addr, addr) && scan_int(str_size, size); + *name = str + (name_str - buf); + return true; } void From 717e27a94ac2e8d8e5100a9f066ca8d39cfeabd0 Mon Sep 17 00:00:00 2001 From: xychen Date: Fri, 7 Aug 2026 17:03:13 +0800 Subject: [PATCH 2/2] cskburn: harden compound argument parsing and add tests Keep strict integer validation and parse addr:size[:path] directly at the delimiters. Avoid fixed path buffers, and reject empty paths and invalid output pointers. Treat only values with a 0x/0X prefix as hexadecimal. Parse all other values as decimal. Add CTest coverage for the C62 backup form, long and Windows paths, overflow, empty fields, and trailing garbage. Run tests in Linux x64 CI. Co-Authored-By: OpenAI Codex (GPT-5) Signed-off-by: xychen --- .github/workflows/build.yml | 15 +++++ CMakeLists.txt | 2 +- cskburn/CMakeLists.txt | 10 +++ cskburn/src/utils.c | 96 +++++++++++++++++---------- cskburn/tests/test_utils.c | 126 ++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 36 deletions(-) create mode 100644 cskburn/tests/test_utils.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 956ce7e..69ccdc4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,6 +57,21 @@ jobs: cmake --build build --config Release ' + - name: Test + if: matrix.name == 'linux-x64' + uses: tj-actions/docker-run@v2 + with: + image: cskburn-${{ matrix.name }}:latest + name: ${{ matrix.name }}-test + options: >- + --platform ${{ matrix.platform }} + --volume ${{ github.workspace }}:/cskburn + --workdir /cskburn + args: | + bash -c ' + ctest --test-dir build --output-on-failure + ' + - name: Upload artifacts uses: actions/upload-artifact@v4 with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e213a4..7a534c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) project(CSKBurnKit) -enable_testing() +include(CTest) # 未显式指定时默认 Release:Ninja 等单配置生成器下 --config 无效, # 否则发布产物会是无优化、带 assert 的默认构建 diff --git a/cskburn/CMakeLists.txt b/cskburn/CMakeLists.txt index ba0409a..dea8bf7 100644 --- a/cskburn/CMakeLists.txt +++ b/cskburn/CMakeLists.txt @@ -48,3 +48,13 @@ target_compile_options( ) install(TARGETS ${PROJECT_NAME} DESTINATION bin) + +if(BUILD_TESTING) + add_executable( + cskburn_utils_test + tests/test_utils.c + src/utils.c + ) + target_include_directories(cskburn_utils_test PRIVATE src) + add_test(NAME cskburn_utils COMMAND cskburn_utils_test) +endif() diff --git a/cskburn/src/utils.c b/cskburn/src/utils.c index 3e133ae..615e647 100644 --- a/cskburn/src/utils.c +++ b/cskburn/src/utils.c @@ -1,5 +1,6 @@ #include "utils.h" +#include #include #include #include @@ -19,44 +20,75 @@ read_file(const char *path, uint8_t *buf, uint32_t limit) return (uint32_t)len; } -bool -scan_int(const char *str, uint32_t *out) +static bool +scan_int_prefix(const char *str, uint32_t *out, const char **end) { - if (str == NULL || str[0] == '\0' || str[0] == '-') { + if (str == NULL || out == NULL || end == NULL) { + return false; + } + + const char *first = str; + while (isspace((unsigned char)*first)) { + first++; + } + if (*first == '\0' || *first == '-') { return false; } - // base 0 自动识别 0x/0X 十六进制前缀,其余按十进制解析 - char *end = NULL; + const char *digits = *first == '+' ? first + 1 : first; + int base = digits[0] == '0' && (digits[1] == 'x' || digits[1] == 'X') ? 16 : 10; + + // 仅 0x/0X 前缀按十六进制解析,其余按十进制解析 + char *parsed_end = NULL; errno = 0; - unsigned long val = strtoul(str, &end, 0); - if (errno != 0 || end == str || *end != '\0' || val > UINT32_MAX) { + unsigned long val = strtoul(str, &parsed_end, base); + if (errno != 0 || parsed_end == str || val > UINT32_MAX) { return false; } *out = (uint32_t)val; + *end = parsed_end; return true; } bool -scan_addr_size(const char *str, uint32_t *addr, uint32_t *size) +scan_int(const char *str, uint32_t *out) { - if (str == NULL || addr == NULL || size == NULL) + if (out == NULL) { + return false; + } + + uint32_t value; + const char *end; + if (!scan_int_prefix(str, &value, &end) || *end != '\0') { return false; + } + + *out = value; + return true; +} - char buf[64]; - if (strlen(str) >= sizeof(buf)) +bool +scan_addr_size(const char *str, uint32_t *addr, uint32_t *size) +{ + if (str == NULL || addr == NULL || size == NULL) { return false; + } - strcpy(buf, str); + uint32_t parsed_addr; + uint32_t parsed_size; + const char *end; - char *addr_str = buf; - char *size_str = strchr(addr_str, ':'); - if (size_str == NULL) + if (!scan_int_prefix(str, &parsed_addr, &end) || *end != ':') { return false; - *size_str++ = '\0'; + } + if (!scan_int(end + 1, &parsed_size)) { + return false; + } - return scan_int(addr_str, addr) && scan_int(size_str, size); + *addr = parsed_addr; + *size = parsed_size; + return true; } bool @@ -65,30 +97,24 @@ scan_addr_size_name(const char *str, uint32_t *size, const char **name) { - if (str == NULL || addr == NULL || size == NULL || name == NULL) + if (str == NULL || addr == NULL || size == NULL || name == NULL) { return false; + } - char buf[512]; - if (strlen(str) >= sizeof(buf)) - return false; - - strcpy(buf, str); - - char *addr_str = buf; - char *size_str = strchr(addr_str, ':'); - if (size_str == NULL) - return false; - *size_str++ = '\0'; + uint32_t parsed_addr; + uint32_t parsed_size; + const char *end; - char *name_str = strchr(size_str, ':'); - if (name_str == NULL) + if (!scan_int_prefix(str, &parsed_addr, &end) || *end != ':') { return false; - *name_str++ = '\0'; - - if (!scan_int(addr_str, addr) || !scan_int(size_str, size)) + } + if (!scan_int_prefix(end + 1, &parsed_size, &end) || *end != ':' || end[1] == '\0') { return false; + } - *name = str + (name_str - buf); + *addr = parsed_addr; + *size = parsed_size; + *name = end + 1; return true; } diff --git a/cskburn/tests/test_utils.c b/cskburn/tests/test_utils.c new file mode 100644 index 0000000..73760e4 --- /dev/null +++ b/cskburn/tests/test_utils.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include + +#include "utils.h" + +#define CHECK(expr) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #expr); \ + return false; \ + } \ + } while (0) + +static bool +test_scan_int(void) +{ + uint32_t value = 0; + + CHECK(scan_int("0", &value) && value == 0); + CHECK(scan_int("4294967295", &value) && value == UINT32_MAX); + CHECK(scan_int("0xabcdef", &value) && value == 0xABCDEF); + CHECK(scan_int("0X100000", &value) && value == 0x100000); + CHECK(scan_int("010", &value) && value == 10); + CHECK(scan_int(" 123", &value) && value == 123); + + CHECK(!scan_int(NULL, &value)); + CHECK(!scan_int("1", NULL)); + CHECK(!scan_int("", &value)); + CHECK(!scan_int("-1", &value)); + CHECK(!scan_int(" -1", &value)); + CHECK(!scan_int("4294967296", &value)); + CHECK(!scan_int("0x", &value)); + CHECK(!scan_int("123junk", &value)); + CHECK(!scan_int("123:456", &value)); + + return true; +} + +static bool +test_scan_addr_size(void) +{ + uint32_t addr = 0; + uint32_t size = 0; + + CHECK(scan_addr_size("0x0:4194304", &addr, &size)); + CHECK(addr == 0 && size == 4194304); + CHECK(scan_addr_size("0X100000:0x100000", &addr, &size)); + CHECK(addr == 0x100000 && size == 0x100000); + CHECK(scan_addr_size("4096:8192", &addr, &size)); + CHECK(addr == 4096 && size == 8192); + CHECK(scan_addr_size("000010:000020", &addr, &size)); + CHECK(addr == 10 && size == 20); + + CHECK(!scan_addr_size(NULL, &addr, &size)); + CHECK(!scan_addr_size("1:2", NULL, &size)); + CHECK(!scan_addr_size("1:2", &addr, NULL)); + CHECK(!scan_addr_size("1", &addr, &size)); + CHECK(!scan_addr_size(":1", &addr, &size)); + CHECK(!scan_addr_size("1:", &addr, &size)); + CHECK(!scan_addr_size("1:2:3", &addr, &size)); + CHECK(!scan_addr_size("1:-2", &addr, &size)); + CHECK(!scan_addr_size("4294967296:1", &addr, &size)); + CHECK(!scan_addr_size("1:4294967296", &addr, &size)); + + addr = 11; + size = 22; + CHECK(!scan_addr_size("1:invalid", &addr, &size)); + CHECK(addr == 11 && size == 22); + + return true; +} + +static bool +test_scan_addr_size_name(void) +{ + uint32_t addr = 0; + uint32_t size = 0; + const char *name = NULL; + + CHECK(scan_addr_size_name("0x0:4194304:factory.bin", &addr, &size, &name)); + CHECK(addr == 0 && size == 4194304 && strcmp(name, "factory.bin") == 0); + CHECK(scan_addr_size_name("0X100000:0x6EC00:C:\\firmware\\dsp.bin", &addr, &size, + &name)); + CHECK(addr == 0x100000 && size == 0x6EC00); + CHECK(strcmp(name, "C:\\firmware\\dsp.bin") == 0); + CHECK(scan_addr_size_name("1:2:output:part.bin", &addr, &size, &name)); + CHECK(strcmp(name, "output:part.bin") == 0); + + char long_arg[700] = "0:1:"; + memset(long_arg + 4, 'a', sizeof(long_arg) - 5); + long_arg[sizeof(long_arg) - 1] = '\0'; + CHECK(scan_addr_size_name(long_arg, &addr, &size, &name)); + CHECK(name == long_arg + 4); + CHECK(strlen(name) == sizeof(long_arg) - 5); + + CHECK(!scan_addr_size_name(NULL, &addr, &size, &name)); + CHECK(!scan_addr_size_name("1:2:file", NULL, &size, &name)); + CHECK(!scan_addr_size_name("1:2:file", &addr, NULL, &name)); + CHECK(!scan_addr_size_name("1:2:file", &addr, &size, NULL)); + CHECK(!scan_addr_size_name("1:2", &addr, &size, &name)); + CHECK(!scan_addr_size_name(":2:file", &addr, &size, &name)); + CHECK(!scan_addr_size_name("1::file", &addr, &size, &name)); + CHECK(!scan_addr_size_name("1:2:", &addr, &size, &name)); + CHECK(!scan_addr_size_name("1:invalid:file", &addr, &size, &name)); + CHECK(!scan_addr_size_name("4294967296:1:file", &addr, &size, &name)); + + addr = 11; + size = 22; + name = "unchanged"; + CHECK(!scan_addr_size_name("1:invalid:file", &addr, &size, &name)); + CHECK(addr == 11 && size == 22 && strcmp(name, "unchanged") == 0); + + return true; +} + +int +main(void) +{ + if (!test_scan_int() || !test_scan_addr_size() || !test_scan_addr_size_name()) { + return 1; + } + puts("utils parser tests passed"); + return 0; +}