AI Tools Compared

Finding memory leaks and buffer overflows in embedded C code presents unique challenges. Unlike desktop applications, embedded systems often run with limited memory, lack standard debugging tools, and operate in real-time constraints where memory corruption can have safety-critical consequences. AI-powered code review tools have emerged as valuable assistants for identifying these issues before they cause failures in production embedded systems.

Understanding Memory Issues in Embedded C

Embedded C code differs from application C in several ways that affect how you should approach memory safety:

Common Memory Issues in Embedded C

Consider this typical embedded code pattern that contains both a memory leak and potential buffer overflow:

#include <stdint.h>
#include <string.h>

#define BUFFER_SIZE 64
#define MAX_READINGS 100

typedef struct {
    uint32_t timestamp;
    float value;
} SensorReading;

static SensorReading* readings_buffer = NULL;
static uint16_t reading_count = 0;

int sensor_init(void) {
    // Problem 1: Memory leak - no check for NULL on repeated calls
    readings_buffer = malloc(MAX_READINGS * sizeof(SensorReading));
    if (readings_buffer == NULL) {
        return -1;
    }
    reading_count = 0;
    return 0;
}

int sensor_add_reading(float value) {
    // Problem 2: Buffer overflow - no bounds checking
    readings_buffer[reading_count].timestamp = get_tick_count();
    readings_buffer[reading_count].value = value;
    reading_count++;
    return 0;
}

void process_data(const char* input, size_t len) {
    // Problem 3: Potential buffer overflow with memcpy
    char local_buffer[BUFFER_SIZE];
    memcpy(local_buffer, input, len);  // If len > BUFFER_SIZE, overflow occurs
}

How AI Tools Help Review Embedded C Code

Modern AI assistants can analyze embedded C code for memory issues in several ways:

1. Pattern Recognition

AI tools recognize common dangerous patterns in embedded code:

2. Contextual Analysis

Good AI analyzers understand embedded-specific contexts:

3. Fix Suggestions

Beyond detecting problems, AI tools often suggest concrete fixes:

// AI-suggested fix for the sensor_add_reading function
int sensor_add_reading(float value) {
    // Added bounds checking
    if (reading_count >= MAX_READINGS) {
        return -1;  // Buffer full error
    }

    readings_buffer[reading_count].timestamp = get_tick_count();
    readings_buffer[reading_count].value = value;
    reading_count++;
    return 0;
}

// AI-suggested fix for process_data
void process_data(const char* input, size_t len) {
    char local_buffer[BUFFER_SIZE];

    // Added bounds checking
    size_t copy_size = (len < BUFFER_SIZE) ? len : BUFFER_SIZE - 1;
    memcpy(local_buffer, input, copy_size);
    local_buffer[copy_size] = '\0';  // Ensure null termination
}

Practical AI Tool Usage for Embedded Code Review

When using AI tools to review embedded C code, provide specific context:

Effective Prompt Strategy

Review this embedded C code for memory safety issues.
Context: STM32F4 microcontroller, ARM Cortex-M4, 64KB RAM
Focus on: memory leaks, buffer overflows, use-after-free
Specific concerns: interrupt handler safety, DMA buffer handling

[Insert code here]

What to Ask AI to Check

Stack Overflow Detection in RTOS Environments

Dynamic heap leaks get most of the attention, but stack overflows are often more dangerous in embedded systems because they corrupt adjacent memory silently. FreeRTOS, Zephyr, and ThreadX all provide configurable stack checking, but AI code review can identify high-risk functions before they hit a device.

Patterns that AI tools consistently flag as stack overflow risks:

// High-risk pattern: large stack allocation inside an ISR
void USART1_IRQHandler(void) {
    char buffer[512];  // 512 bytes on ISR stack — dangerous
    parse_incoming_frame(buffer, sizeof(buffer));
}

// Preferred pattern: use a global or static buffer for ISR-scope processing
static char isr_buffer[512];  // Allocated at link time, not at runtime

