Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cs.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,11 @@ extern void *kern_os_realloc(void *addr, size_t nsize);

static void *cs_kern_os_calloc(size_t num, size_t size)
{
return kern_os_malloc(num * size); // malloc bzeroes the buffer
size_t alloc = num * size;
if (num && size != alloc / num) {
return NULL; // overflow check
}
return kern_os_malloc(alloc); // malloc bzeroes the buffer
}

cs_malloc_t cs_mem_malloc = kern_os_malloc;
Expand Down
59 changes: 59 additions & 0 deletions tests/integration/test_poc.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,64 @@ static void test_overflow_set_reg_mem_n(void)
return;
}

/**
* \brief Clone of cs.c::cs_kern_os_calloc()
* So we can run it in the CI.
*/
static void *clone_cs_kern_os_calloc(size_t num, size_t size)
{
size_t alloc = num * size;
if (num && size != alloc / num) {
return NULL; // overflow check
}
return malloc(alloc);
}

#define VALID_N 10

static void test_integer_overflow(void)
{
// Invariant: Multiplication of num * size must not overflow before allocation
// We test this by ensuring the function handles boundary cases safely

// Test cases: exploit case, boundary values, valid input
struct {
size_t num;
size_t size;
const char *description;
} test_cases[] = {
// Valid normal input
{ VALID_N, VALID_N, "valid normal input" },
// Exploit case: multiplication overflows to small value
{ SIZE_MAX, 2, "overflow to small allocation" },
// Another overflow case
{ 1ULL << (sizeof(size_t) * 4), 1ULL << (sizeof(size_t) * 4),
"midpoint overflow" },
// Zero allocation (edge case)
{ 0, SIZE_MAX, "zero num" }
};

int num_cases = sizeof(test_cases) / sizeof(test_cases[0]);

for (int i = 0; i < num_cases; i++) {
printf("num = 0x%zx size = 0x%zx\n", test_cases[i].num,
test_cases[i].size);
// The security property: if multiplication would overflow,
// the function should handle it safely (return NULL or abort)
void *result = clone_cs_kern_os_calloc(test_cases[i].num,
test_cases[i].size);

// For valid inputs, we expect non-NULL (or NULL is also acceptable
// if memory allocation fails for other reasons)
if (test_cases[i].num == VALID_N || test_cases[i].num == 0) {
assert(result != NULL);
free(result);
} else {
assert(result == NULL);
}
}
}

/// Signed left shift overflow when assembling a little-endian SH-DSP
/// parallel instruction word. code[3] is promoted to int and shifted
/// left by 24, which is UB whenever its top bit is set (code[3] >= 0x80).
Expand All @@ -135,6 +193,7 @@ int main()
test_overflow_cs_insn_bytes();
test_overflow_cs_insn_bytes_iter();
test_overflow_set_reg_mem_n();
test_integer_overflow();
test_ub_shift_sh_dsp_p();

return 0;
Expand Down
Loading