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
2 changes: 2 additions & 0 deletions Codes/Cipher/Stream/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ int main(int argc, char* argv[])
printx("Original", (const uint8_t *)data, length);

memset(buffer, 0, sizeof(buffer));
if (length > sizeof(buffer))
length = sizeof(buffer);
memcpy(buffer, data, length);

stream_encrypt(buffer, length, key, nonce, counter);
Expand Down
91 changes: 91 additions & 0 deletions tests/test_invariant_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include <check.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

/* Invariant: memcpy in stream cipher must never copy more bytes than the
* allocated buffer size. Oversized 'length' inputs must be rejected or
* truncated, never cause out-of-bounds writes. */

#define BUFFER_SIZE 256

static int run_with_length(size_t length)
{
/* Fork and exec the real binary with a crafted length argument.
* The child process exercises the actual production code path.
* We treat a crash (signal) as a test failure. */
pid_t pid = fork();
if (pid == 0) {
/* Child: redirect stdout/stderr to /dev/null */
int devnull = open("/dev/null", 1);
dup2(devnull, 1);
dup2(devnull, 2);
close(devnull);

char len_str[32];
snprintf(len_str, sizeof(len_str), "%zu", length);
/* Pass length as argument; binary reads it as the copy length */
execl("Codes/Cipher/Stream/main", "main", len_str, NULL);
_exit(127);
}
int status = 0;
waitpid(pid, &status, 0);
return status;
}

START_TEST(test_buffer_read_never_exceeds_declared_length)
{
/* Invariant: buffer reads must never exceed allocated buffer size */
size_t payloads[] = {
BUFFER_SIZE - 1, /* valid: one byte under limit */
BUFFER_SIZE, /* boundary: exactly at limit */
BUFFER_SIZE * 2, /* exploit: 2x oversized */
BUFFER_SIZE * 10, /* extreme: 10x oversized */
};
int num_payloads = (int)(sizeof(payloads) / sizeof(payloads[0]));

for (int i = 0; i < num_payloads; i++) {
int status = run_with_length(payloads[i]);

/* The process must NOT be killed by a signal (e.g. SIGSEGV, SIGABRT).
* A normal exit (any code) is acceptable; a signal death is not. */
ck_assert_msg(
!WIFSIGNALED(status),
"Process killed by signal %d for length=%zu — buffer overflow detected",
WTERMSIG(status), payloads[i]
);
}
}
END_TEST

Suite *security_suite(void)
{
Suite *s;
TCase *tc_core;

s = suite_create("Security");
tc_core = tcase_create("Core");
tcase_set_timeout(tc_core, 30);

tcase_add_test(tc_core, test_buffer_read_never_exceeds_declared_length);
suite_add_tcase(s, tc_core);

return s;
}

int main(void)
{
int number_failed;
Suite *s;
SRunner *sr;

s = security_suite();
sr = srunner_create(s);

srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);

return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}