C No Evil



Your mortal enemy would like to ship a really great product in a week or two. Towards the goal of maximally delaying this product, you may inject a single C preprocessor definition into one of their header files. What is it? Keep in mind that anything which stops the project from compiling will be rapidly detected and therefore does not meet the goal.

Here are a few examples to get started with (some my own, some from a Reddit thread today).

#define FALSE 1
#define FALSE exit()
#define while if
#define goto
#define struct union
#define assert(x)
#define volatile
#define continue break
#define double int
#define long short
#define unsigned signed

Have fun.

Update: Someone on hacker news posted a link to The Disgruntled Bomb, which is based on the same idea. Nice!


24 responses to “C No Evil”

  1. It seems to me the prototypes are just similar enough that this might go unnoticed:

    #define crypt strcpy

    Some compilers famously would continue to compile correctly all functions that had a single return statement at the very end of the body if you did this, but of course return statements in the middle of the body would break:

    #define return

    Function rand() is in stdlib.h, so you can try your luck:

    #define TRUE (rand())

  2. I appreciate the gist of the challenge/question, but to have a shot at answering something meaningful, you’d need to see the code you’re trying to damage.

    Changing “volatile” to whitespace when there is no use of volatile doesn’t do much damage.

    Go ahead & #define “goto” to whitespace in my code – won’t do a thing…

    Not trying to be a downer, just trying to make it more intellectual / challenging, that’s all. Maybe that’s pointless, since technically a program could be an empty main(), and thus virtually immune to any of these attacks.

  3. The goal should be for the injection to be undetected by compiler and runtime for a significant period, to work the changes through distribution channels. Optimize on that.

  4. #define NULL ((void*)1)

    This will be particularly bad if you can get *everything* including *some* of the dev’s kernels built using it.

  5. I came up with this one a while ago:

    #define if(x) if(((x) != 0) ^ ((random() % 1000000 == 0))

    In short, it makes every if statement in the program have about a one-in-a-million chance of flipping. A debugger won’t show any changes (other than the obvious one of the code flow going where it shouldn’t be), and stepping through the code a second time is unlikely to show the problem again.

    If you place this in a header which is included everywhere but which nobody pays attention to, I think this could survive for *years* without detection, causing continual low-level havoc.

  6. #define fopen(f,m) fopen(f, m “b”)

    This can mess up Windows code, but has no effect at all on Unix, of course (which means, if my past jobs are any indication, that none of the devs will find it early).

  7. Thanks folks, these are great.

    I think the ones that introduce non-deterministic, low-probability failures are probably the most evil.

  8. In response to JR:

    #define free(p)

    Secret memory leak is more stealthy than secret double free ๐Ÿ™‚

  9. The following one could probably be looked at directly by the adversary without him catching on:

    #define memcpy(dest, source, n) bcopy((void*)dest, (void*)source, n)

    Frustratingly deterministic take on the random if failures, with extra heisenbug power:

    #define if(x) if(((x) != 0) ^ ((__LINE__ % 100 == 0))

    Related to Henry’s malloc trick:

    #define bzero(x,y) bzero((x)-1,y+1)

    These would be good for adding exploits that would likely go unnoticed until after shipping:

    #define strncpy(d,s,n) strcpy(d,s)
    #define random(x) 42

    (think password hashes and authentication tokens with the second one… for extra hard to detect evil,
    replace with ‘random(x) % 100’ to make it appear to generate random numbers, while giving you a small space to brute force)

  10. I’m no C programmer, but my favourite in C++ is

    #define for new int; for

    I guess the C equivalent would be

    #define for malloc(sizeof(int)); for

  11. Anything that only comes into play when the DEBUG macro is defined? Or if that isn’t available, put it in a system header that’s only included by a few files. Whatever is done, it should result in a small number of critical failures to give the least information about where it is.

  12. come on guys, gotta be truly sneaky and subtle here. if you’re using stuff like #define TRUE FALSE it won’t cause any appreciable delay. anyone who finds that statement in the header file will instantly see that its incoherent nonsense and remove it. thats no better than just inserting random compile time errors.

    what you need to do is introduce subtle bugs that look fine to a programmer reading the code without taking much time to examine it.

    the secret memory leak tricks are pretty good, but the preprocessor statement should be stealthy in its own right. if you #define free(p) then you’ll get your leak but you’ll also get it fixed kinda quickly because defining anything as whitespace is obviously weird and suspicious.

    i’d go with something that looks innocent but isn’t. it should look like a naive mistake, something a programmer did to save a few keystrokes without realizing how bad of an error it is.

    #define atoi(*char str) toInt(char str)

    or something of that nature. it looks totally passable, it really might have been an attempt by an experienced Java programmer to alias atoi to the function name he remembers more readily. its wrong though. the argument is a reference but the alias takes a value, it will never work correctly.

  13. #define main main(int argc, char *argv[], char *envp[]) { extern int time(int *), abort(), other_main(); return (time(0) % 10000000) ? other_main(argc, argv, envp) : abort(); } int other_main

  14. I reckon an experienced C/C++ programmer would track down anything quite quickly, especially if he’s been tipped off about the possibility of sabotage (or is the paranoid type! a.k.a. experienced).

    I also reckon we just have to assume the coding is done in a simple environment as simply hovering the mouse over anything that’s been #defined will be given away.

    The more evil examples above most probably won’t show up until the product is out there, but the aim of the exercise was to delay the release in the first place!

    Anyway, here’s my submission:

    #define stderr stdout

  15. A lot of fun submissions, but you guys are going about it all wrong. I’d use one of those dream-machine devices from “Inception” and plant the following idea into management’s subconscious mind:

    “Use C++”