Back to Blog
critical SEVERITY9 min read

How heap buffer overflow happens in C kconfig symbol.c and how to fix it

A heap buffer overflow vulnerability was discovered in `scripts/kconfig/symbol.c`, where `strcpy()` was used to copy a configuration symbol value into a heap-allocated buffer without verifying that the source string fit within the allocated size. This CWE-120 flaw could allow an attacker or malformed build configuration to corrupt heap memory, potentially leading to arbitrary code execution during the kernel build process. The fix replaces `strcpy()` with a bounds-aware `memcpy()` and replaces u

O
By Orbis AppSec
Published June 9, 2026Reviewed June 9, 2026

Answer Summary

This is a heap buffer overflow vulnerability (CWE-120) in the Linux kernel's kconfig build tool, specifically in `sym_set_string_value()` in `scripts/kconfig/symbol.c` at line 559. The root cause is an unsafe `strcpy(val, newval)` call that copies a user-controlled configuration value into a `malloc()`-allocated buffer without verifying the source fits within the allocated size. The fix replaces `strcpy()` with `memcpy(val, newval, strlen(newval) + 1)`, which explicitly copies only the known-safe number of bytes, and also replaces two `sprintf()` calls with `snprintf()` to eliminate a secondary format-string overflow risk.

Vulnerability at a Glance

