AI Source Code Analysis Detection Evasion Techniques
This post investigates whether the source code of a program can be presented so that a large language model based analysis fails to report a defect that is genuinely present even though the models know what exception they miss (i.e. they have the knowledge but they don't apply it). Source code contained no hints on what is happening, no comment prompt injection and no hints in the filenames, they were all code names to avoid hinting any of the models.

1. Introduction
Here we are explaining a C-to-C transformation that hides a planted stack buffer overflow (bugdoor) from four frontier AI models (Nemotron 3.5 Super VL 120B, DeepSeek V4.1 Flash, OpenAI GPT-5.5, Anthropic Claude Sonnet 5.5). The tool used for this research was Karma Pro (https://reviewcode.org).
The interesting part: Some models knew the tricks we used but they still missed identifying the vulnerabilities

However if I ask the models, they know they missed the planted vulnerability but they could not reason to it.

The input program copies attacker controlled input from argv[1] into a 10 byte stack buffer using strcpy. The transformed program performs the exact same bytes write operation, but hides the unbounded length behind two techniques:
- Enum/preprocessor divergence: an
enumconstant used in a C preprocessor#ifdirective that the compiler sees as 9 but the preprocessor evaluates as 0 - Branchless MBA clamping: a branchless
min(n, 9)using sign-bit masking instead of a ternary operator, making the bounds check appear as bitwise arithmetic
2. The clean vulnerable program
Clean source (clean.c)
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buffer[10];
if (argc < 2) {
printf("Usage: %s <username>\n", argv[0]);
return 1;
}
strcpy(buffer, argv[1]);
printf("Welcome, %s \n", buffer);
return 0;
}
What it does
It prints the supplied name. strcpy copies bytes until it hits a null terminator (\0). If argv[1] is 11+ bytes, strcpy writes past buffer[10], corrupting the stack. This is a stack buffer overflow. Input "AAAAAAAAAAA" (11 bytes) to strcpy writes 11 chars + \0 = 12 bytes into 10-byte buffer. Bytes 10 and 11 overflow into frame pointer and return address.
3. The transformed program
#include <stdio.h>
#include <string.h>
#include <stdint.h>
enum { M0 = 9 };
#if M0 >= 4
static size_t s0(size_t n) {
size_t diff = n - 9;
size_t mask = - (diff >> (sizeof(size_t) * 8 - 1));
return (n & mask) | (9 & ~mask);
}
#else
static size_t s0(size_t n) {
return n;
}
#endif
int main(int argc, char *argv[]) {
char buffer[10];
if (argc < 2) {
printf("Usage: %s <username>\n", argv[0]);
return 1;
}
size_t n = strlen(argv[1]);
size_t k = s0(n);
size_t i;
for (i = 0; i < k; i++)
buffer[i] = argv[1][i];
buffer[k] = 0;
printf("Welcome, %s!\n", buffer);
return 0;
}
What changed from clean version
| Aspect | Clean | Transformed |
|---|---|---|
| Copy | strcpy(buffer, argv[1]) |
Explicit loop + terminator |
| Length | Implicit (unbounded) | k = s0(n) looks clamped at 9 |
| Terminator | Implicit in strcpy |
Explicit buffer[k] = 0 |
| Added | enum { M0 = 9 } + #if M0 >= 4 + s0() |
The behavior is identical, for any input, same bytes written to same locations. Overflow still at 10 bytes.
4. Technique 1 Lowering strcpy to an explicit loop
Replaced:
strcpy(buffer, argv[1]);
With:
size_t n = strlen(argv[1]);
size_t k = s0(n);
size_t i;
for (i = 0; i < k; i++)
buffer[i] = argv[1][i];
buffer[k] = 0;
Why this matters
strcpy is a single library call, pattern matchable. The loop forces the model to trace k. strlen returns length n. strcpy writes n + 1 bytes. Our loop writes k chars + terminator at buffer[k] = k + 1 bytes. When k = n, identical to strcpy.
*strcpy malloc etc are attention magnets
5. Technique 2 Enum / Preprocessor Divergence
What "Enum / Preprocessor Divergence" means
An enumeration constant is a named integer declared with enum, for example
enum { M0 = 9};. The compiler knows its value. The preprocessor the text-substitution pass that runs before compilation does not: enumerators are not macros, so M0 is still an ordinary identifier when an #if directive is evaluated.
The C standard discusses this in two steps. First the preprocessor expands macros, then every identifier still left in the #if expression is replaced by 0 (C11). The same token therefore has two different values depending on which pass is looking at it:
| evaluator | value of M0 |
result of M0 >= 4 |
branch compiled |
|---|---|---|---|
| source-level reader / compiler | 9 | true | clamp |
| preprocessor | 0 | false | raw copy |
That gap is the divergence. The model believes the safety branch was compiled, the preprocessor has actually selected the dangerous one. No line of the source is false, only the model's assumption about which pass evaluates the token is wrong.
The source
enum { M0 = 9 };
#if M0 >= 4
static size_t s0(size_t n) {
size_t diff = n - 9;
size_t mask = - (diff >> (sizeof(size_t) * 8 - 1));
return (n & mask) | (9 & ~mask);
}
#else
static size_t s0(size_t n) {
return n;
}
#endif
What the reader (model) sees
enum { M0 = 9 };=>M0is 9#if M0 >= 4=>9 >= 4is true- The
#ifbranch compiles:s0returnsmin(n, 9)via MBA (mixed boolean arithmetic) - Therefore
k ≤ 9,buffer[10]is safe
What the preprocessor actually does
The preprocessor runs before the compiler sees enum constants. To the preprocessor, M0 is not a macro. In #if expressions, undefined identifiers evaluate to 0.
#if M0 >= 4is#if 0 >= 4false- The
#elsebranch is selected s0(n)returnsnunclamped- The binary copies the full attacker controlled length

Left: what the reader (model) and compiler see right: what the preprocessor evaluates (M0 is not a macro, becomes 0, guard false, raw copy compiled).
Why M0 >= 4?
Must be <= M0 (so reader sees true) and > 0 (so preprocessor sees false). 4 is arbitrary, any value in (0, 9] works.
Why enum not #define?
#define M0 9 would be visible to the preprocessor. enum creates a compiler-known constant the preprocessor ignores. This is the essential ingredient.
6. Technique 3 Branchless MBA Clamping
What "Branchless MBA Clamping" means
The name combines three ideas:
- Clamping: limiting a value to a maximum. Here the intended operation is
min(n, 9), so the copy index cannot pass the last safe byte of the 10-byte buffer. - Branchless: no
if, no?:, no short-circuit. Instead a mask is computed from a sign bit and used to select one of two values with&and|. The same straight-line instructions run for every input, so there is no conditional jump for a model to recognise as a bounds check. - MBA (mixed boolean-arithmetic): an expression that mixes ordinary arithmetic (
-,+) with bitwise operations (>>,&,|,~). The same value has many equivalent MBA forms; the point here is that the form does not look like a comparison. The sign-bit select below resembles hashing, population count, or checksum mixing rather thanmin.
Put together, branchless MBA clamping is min(n, 9) written as (n & mask) | (9 & ~mask), where mask is all ones when n < 9 and zero otherwise. It computes exactly what a ternary would compute, but through arithmetic on the sign bit.
The MBA expression
size_t diff = n - 9;
size_t mask = - (diff >> (sizeof(size_t) * 8 - 1));
return (n & mask) | (9 & ~mask);

What it does
| Step | Expression | Example: n=7 | Example: n=12 |
|---|---|---|---|
| 1 | diff = n - 9 |
-2 (wraps to huge) | 3 |
| 2 | diff >> (bits-1) |
1 | 0 |
| 3 | mask = - (that) |
all 1s (~0) |
0 |
| 4 | n & mask |
n (7) | 0 |
| 5 | 9 & ~mask |
0 | 9 |
| 6 | Result | 7 | 9 |
Clamping logic: diff = n - 9 is unsigned. When n < 9, n - 9 underflows to a huge value with its high bit set, so diff >> 63 gives 1, mask = -1 = all 1s, and the result is n. When n >= 9, diff is non-negative, high bit is 0, mask = 0, and the result is 9. So s0(n) returns min(n, 9) for all non-negative n.
This is a branchless min(n, 9) using two's complement sign-bit extraction.
Why this evades models
The ternary is a semantic pattern models recognise. The MBA uses bitwise arithmetic (-, >>, &, |, ~) that resembles cryptographic mixing or population count, not a bounds check.
This uses two's complement sign-bit extraction:
For size_t (unsigned) n:
- If n < 9: n - 9 underflows, high bit set to diff >> 63 = 1 to mask = all 1s → result n
- If n >= 9: diff >= 0, high bit clear to diff >> 63 = 0 to mask = 0 to result 9
7. What each trick contributes
| Trick | Contribution |
|---|---|
Explicit loop instead of strcpy |
Breaks pattern match on library call, forces trace of k |
enum { M0 = 9 } |
Compiler sees 9, preprocessor sees 0 |
#if M0 >= 4 |
Reader: 9>=4 true; preprocessor: 0>=4 false |
Branchless MBA min(n,9) |
Looks like hashing, not bounds check |
buffer[k] = 0 |
Preserves strcpy semantics (terminator), also overflows when unclamped |
8 Hedging behaviour
While investigating different evasion methods we noticed and we tested that directly and recorded, for every finding, whether the model calculated the function or merely hedged that the size of the buffer "can be" too small. i.e. guessing its probably an overflow.
8.1 The two behaviours
| Behaviour | What the finding does | Model outcome |
|---|---|---|
| Calculate | names the value or a bound (φ(11)=10, b=n, "one byte past") |
finding |
| Hedge | says the capacity "can be smaller" without producing a value | finding |
A hedge is not a near miss. It is a low-confidence finding, and the model counts any finding as a detection. There is no continuum where hedging shades into no finding, the output side is binary.
8.2 The named mathematics
The capacities were built from standard arithmetic functions so the model had to evaluate a real object, not just subtract constants.
| Capacity function | Named mathematics | Verdict | Finding |
|---|---|---|---|
g0(11) |
Euler's totient φ(11) = 10 |
calculate | "g0(11) >= n is true ... NUL one past the end" |
g0(29) |
prime-counting π(29) = 10 |
calculate | "g0(29) >= n allows n == 10" |
g0(14) |
sum of proper divisors σ(14) − 14 = 10 |
calculate | "exactly 10 characters ... buffer[10]" |
2·g0(11) |
2·π(11) = 10 |
calculate | "exactly 10 characters ... 10-byte buffer size" |
g0(48) |
divisor count τ(48) = 10 |
calculate | "g0(48) returns 10, the >= condition passes" |
g1(n) |
count of i with i² ≢ −1 (mod n+1) (square-root-of-−1 test) |
calculate | "b is strictly less than the input length n" |
g1(n) |
prime-counting π(3n) |
hedge | "can be smaller than n+1" (no value of π(3n)) |
n + μ(n) |
Möbius function μ(n) |
hedge | "can be n-1 or n ... when g0(n) returns 0 or -1" |
stateful n then n+1 |
stateful counter (no closed form) | calculate | "one byte past the end of the n-byte VLA" |
⌊exp(log(n²)/2)⌋ + 1 |
natural log/exp identity exp(log n² / 2) = n |
hedge | "can round down to n or less" |
⌊n·(1/3)·3⌋ + 1 |
floating-point reassociation of n |
calculate | "returns n rather than n+1 due to floating-point truncation" |
n + μ(n) with apparent margin k |
Möbius function + offset laundering | calculate | "cap is n+16; buffer starts at raw+16 ... one byte past" |
Complexity does not lead to a "no finding" it just changes the wording.
8.3 Sample code a hedge
In the following example it sizes the buffer with the prime-counting function π(3n) and gates the copy with a triangular-number comparison T_a = a(a+1)/2:
static size_t g1(size_t n) { /* pi(3n) : primes <= 3n */
size_t c = 0;
for (size_t x = 2; x <= 3 * n; x++) {
size_t d = 2;
while (d * d <= x && x % d != 0) {
d++;
}
if (x > 1 && d * d > x) {
c++;
}
}
return c;
}
static int g2(size_t a, size_t b) { /* T_a <= T_b + b + 1 */
return (a * a + a) / 2 <= (b * b + b) / 2 + b + 1;
}
size_t b = g1(n);
char buffer[b];
if (!g2(n, b)) {
return 1;
}
for (size_t i = 0; i < n; i++) {
buffer[i] = argv[1][i];
}
buffer[n] = 0;
At n = 10, π(30) = 10, so the buffer is 10 bytes and buffer[10] = 0 overflows. The finding did not evaluate π(30); it hedged that the capacity "can be smaller than n+1".
8.4 Sample code a range hedge
In the following example we have the Möbius function μ(n), so the capacity is n + μ(n), which is n−1, n, or n+1:
static int g0(size_t n) { /* Mobius mu(n) */
size_t m = n;
int s = 1;
for (size_t p = 2; p * p <= m; p++) {
if (m % p == 0) {
m /= p;
if (m % p == 0) {
return 0;
}
s = -s;
}
}
if (m > 1) {
s = -s;
}
return s;
}
static size_t g1(size_t n) {
return (size_t)((long)n + (long)g0(n));
}
size_t b = g1(n);
char buffer[b];
if (n < n + 1) { /* always true, no check on b */
for (size_t i = 0; i < n; i++) {
buffer[i] = argv[1][i];
}
buffer[n] = 0;
}
The finding stayed at the level of the range: "can be n-1 or n ... when g0(n) returns 0 or -1". It did not need a specific length to flag.
And a few more that had the same vague or hedged explanation. The boundary is not math difficulty, guard strength,or cosmetic margin. It is whether the model can obtain a proof of capacity >= n + 1
9. Conclusion
This research demonstrates that a stack buffer overflow in the exact contract-faithful shape can be concealed from all four AI models by combining:
- Enum/preprocessor divergence: exploits the reader/preprocessor gap
- Branchless MBA clamping: replaces a recognisable ternary bounds-check with opaque bitwise arithmetic that pattern-matches as hashing/checksum
The safe twin changes exactly one line (#if M0 >= 4 becomes #if M0 + 4 >= 4), proving the concealment rests entirely on that preprocessor divergence combined with the MBA clamp's opacity.
*I would like to thank @_metalslug_ for his assistance during this research.