void USART1_IRQHandler(void) {
    parse_incoming_frame(isr_buffer, sizeof(isr_buffer));
}

When prompting Claude or ChatGPT to audit for stack usage, include the RTOS task stack sizes from your FreeRTOSConfig.h or equivalent. This gives the AI concrete numbers to evaluate against rather than reasoning about risk in the abstract.

Review this embedded C code for stack overflow risk.

Context:
- FreeRTOS 10.4, configMINIMAL_STACK_SIZE = 128 words (512 bytes)
- ISR stack is shared, 1KB total
- Target: STM32H7, Cortex-M7

Flag any function that allocates more than 256 bytes on the stack,
any ISR with local buffers, and any recursive call chains.

Claude correctly identifies risky ISR stack allocations and suggests static or DMA-coherent alternatives. ChatGPT usually catches the obvious cases but misses recursive call chains that span multiple files unless you paste all relevant functions.

Integer Overflow in Size Calculations

Integer overflow in size calculations is a subtle class of bug that causes buffer overflows while appearing safe at first glance. AI tools vary significantly in their ability to catch these.

// Dangerous: product of two uint16_t values overflows uint16_t
uint16_t rows = 300;
uint16_t cols = 300;
uint16_t buffer_size = rows * cols;  // Overflows: 90000 > 65535

uint8_t* image_buf = malloc(buffer_size);  // Allocates far less than needed
memset(image_buf, 0, rows * cols);         // Writes past allocated region

Claude correctly identifies that rows * cols overflows a uint16_t and suggests either promoting to uint32_t before multiplication or using a saturating arithmetic approach. ChatGPT catches this in isolation but misses it when the multiplication is embedded in a more complex expression. Copilot does not flag integer promotion issues without being explicitly asked.

The safe pattern for embedded size calculations:

// Safe: use size_t for intermediate calculations
size_t buffer_size = (size_t)rows * (size_t)cols;
if (buffer_size > MAX_IMAGE_BYTES) {
    return -EINVAL;  // Explicit bounds enforcement
}
uint8_t* image_buf = malloc(buffer_size);

Limitations and Verification

AI tools assist but don’t replace thorough testing:

  1. Static analysis tools like Coverity, PC-lint, or MISRA checkers remain essential
  2. Runtime tools such as Valgrind (where available) or custom memory monitors catch issues AI might miss
  3. Code reviews by experienced embedded developers catch context-specific problems
  4. Testing under realistic conditions reveals issues that static analysis cannot

Complementary Approach

Use AI as a first-pass reviewer, then apply dedicated tools:

Method Best For Limitations
AI Code Review Pattern recognition, quick scans May miss hardware-specific issues
Static Analyzers MISRA compliance, deep analysis Can produce false positives
Runtime Testing Finding actual leaks Requires instrumentation
Code Review Context understanding Time-intensive

Pricing for AI Code Review Tools

Tool Cost Best For
Claude API $3–15 per 1M input tokens Detailed analysis, security review
GitHub Copilot $10–20/month IDE-integrated, real-time
ChatGPT (GPT-4) $0.03–0.06 per 1K input tokens Fast feedback, iterative debugging
Amazon Q Developer $20/month AWS integration, focused learning
Cursor $20/month File-level security checks

For teams doing frequent embedded C reviews, flat-rate tools (Copilot, Cursor) are more cost-effective. For occasional reviews, pay-per-query API tools are cheaper.

Practical Workflow: Using AI for Embedded C Review

Step 1: Prepare Your Code Context

Provide the AI with:

Step 2: Request Focused Analysis

Review this embedded C code for memory safety issues.

Target: STM32F4 microcontroller, 192KB RAM, FreeRTOS 10.2
Focus: Memory leaks, buffer overflows, stack overflows
Specific concerns: ISR safety, DMA buffer handling

[Insert code here]

For each issue found:
1. Describe the problem
2. Explain the risk
3. Provide a corrected code snippet

