Skip to content

Watchdog is a minimal and thread safe C library for tracking memory allocations, reallocations, and frees to detect memory leaks, buffer overflows, and double frees.

License

Notifications You must be signed in to change notification settings

ragibasif/watchdog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

77 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Watchdog


Email LinkedIn GitHub YouTube

Watchdog wraps dynamic memory functions (malloc, calloc, realloc, free) and tracks all memory activity at runtime:

  • Memory Leak Detection
  • Buffer Overflows / Out-of-Bounds Access
  • Double Free Detection
  • Thread Safe
  • Verbose Logging with Optional File Output
  • Minimal Integration – Just One Header and One C File

YouTube - Stop Memory Leaks In C With Watchdog

#define WATCHDOG_ENABLE
#include "watchdog.h"

#include <stdio.h>
#include <stdlib.h>

int main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) {

    bool enable_verbose_log  = true;
    bool log_to_file         = false;
    bool enable_color_output = true;

    w_init(enable_verbose_log, log_to_file, enable_color_output);

    int *buffer = malloc(sizeof *buffer * 1024);
    free(buffer);

    return EXIT_SUCCESS;
}

./docs/demo_0.gif

Why I Built It

When I first started learning C and low-level programming, I barely understood pointers, let alone used them with confidence. Coming from a Python background, the whole process felt intimidating. But I was determined.

At the time, I had just finished a Computer Architecture course and was wrapping up an Operating Systems class. I enjoyed these classes far more than anything I had done in Python or web development. Seeing "how the sausage is made" didn't put me off; it made me hungry for more. Computer Architecture introduced me to assembly, but that felt too low-level for my taste. I wanted to build something useful in a reasonable amount of time.

Yes, we have all heard the horror stories about C's "footguns" and lack of safety. Rather than scare me off, they made me curious. C's simplicity and its explicit control over memory made learning it super fun.

For a while, I rode the high of learning a new language. I churned out countless "Hello, World!" programs in my minimal but efficient Neovim setup. I use Neovim by the way. Then I tried writing a real program and I found myself in a pit full of segmentation faults, buffer overflows, invalid memory accesses, leaks, and a blizzard of compiler errors and warnings.

I knew about tools like AddressSanitizer and Valgrind, and I did try them. But as a beginner, I found their output overwhelming. I wanted something cleaner, friendlier, and more approachable. I couldn't find something that of that kind, so I built my own.

These days, I do use Valgrind and AddressSanitizer. But I'm glad I took the time to create Watchdog. The project taught me a lot about C, memory management, and computer architecture.

Usage

Installation

Include watchdog.h and watchdog.c in your project.

Then #include watchdog.h in a source/header file and pass flag -DWATCHDOG_ENABLE to the CFLAGS of your build system to enable the debugger or add #define WATCHDOG_ENABLE to a file.

Defaults

Verbose logging is on by default, log to file is off by default, and color output is off by default.

static bool verbose_log  = true;
static bool log_to_file  = false;
static bool color_output = false;

To customize the defaults, pass appropriate boolean to w_init.

bool enable_verbose_log  = true;
bool log_to_file         = false;
bool enable_color_output = true;

w_init(enable_verbose_log, log_to_file, enable_color_output);

Enabling log_to_file will direct log output to a log file named watchdog.log. Color output is turned off if log_to_file is enabled regardless of the enable_color_output variable value.

If enable_verbose_log is set to false, only errors will be logged.

Examples

Code samples are located in a dedicated examples/ folder.

Malloc

void malloc_example(void) {

    size_t count  = 5;
    int   *buffer = malloc(sizeof *buffer * 5);
    for (size_t i = 0; i < count; i++) {
        buffer[i] = -(i - count) * count;
    }
    for (size_t i = 0; i < count; i++) {
        printf("%d ", buffer[i]);
    }

    putchar('\n');
    free(buffer);
    buffer = NULL;
}

./docs/malloc.gif

Realloc

void realloc_example(void) {
    short *buffer = malloc(34222);
    buffer        = realloc(buffer, 2342);
    buffer        = realloc(buffer, 2342342);
    buffer        = realloc(buffer, 2);
    buffer        = realloc(buffer, 10);
    buffer        = realloc(buffer, 0);
    free(buffer);
    buffer = NULL;
}

./docs/realloc.gif

Calloc

void calloc_example(void) {
    // Demonstrates correct usage of calloc
    size_t count  = 5;
    int   *buffer = calloc(count, sizeof *buffer);
    for (size_t i = 0; i < count; i++) {
        printf("%d ", buffer[i]); // should output 0
    }
    putchar('\n');
    free(buffer);
    buffer = NULL;
}

./docs/calloc.gif

Free

void free_example(void) {
    // Demonstrates correct usage of free
    size_t         count   = 5;
    short int     *buffer0 = malloc(sizeof *buffer0 * count);
    unsigned char *buffer1 = calloc(count, sizeof *buffer1);
    long double   *buffer2 = realloc(buffer2, sizeof *buffer2 * count);
    free(buffer1);
    buffer1 = NULL;
    free(buffer2);
    buffer2 = NULL;
    free(buffer0);
    buffer1 = NULL;
}

./docs/free.gif

Leak Check

void leak_example(void) {
    unsigned long long int *buffer = malloc(sizeof *buffer * 20);
    // intentionally not calling free
    // watchdog will detect this and report it
}

./docs/leak_check.gif

Overflow Check

void overflow_example(void) {
    char *buffer = malloc(sizeof *buffer * 10);
    strcpy(buffer, "This will overflow"); // out-of-bounds write
    // of buffer overflows will be detected with using canary values
}

./docs/overflow_check.gif

Double Free Check

void double_free_example(void) {
    char **buffer = malloc(sizeof *buffer * 20);
    free(buffer);
    free(buffer); // triggers a double-free error
}

./docs/double_free_check.gif

Invalid Free Check

void invalid_free_example(void) {
    float *buffer;
    free(buffer); // will trigger an error since buffer wasn't allocated
}

./docs/invalid_free_check.gif

License

This project is licensed under the MIT License.

FAQ

Does Watchdog work in production builds?

No. It's intended for development/debug builds.

Does it detect stack overflows?

No. It only monitors dynamic memory (heap).

What systems does it support?

POSIX-compliant systems (Linux, macOS). Windows support is limited (for now).