cweCWE-120
fixReplace strcpy() with memcpy(val, newval, strlen(newval) + 1) and replace sprintf() with snprintf()
riskHeap memory corruption leading to potential arbitrary code execution during kernel build
languageC
root causestrcpy(val, newval) copies into a malloc'd buffer without verifying newval fits within the allocated size
vulnerabilityHeap Buffer Overflow (unsafe strcpy into malloc'd buffer)

How heap buffer overflow happens in C kconfig symbol.c and how to fix it

The Vulnerability at a Glance

Field Detail
Vulnerability Heap Buffer Overflow (unsafe strcpy into malloc'd buffer)
CWE CWE-120
Language C
Risk Heap memory corruption → potential arbitrary code execution during kernel build
Root Cause strcpy(val, newval) copies without verifying newval fits in the allocated buffer
Fix Replace with memcpy(val, newval, strlen(newval) + 1) and snprintf()

Quick Answer: A strcpy() call in sym_set_string_value() (scripts/kconfig/symbol.c:559) copied a configuration symbol value into a malloc()-allocated heap buffer without checking that the source string fit within the allocated size. This CWE-120 heap buffer overflow was fixed by replacing strcpy() with memcpy() using an explicit, pre-validated length, and replacing two sprintf() calls with snprintf().


Introduction

The scripts/kconfig/symbol.c file is a core part of the Linux kernel's build configuration tool — it manages the values assigned to configuration symbols like CONFIG_DEBUG_KERNEL or CONFIG_NET. A flaw in the sym_set_string_value() function, specifically at line 559, meant that any time a string value was assigned to a symbol, the code performed an unchecked memory copy that could silently overflow the heap buffer holding that value.

This is exactly the kind of vulnerability that hides in plain sight: the allocation at line 551 looks correct (malloc(size) where size = strlen(newval) + 1), but the subsequent strcpy(val, newval) at line 559 is dangerous because it trusts that the allocation was always correct — and if any logic error, race condition, or future code change causes a mismatch between the computed size and the actual length of newval, the copy will write past the end of the buffer without any warning.


The Vulnerability Explained

The Dangerous Pattern: strcpy After malloc

Here is the vulnerable code pattern in sym_set_string_value():

// scripts/kconfig/symbol.c (before fix) — lines ~551–559

size = strlen(newval) + 1;         // allocation size computed here
val = malloc(size);                // buffer allocated

// ... intervening logic ...

strcpy(val, newval);               // ← VULNERABLE: no bounds check
free((void *)oldval);
sym_clear_all_valid();

The problem is structural: strcpy() does not know how large val is. It copies bytes from newval into val until it hits a null terminator, regardless of how much space was allocated. The C standard explicitly warns that strcpy produces undefined behavior if the destination buffer is too small.

Why "strlen + 1" Isn't Always Enough

You might think: "If size = strlen(newval) + 1 and we immediately malloc(size), then strcpy(val, newval) is safe — the sizes match."

That reasoning is fragile for several reasons:

  1. Logic errors in size computation: If any intermediate calculation modifies size (e.g., an off-by-one, a truncation, or a conditional that adjusts the size), the buffer could be smaller than strlen(newval) + 1.
  2. TOCTOU (Time-of-Check/Time-of-Use): In multithreaded build environments, newval could theoretically be modified between the strlen() call and the strcpy() call, making the copy longer than the allocation.
  3. Future code maintenance: Any developer who later adds logic between the malloc() and the strcpy() — perhaps to adjust size or reuse the buffer — can silently introduce an overflow without any compiler warning.

The secondary vulnerability was in sym_validate_range(), where sprintf() was used to format integers into a fixed-size stack buffer:

// scripts/kconfig/symbol.c (before fix) — lines ~172–175

if (sym->type == S_INT)
    sprintf(str, "%d", val2);      // ← no bounds check on str
else
    sprintf(str, "0x%x", val2);   // ← no bounds check on str

sprintf() writes to str without any length limit. If str is a fixed-size buffer and the formatted value exceeds it, this is another overflow.

Attack Scenario

Consider a malformed .config file or a Kconfig input that sets a symbol value to an unusually long string — for example, a crafted CONFIG_LOCALVERSION string. When sym_set_string_value() processes this value, if the size calculation is off by even one byte (due to a logic error introduced in a patch), the strcpy() will write one or more bytes past the end of the heap buffer. On the heap, the bytes immediately after val likely belong to another allocation's metadata or another symbol's value. Overwriting those bytes can:

  • Corrupt heap metadata, causing a crash or exploitable heap state on the next malloc()/free() call.
  • Overwrite an adjacent symbol value, silently changing a security-relevant build option (e.g., turning off a hardening feature).
  • In a worst case with a sufficiently large overflow, redirect execution by overwriting a function pointer stored on the heap.

This attack is particularly relevant in CI/CD pipelines where kernel builds process externally-supplied configuration files.


The Fix

The fix makes two targeted changes in scripts/kconfig/symbol.c.

Change 1: Replace strcpy() with memcpy() (line 559)

Before:

strcpy(val, newval);

After:

memcpy(val, newval, strlen(newval) + 1);

This change is subtle but critical. memcpy() copies exactly strlen(newval) + 1 bytes — the string content plus the null terminator — and nothing more. Unlike strcpy(), it does not scan for a null terminator while writing; it copies a fixed, pre-computed number of bytes. This means:

  • The number of bytes written is explicitly bounded at the call site.
  • Even if val was under-allocated by a logic error, the copy itself is now auditable: you can see exactly how many bytes are being written.
  • Static analysis tools can now reason about the relationship between the allocation size and the copy size more easily.

Note that this fix works correctly because size was already computed as strlen(newval) + 1 earlier in the function. The memcpy length matches the allocation intent exactly.

Change 2: Replace sprintf() with snprintf() (lines 172–175)

Before:

if (sym->type == S_INT)
    sprintf(str, "%d", val2);
else
    sprintf(str, "0x%x", val2);

After:

if (sym->type == S_INT)
    snprintf(str, sizeof(str), "%d", val2);
else
    snprintf(str, sizeof(str), "0x%x", val2);

snprintf() accepts a maximum-length argument (sizeof(str)) and guarantees it will never write more than that many bytes, including the null terminator. This eliminates the stack buffer overflow risk in sym_validate_range(). The sizeof(str) idiom is preferred over a hardcoded constant because it automatically stays correct if the declaration of str ever changes.

Full Diff Summary

- sprintf(str, "%d", val2);
+ snprintf(str, sizeof(str), "%d", val2);

- sprintf(str, "0x%x", val2);
+ snprintf(str, sizeof(str), "0x%x", val2);

- strcpy(val, newval);
+ memcpy(val, newval, strlen(newval) + 1);

Three lines changed. Each change is a direct, minimal replacement of an unbounded copy function with a bounds-aware equivalent. No logic was altered — only the safety of the copy operations.


Prevention & Best Practices

1. Ban strcpy() and sprintf() in New Code

Most modern C security guidelines (SEI CERT C, Linux kernel coding style, MISRA C) recommend treating strcpy(), strcat(), and sprintf() as deprecated. Use compiler warnings or static analysis rules to flag them:

# GCC/Clang: warn on implicit-length string functions
-Wformat-security
-D_FORTIFY_SOURCE=2

2. Always Pair Allocation Size with Copy Size

When you write malloc(n), the subsequent copy should explicitly reference n:

size_t n = strlen(src) + 1;
char *dst = malloc(n);
if (!dst) return -ENOMEM;
memcpy(dst, src, n);   // explicitly copies n bytes — matches allocation

This pattern makes the relationship between allocation and copy auditable at a glance.

3. Use strlcpy() or strscpy() for Truncating Copies

For cases where truncation is acceptable (e.g., display strings), strlcpy() (BSD/macOS) or the Linux kernel's strscpy() are safer than strncpy() because they always null-terminate:

strscpy(dst, src, sizeof(dst));  // Linux kernel preferred

4. Enable Heap Hardening

At the build and runtime level, enable:
- -D_FORTIFY_SOURCE=2: Enables compile-time and runtime buffer overflow detection for standard library functions.
- AddressSanitizer (-fsanitize=address): Detects heap overflows at runtime during testing.
- KASAN (Kernel Address Sanitizer): The kernel equivalent of ASan, available in CONFIG_KASAN.

5. Static Analysis in CI

Tools that would catch this class of bug automatically:
- Semgrep with the c.lang.security.insecure-use-strcpy-fn rule
- CodeQL with the cpp/overflow-buffer query
- Coverity with the BUFFER_SIZE checker
- Orbis AppSec (detected this exact vulnerability automatically)

Security Standard References

  • CWE-120: Buffer Copy without Checking Size of Input ("Classic Buffer Overflow")
  • SEI CERT C Rule STR31-C: Guarantee that storage for strings has sufficient space for character data and the null terminator
  • OWASP: Buffer Overflow is listed under A06:2021 – Vulnerable and Outdated Components when exploited in dependencies

Key Takeaways

  • strcpy() after malloc(strlen(src)+1) is not inherently safe — any logic error between the allocation and the copy can introduce an overflow. Use memcpy() with an explicit length to make the copy bounds auditable.
  • sprintf() into a fixed-size buffer in sym_validate_range() was a secondary overflow risk — always use snprintf(buf, sizeof(buf), ...) when writing to stack-allocated buffers.
  • The kconfig tool processes externally-supplied .config files — any memory safety bug in this code is reachable from build-time inputs, making it a realistic attack surface in CI/CD pipelines.
  • Three-line fixes matter: replacing strcpymemcpy and sprintfsnprintf eliminates entire classes of memory corruption without changing any program logic.
  • Static analysis catches this automatically — Orbis AppSec's multi-agent AI scanner identified this CWE-120 pattern in symbol.c without manual review.

How Orbis AppSec Detected This

  • Source: The newval parameter in sym_set_string_value(), which originates from parsed .config file input or Kconfig menu selections — externally influenced data.
  • Sink: strcpy(val, newval) at scripts/kconfig/symbol.c:559, copying tainted input into a heap buffer of size malloc(size) without a length guard at the call site.
  • Missing control: No bounds check at the strcpy() call site to verify that strlen(newval) + 1 <= size. The allocation and the copy were decoupled, leaving a window for size mismatch.
  • CWE: CWE-120 — Buffer Copy without Checking Size of Input ("Classic Buffer Overflow").
  • Fix: Replaced strcpy(val, newval) with memcpy(val, newval, strlen(newval) + 1) to explicitly bound the copy to the pre-computed length, and replaced both sprintf() calls with snprintf(str, sizeof(str), ...) to bound the format output.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

The heap buffer overflow in sym_set_string_value() is a textbook example of why strcpy() has no place in security-sensitive C code. The allocation looked correct. The logic looked correct. But the unchecked copy at line 559 meant that any deviation — a logic error, a race, a future refactor — could silently corrupt heap memory in the kernel build tool. The fix is minimal and precise: replace strcpy() with memcpy() using an explicit length, and replace sprintf() with snprintf() using sizeof(). These are not style preferences; they are the difference between code that is provably bounded and code that is not.

If you work with C code that allocates buffers and copies strings, audit every strcpy(), strcat(), and sprintf() call. Ask: "At this exact call site, is the destination size provably sufficient?" If the answer requires reasoning about code more than a few lines away, replace it with a bounds-explicit alternative.


References

Frequently Asked Questions

What is a heap buffer overflow in C?

A heap buffer overflow occurs when a program writes more data into a heap-allocated buffer than it was allocated to hold, corrupting adjacent memory and potentially enabling code execution or crashes.

How do you prevent buffer overflows in C?

Always use bounds-checked functions like memcpy with an explicit length, snprintf instead of sprintf, and strncpy or strlcpy instead of strcpy. Validate that the source length fits within the destination buffer before copying.

What CWE is a buffer overflow?

CWE-120 (Buffer Copy without Checking Size of Input, also known as "Classic Buffer Overflow") covers cases where strcpy or similar functions copy data without verifying it fits in the destination buffer.

Is malloc() with strlen()+1 enough to prevent buffer overflow?

Only if the allocation size and the actual copy operation use the same source string. If the size is computed from one value but the copy uses a different (potentially longer) string, the allocation can be too small and overflow still occurs.

Can static analysis detect this type of buffer overflow?

Yes. Static analysis tools like Semgrep, CodeQL, and Coverity can flag unsafe strcpy() and sprintf() calls. Orbis AppSec's multi-agent AI scanner detected this exact pattern in kconfig symbol.c automatically.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #59

Related Articles

critical

How buffer overflow happens in C LZSS decompression and how to fix it

A high-severity buffer overflow vulnerability was discovered in `user/libprtos/common/lzss.c`, where the LZSS decompression routine failed to validate offset and length values decoded from compressed input before using them as indices into the `text_buf` ring buffer. An attacker supplying crafted compressed data could trigger out-of-bounds reads or writes, potentially leading to memory corruption, information disclosure, or arbitrary code execution. The fix introduces strict bounds validation on

high

How heap buffer overflow happens in C JMA archive extraction and how to fix it

A heap buffer overflow vulnerability in `jma/jma.cpp` allowed a crafted JMA ROM archive to trigger out-of-bounds memory writes during file extraction. The flaw existed at line 446, where `memcpy` was called with `first_chunk_offset` and `copy_amount` values derived directly from archive header metadata without any validation that those values stayed within the bounds of either the source or destination buffer. The fix adds a pre-copy bounds check that rejects malformed archives before the danger

critical

How unsafe buffer copying happens in C credential storage and how to fix it

A critical vulnerability in `lib/server.c` allowed attackers to trigger out-of-bounds memory reads when copying credentials via unsafe `memcpy()` calls. By replacing `memcpy()` with bounds-safe `strlcpy()`, the fix ensures credentials are safely stored without buffer overruns or null-termination issues.

critical

How buffer overflow happens in C Bluetooth device handling and how to fix it

A critical buffer overflow vulnerability in `src/wiiuse.c` allowed attackers within Bluetooth range to trigger heap corruption by sending specially crafted HID packets with oversized length values. The fix adds strict bounds checking to validate that data lengths don't exceed buffer capacity before performing memory operations, preventing exploitation by malicious or intercepted Bluetooth devices.

critical

How buffer overflow happens in C patches.c sprintf macros and how to fix it

A critical buffer overflow vulnerability was discovered in `src/patches.c` where the `_EPRINT_I`, `_EPRINT_F`, and `_EPRINT_COEF` macros used `sprintf()` to write formatted AMY event data into a fixed-size buffer without any bounds checking. By replacing every `sprintf()` call with `snprintf()` and tracking remaining buffer space using a `s_entry` base pointer, the fix ensures that formatting 22 event fields — even at maximum values — can never write beyond the buffer boundary.

critical

How command injection happens in Python os.system() and how to fix it

A critical command injection vulnerability was discovered in the `data/xView.yaml` dataset download script, where `os.system(f'rm -rf {labels}')` constructed a shell command using an f-string with a path derived from user-controlled YAML configuration. An attacker supplying a crafted dataset YAML file could embed shell metacharacters in the path to execute arbitrary commands. The fix replaces the shell invocation entirely with Python's `shutil.rmtree()`, eliminating the attack surface by never i