Add unit tests verifying plugin dimension overflow and NULL buffer invariants

This commit is contained in:
Harshit-Dhanwalkar 2026-06-01 23:21:32 +05:30
parent ae46b9d7b4
commit 9c717cbd86
9 changed files with 221 additions and 103 deletions

View file

@ -4,7 +4,6 @@ LDFLAGS = -lm
LDFLAGS += -lpthread # Multi-threaded capture-render, pthreads producer/consumer
LDFLAGS += -ldl # Dynamic loading symbols
LDFLAGS += -msse4.1 # SIMD - SSE2 for the YUYV to gray conversion
# LDFLAGS += -fvisibility=hidden
SRCDIR = src
INCDIR = include
@ -14,11 +13,16 @@ OBJDIR = $(BUILDDIR)/obj
SOURCES = $(wildcard $(SRCDIR)/*.c)
OBJECTS = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SOURCES))
TARGET = $(BUILDDIR)/webcam_ascii
PLUGIN_SRCS = $(wildcard filters/*.c)
PLUGIN_TARGETS = $(patsubst filters/%.c,$(BUILDDIR)/%.so,$(PLUGIN_SRCS))
PLUGIN_SRCS = $(wildcard filters/*.c)
PLUGIN_C_SRCS = $(filter-out filters/%.h, $(PLUGIN_SRCS))
PLUGIN_TARGETS = $(patsubst filters/%.c,$(BUILDDIR)/%.so,$(PLUGIN_C_SRCS))
# Security Testing Targets
TEST_TARGET = $(BUILDDIR)/security_tests
TEST_SRC = ../tests/secutrity_test.c
FILTER_SRCS = filters/edge_detect.c filters/invert.c filters/threshold.c
.PHONY: all clean plugins
.PHONY: all clean plugins test
all: $(TARGET) plugins
@ -32,15 +36,18 @@ $(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
$(OBJDIR):
mkdir -p $(OBJDIR)
# Plugins
plugins: $(PLUGIN_TARGET)
# Plugins compilation
plugins: $(PLUGIN_TARGETS)
$(BUILDDIR)/%.so: filters/%.c | $(BUILDDIR)
$(CC) $(CFLAGS) -fPIC -shared $< -o $@.tmp
mv $@.tmp $@
plugins: $(PLUGIN_TARGETS)
# Automated Unit Tests Execution
test: $(BUILDDIR)
$(CC) -I.. $(CFLAGS) -DTESTING $(TEST_SRC) $(FILTER_SRCS) -o $(TEST_TARGET) -lcheck -lsubunit -lm -lrt -lpthread
@echo " RUNNING ALL PLUGINS INVARIANT TEST "
./$(TEST_TARGET)
$(BUILDDIR):
mkdir -p $(BUILDDIR)

View file

@ -4,11 +4,11 @@
static inline int clamp(int v) { return v < 0 ? 0 : v > 255 ? 255 : v; }
static void do_edge_boost(uint8_t *gray, int w, int h, void *ctx) {
void do_edge_boost(uint8_t *gray, int w, int h, void *ctx) {
int strength = ctx ? *(int *)ctx : 128;
// Unsharp mask: sharpened = original + (original - blurred) * strength
if (h <= 0 || w <= 0)
if (!gray || h <= 0 || w <= 0)
return;
uint8_t *tmp = calloc((size_t)w, (size_t)h);
if (!tmp)
@ -38,10 +38,13 @@ static void do_edge_boost(uint8_t *gray, int w, int h, void *ctx) {
free(tmp);
}
#ifndef TESTING
static filter_plugin_t self = {
do_edge_boost,
"edge_boost"
};
filter_plugin_t *plugin_get(void) {
return &self;
}
#endif

34
C/filters/edge_detect.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef EDGE_DETECT_H
#define EDGE_DETECT_H
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
void do_edge_boost(uint8_t *gray, int w, int h, void *ctx);
static inline uint8_t *edge_detect(uint8_t *gray, int w, int h) {
if (w <= 0 || h <= 0 || (uint64_t)w * (uint64_t)h > (uint64_t)UINT32_MAX) {
return NULL;
}
if (gray == NULL) {
return NULL;
}
size_t size = (size_t)w * (size_t)h;
uint8_t *out_buffer = malloc(size);
if (!out_buffer) {
return NULL;
}
for (size_t i = 0; i < size; i++) {
out_buffer[i] = gray[i];
}
do_edge_boost(out_buffer, w, h, NULL);
return out_buffer;
}
#endif

View file

@ -1,7 +1,7 @@
#include "plugins.h"
#include <stdint.h>
static void do_invert(uint8_t *gray, int w, int h, void *ctx) {
void do_invert(uint8_t *gray, int w, int h, void *ctx) {
int strength = ctx ? *(int *)ctx : 255;
int total_pixels = w * h;
for (int i = 0; i < total_pixels; i++) {
@ -11,10 +11,13 @@ static void do_invert(uint8_t *gray, int w, int h, void *ctx) {
}
}
#ifndef TESTING
static filter_plugin_t self = {
.process = do_invert,
.name = "invert"
};
filter_plugin_t *plugin_get(void) {
return &self;
}
#endif

31
C/filters/invert.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef INVERT_H
#define INVERT_H
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
void do_invert(uint8_t *gray, int w, int h, void *ctx);
static inline uint8_t *invert(uint8_t *gray, int w, int h) {
if (w <= 0 || h <= 0 || (uint64_t)w * (uint64_t)h > (uint64_t)UINT32_MAX ||
!gray)
return NULL;
if (gray == NULL) {
return NULL;
}
size_t size = (size_t)w * (size_t)h;
uint8_t *out_buffer = malloc(size);
if (!out_buffer)
return NULL;
for (size_t i = 0; i < size; i++)
out_buffer[i] = gray[i];
do_invert(out_buffer, w, h, NULL);
return out_buffer;
}
#endif

View file

@ -3,7 +3,7 @@
#define DEFAULT_THRESH 35
static void thresh_process(uint8_t *gray, int w, int h, void *ctx) {
void thresh_process(uint8_t *gray, int w, int h, void *ctx) {
// uint8_t thresh = (uint8_t)(ctx ? *(int *)ctx : DEFAULT_THRESH);
int thresh = ctx ? *(int *)ctx : DEFAULT_THRESH; // reads &plugin_param from main
int total_pixels = w * h;
@ -11,6 +11,7 @@ static void thresh_process(uint8_t *gray, int w, int h, void *ctx) {
gray[i] = (gray[i] > thresh) ? 255 : 0;
}
#ifndef TESTING
static filter_plugin_t self = {
.process = thresh_process,
.name = "threshold",
@ -19,3 +20,4 @@ static filter_plugin_t self = {
filter_plugin_t *plugin_get(void) {
return &self;
}
#endif

31
C/filters/threshold.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef THRESHOLD_H
#define THRESHOLD_H
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
void thresh_process(uint8_t *gray, int w, int h, void *ctx);
static inline uint8_t *threshold(uint8_t *gray, int w, int h) {
if (w <= 0 || h <= 0 || (uint64_t)w * (uint64_t)h > (uint64_t)UINT32_MAX ||
!gray)
return NULL;
if (gray == NULL) {
return NULL;
}
size_t size = (size_t)w * (size_t)h;
uint8_t *out_buffer = malloc(size);
if (!out_buffer)
return NULL;
for (size_t i = 0; i < size; i++)
out_buffer[i] = gray[i];
thresh_process(out_buffer, w, h, NULL);
return out_buffer;
}
#endif