Expected output: Detailed analysis with 3–8 issues identified, fix recommendations for critical items.

Step 3: Validate Against Static Analysis Results

Cross-reference AI findings with Coverity, MISRA, or PC-lint results. AI often catches logic-level issues that static tools miss, while static tools find complex analysis issues AI might overlook.

Comparing AI Assistance with Traditional Methods

Aspect AI Review Static Analyzer Code Review Runtime Test
False positives Medium High Low Low
Speed Fast (1–5 min) Very fast (seconds) Slow (hours) Slow (hours)
Logic errors Good Poor Excellent Excellent
Common patterns Excellent Excellent Good Poor
Context understanding Excellent Limited Excellent Limited

Optimal combination: AI (first pass) → Static analyzer → Code review → Runtime testing

Real-World Example: Complete Memory Safety Audit

Here’s a complete code review prompt that yields results:

// Code to review
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

#define MAX_DEVICES 32
#define BUFFER_SIZE 256

typedef struct {
    uint8_t id;
    char name[64];
    uint32_t last_update;
} Device;

static Device* devices[MAX_DEVICES];
static uint8_t device_count = 0;

int register_device(const char* name) {
    Device* dev = malloc(sizeof(Device));
    if (dev == NULL) return -1;

    strcpy(dev->name, name);  // Buffer overflow risk
    dev->id = device_count;
    dev->last_update = get_time();

    if (device_count < MAX_DEVICES) {
        devices[device_count++] = dev;
        return 0;
    }
    return -1;  // Memory leak: dev is not freed
}

void process_sensor_data(const uint8_t* data, size_t len) {
    uint8_t buffer[BUFFER_SIZE];
    memcpy(buffer, data, len);  // Buffer overflow if len > BUFFER_SIZE
}

Expected AI Analysis:

  1. strcpy() without bounds checking (HIGH severity)
  2. Memory leak when device_count >= MAX_DEVICES (MEDIUM severity)
  3. memcpy without length validation (HIGH severity)
  4. No bounds check in register_device before array access (MEDIUM severity)

Integration with Development Workflow

Add AI review to your embedded development process:

# Pre-commit hook for embedded C code review
#!/bin/bash

for file in $(git diff --cached --name-only | grep "\\.c$"); do
    claude "Review this embedded C code for memory safety issues:

    Platform: STM32F4
    Focus: leaks, overflows, ISR safety

    [$(cat "$file")]" > ".review-$(basename $file).txt"

    echo "AI review saved to .review-$(basename $file).txt"
done

Advanced Topics: ISR and DMA Safety

AI review particularly helps with interrupt-safe code:

// ISR with potential race condition
volatile uint32_t interrupt_count = 0;

void timer_isr(void) {
    interrupt_count++;  // Not protected from main thread access
}

void get_interrupt_count() {
    return interrupt_count;  // Data race
}

AI tools can identify these subtle concurrency issues and recommend fixes:

// Corrected version
volatile uint32_t interrupt_count = 0;
static uint32_t cached_count = 0;

void timer_isr(void) {
    interrupt_count++;
}

uint32_t get_interrupt_count(void) {
    __disable_irq();
    cached_count = interrupt_count;
    __enable_irq();
    return cached_count;
}

DMA transfers introduce additional hazards: the DMA controller writes directly to memory without CPU involvement, so a buffer that appears initialized can be partially overwritten mid-read. Prompting AI to audit DMA buffer handling requires specifying whether your architecture has a hardware cache that needs flushing:

Review this DMA transfer code for memory safety.
Architecture: STM32H7, Cortex-M7 with D-cache enabled
Question: Are cache invalidation and clean operations placed correctly
relative to DMA start and completion? Are DMA buffers aligned to
cache line size (32 bytes on Cortex-M7)?

Claude correctly identifies cache coherency issues on cached architectures. ChatGPT and Copilot sometimes miss the distinction between cached and non-cached regions unless the prompt is explicit.

Built by theluckystrike — More at zovo.one