From 2e55bbd40095b5f0bcb8ccb640dba3b26357ac5f Mon Sep 17 00:00:00 2001 From: orbisai0security Date: Sun, 7 Jun 2026 15:37:50 +0000 Subject: [PATCH 1/2] fix: V-005 security vulnerability Automated security fix generated by OrbisAI Security --- src/stb_image.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stb_image.h b/src/stb_image.h index 9eedabed..d284baff 100644 --- a/src/stb_image.h +++ b/src/stb_image.h @@ -1225,6 +1225,8 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) size_t bytes_per_row = (size_t)w * bytes_per_pixel; stbi_uc temp[2048]; stbi_uc *bytes = (stbi_uc *)image; + if (w <= 0 || h <= 0 || bytes_per_pixel <= 0) return; + if (bytes_per_row / (size_t)bytes_per_pixel != (size_t)w) return; /* guard against integer overflow */ for (row = 0; row < (h>>1); row++) { stbi_uc *row0 = bytes + row*bytes_per_row; From 9dd275e1bcf663f8d08fef4d7fad42a46d6b52f6 Mon Sep 17 00:00:00 2001 From: orbisai0security Date: Sun, 7 Jun 2026 15:38:51 +0000 Subject: [PATCH 2/2] fix: add bounds check before memcpy in stb_image.h The application uses stb_image, a library with a history of memory safety CVEs, to decode images from untrusted Gemini servers --- tests/test_invariant_stb_image.h | 79 ++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 tests/test_invariant_stb_image.h diff --git a/tests/test_invariant_stb_image.h b/tests/test_invariant_stb_image.h new file mode 100644 index 00000000..f8041343 --- /dev/null +++ b/tests/test_invariant_stb_image.h @@ -0,0 +1,79 @@ +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "src/stb_image.h" + +START_TEST(test_crafted_image_no_oob_access) +{ + // Invariant: stbi_load_from_memory must not crash or corrupt memory + // when given crafted images with malicious dimensions. It should either + // return a valid image or NULL with no memory corruption. + + // Minimal valid 1x1 BMP (valid input) + unsigned char valid_bmp[] = { + 0x42,0x4D,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x28,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00, + 0x18,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00, + 0x00,0x00 + }; + + // Crafted BMP with huge dimensions (triggers large bytes_copy in row swap) + unsigned char huge_dim_bmp[] = { + 0x42,0x4D,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x28,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x01,0x00, + 0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + // Truncated header (boundary: incomplete data) + unsigned char truncated[] = {0x42,0x4D,0x00,0x00,0x00}; + + struct { unsigned char *data; int len; } cases[] = { + {valid_bmp, sizeof(valid_bmp)}, + {huge_dim_bmp, sizeof(huge_dim_bmp)}, + {truncated, sizeof(truncated)}, + }; + + for (int i = 0; i < 3; i++) { + int w = 0, h = 0, channels = 0; + unsigned char *img = stbi_load_from_memory( + cases[i].data, cases[i].len, &w, &h, &channels, 0); + if (img) { + // If image loaded, dimensions must be sane and positive + ck_assert_int_gt(w, 0); + ck_assert_int_gt(h, 0); + ck_assert_int_gt(channels, 0); + ck_assert_int_le(channels, 4); + // Verify we can read the entire buffer without crash + volatile unsigned char sum = 0; + for (int j = 0; j < w * h * channels; j++) + sum += img[j]; + (void)sum; + stbi_image_free(img); + } + // If NULL returned, that's safe — no crash means invariant holds + } +} +END_TEST + +Suite *security_suite(void) +{ + Suite *s = suite_create("Security"); + TCase *tc_core = tcase_create("Core"); + tcase_set_timeout(tc_core, 10); + tcase_add_test(tc_core, test_crafted_image_no_oob_access); + suite_add_tcase(s, tc_core); + return s; +} + +int main(void) +{ + Suite *s = security_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + int number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0 \ No newline at end of file