#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <setjmp.h>
#include "exp_rs.h"
#include "common_allocator.h"
#define FFI_ERROR_NULL_POINTER -1
#define FFI_ERROR_INVALID_POINTER -5
static jmp_buf jump_buffer;
static int signal_caught = 0;
void segfault_handler(int sig) {
(void)sig;
signal_caught = 1;
longjmp(jump_buffer, 1);
}
void test_double_free_protection() {
printf("=== Test Double-Free Protection ===\n");
ExprBatch* batch = expr_batch_new(8192);
assert(batch != NULL);
printf("✓ Batch created at %p\n", (void*)batch);
ExprResult validity = expr_batch_is_valid(batch);
printf("Batch validity before use: status=%d, value=%.1f\n", validity.status, validity.value);
if (validity.status != 0) {
printf("Error: %s\n", validity.error);
}
assert(validity.status == 0 && validity.value == 1.0);
expr_batch_add_expression(batch, "x + 1");
expr_batch_add_variable(batch, "x", 5.0);
validity = expr_batch_is_valid(batch);
printf("Batch validity after adding data: status=%d, value=%.1f\n", validity.status, validity.value);
assert(validity.status == 0 && validity.value == 1.0);
printf("Freeing batch for the first time...\n");
expr_batch_free(batch);
printf("✓ First free succeeded\n");
validity = expr_batch_is_valid(batch);
printf("Batch validity after free: status=%d\n", validity.status);
if (validity.status != 0) {
printf("Expected error message: %s\n", validity.error);
}
assert(validity.status == FFI_ERROR_INVALID_POINTER);
printf("Attempting to free the same batch again...\n");
signal_caught = 0;
signal(SIGSEGV, segfault_handler);
if (setjmp(jump_buffer) == 0) {
expr_batch_free(batch);
if (!signal_caught) {
printf("✓ Double-free handled safely (no crash)\n");
} else {
printf("✗ FAILED: Segfault occurred on double-free\n");
exit(1);
}
} else {
printf("✗ FAILED: Segfault caught on double-free\n");
exit(1);
}
signal(SIGSEGV, SIG_DFL);
}
void test_use_after_free_protection() {
printf("\n=== Test Use-After-Free Protection ===\n");
ExprBatch* batch = expr_batch_new(8192);
assert(batch != NULL);
printf("✓ Batch created at %p\n", (void*)batch);
expr_batch_add_expression(batch, "x * 2");
expr_batch_add_variable(batch, "x", 3.0);
printf("Freeing batch...\n");
expr_batch_free(batch);
printf("✓ Batch freed\n");
printf("Attempting to clear freed batch...\n");
signal_caught = 0;
signal(SIGSEGV, segfault_handler);
if (setjmp(jump_buffer) == 0) {
int result = expr_batch_clear(batch);
if (!signal_caught) {
if (result != 0) {
printf("✓ Clear on freed batch returned error code: %d\n", result);
} else {
printf("⚠ Clear on freed batch returned success (0) - may need investigation\n");
}
} else {
printf("✗ FAILED: Segfault occurred on use-after-free\n");
exit(1);
}
} else {
printf("✗ FAILED: Segfault caught on use-after-free\n");
exit(1);
}
signal(SIGSEGV, SIG_DFL);
}
void test_null_pointer_handling() {
printf("\n=== Test NULL Pointer Handling ===\n");
printf("Testing is_valid on NULL pointer...\n");
ExprResult validity = expr_batch_is_valid(NULL);
printf("NULL pointer validity: status=%d\n", validity.status);
if (validity.status != 0) {
printf("Expected error message: %s\n", validity.error);
}
assert(validity.status == FFI_ERROR_NULL_POINTER);
printf("Testing double-free on NULL pointer...\n");
expr_batch_free(NULL);
expr_batch_free(NULL);
printf("✓ NULL pointer double-free handled safely\n");
printf("Testing clear on NULL pointer...\n");
int result = expr_batch_clear(NULL);
if (result != 0) {
printf("✓ Clear on NULL returned error code: %d\n", result);
} else {
printf("✗ FAILED: Clear on NULL returned success\n");
exit(1);
}
}
void test_invalid_pointer_detection() {
printf("\n=== Test Invalid Pointer Detection ===\n");
char fake_data[1024];
for (int i = 0; i < 1024; i++) {
fake_data[i] = (char)(i % 256);
}
ExprBatch* fake_batch = (ExprBatch*)fake_data;
printf("Testing operations on invalid pointer %p...\n", (void*)fake_batch);
ExprResult validity = expr_batch_is_valid(fake_batch);
printf("Invalid pointer validity: status=%d\n", validity.status);
if (validity.status != 0) {
printf("Expected error message: %s\n", validity.error);
}
assert(validity.status == FFI_ERROR_INVALID_POINTER);
signal_caught = 0;
signal(SIGSEGV, segfault_handler);
if (setjmp(jump_buffer) == 0) {
expr_batch_free(fake_batch);
if (!signal_caught) {
printf("✓ Invalid pointer free handled safely\n");
} else {
printf("✗ FAILED: Segfault on invalid pointer\n");
exit(1);
}
} else {
printf("✗ FAILED: Segfault caught on invalid pointer\n");
exit(1);
}
if (setjmp(jump_buffer) == 0) {
int result = expr_batch_clear(fake_batch);
if (!signal_caught) {
if (result != 0) {
printf("✓ Clear on invalid pointer returned error: %d\n", result);
} else {
printf("⚠ Clear on invalid pointer returned success\n");
}
} else {
printf("✗ FAILED: Segfault on invalid pointer clear\n");
exit(1);
}
} else {
printf("✗ FAILED: Segfault caught on invalid pointer clear\n");
exit(1);
}
signal(SIGSEGV, SIG_DFL);
}
int main() {
init_memory_tracking();
printf("\n==== Double-Free Protection Tests ====\n\n");
test_double_free_protection();
test_use_after_free_protection();
test_null_pointer_handling();
test_invalid_pointer_detection();
printf("\n==== All Protection Tests Passed! ====\n\n");
return 0;
}