30

I am well aware, that there will probably not be "the one most difficult to find error in c++", but I am still interested in what other people can think of / may have already encountered.

The idea for this question arose during a discussion with a friend. We agreed, that it must be rather simple to sabotage a cpp project by delierately including errors in the source code that you submit... But the best thing we could think of was to use uninitialized variables (leading to random segmentation faults at runtime). I am sure there are better ways...?!

Wanted characteristics of the faulty code:

  • must look like valid code on first sight
  • must not stop the code from compiling (too obvious)
  • if possible the error should look like it might just have been a mistake (should it ever be found)
  • the error must be grave enough to stop the software from shipping (e.g. random seg faults, logical malfunction of the code etc.)

Still, while it must be noticeable, it should not be obvious right after the submition of the code... Well, you get the idea.

Don't worry, our considerations are purely theoretical (we do not plan to sabotage any project). We simply considered this to be a nice enough thought experiment to share with others :-)

In short:

What is the most subtle way to sabotage sourcecode that might go unnoticed in a differential commit (like git) but will ultimately prevent a release of the software?

example
  • 3,349
  • 1
  • 20
  • 29
  • 1
    `cpp` is the C preprocessor. Perhaps you are asking about `c++`. – William Pursell Jan 11 '13 at 00:01
  • 14
    Oooh don't close this, don't prove yet again that SO is where we hate fun! – Matteo Italia Jan 11 '13 at 00:01
  • 2
    Link with two different boost versions, or runtimes, or… already seen it in action. – liori Jan 11 '13 at 00:03
  • @WilliamPursell yes, derived from the file-extension i often refer to c++ by "cpp". let me change the question to make it nonambiguous. – example Jan 11 '13 at 00:03
  • @Matteo Too bad the others don't agree :-( really don't see why this is "ambiguous, vague, incomplete, overly broad, or rhetorical"... – example Jan 11 '13 at 00:09
  • 1
    While I didn't personally action to have this closed, it makes sense as it fails to meet the faq's criteria on a good question: _practical, answerable questions based on actual problems that you face. Chatty, open-ended questions diminish the usefulness of our site and push other questions off the front page_ – Foggzie Jan 11 '13 at 00:17
  • @GuntherFox is it practical and answerable if I formulate a concrete question? "What is the most subtle way to sabotage sourcecode that might go unnoticed in a differential commit (like git) but will ultimately prevent a release of the software?" – example Jan 11 '13 at 00:20
  • The issue isn't how the question was asked but the nature of the question itself. I believe the problem SO's community will find with it is that there's no definitive answer. It's not like someone can say "Put this in the code and there's not a single thing that will be more difficult to find or fix." There's too much room for discussion/argument and that makes this question too open-ended. – Foggzie Jan 11 '13 at 00:28
  • @GuntherFox but there are many questions along the lines of "what is the best / the fastest / most efficient way to do X"... It is opened for now though =) I will see whether it still is when I wake up. – example Jan 11 '13 at 00:33
  • 1
    True but those questions usually pertain to measurable information and algorithms where the extremes (highest/lowest/fastest/slowest) can be mathematically proven or argued definitively. Asking about a person's ability to find errors is digging into the human brain, not a computer's brain; StackOverflow members are usually only good with the latter. – Foggzie Jan 11 '13 at 00:38
  • 2
    I think this is a great way to get accustomed with the sublest of bugs that could appear in your code. – chris Jan 11 '13 at 00:50

8 Answers8

18

Classic:

#define if while
#define else

buried in some header.


Jamming on @WhozCraig's comment, also:

#define true (!!(__LINE__ % 10))

Once every ten lines true won't be so true, but the compiled program's behavior will stay consistent... to change inexplicably when something changes in the sources.

Along this line:

#define for if(__LINE__ % 10) for

#define NULL (!(__LINE__ % 10))

Or what about:

#define virtual 

this will cause serious problems - but only when dynamic dispatch is used, which may make its detection much more problematic.


In a similar fashion:

#define dynamic_cast static_cast

// Fail early, fail often
#define throw std::abort();
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • @WhozCraig: if would give a bit of Java2K feeling to the project :) ; another classic one for C would be `#define true false`, but I'm not sure if the C++ preprocessor allow you to redefine `true` and `false` (since they are values recognized by it). – Matteo Italia Jan 11 '13 at 00:02
  • 1
    @WhozCraig make it mod 100000 so it's almost impossible to reproduce – David Brown Jan 11 '13 at 00:03
  • 1
    Then there's `#define else else if (0)` . clearly we don't do this with Matteo's example; that'd be silly =P – WhozCraig Jan 11 '13 at 00:12
  • Really like the behaviour of the "define true ..." and "#define virtual". Only drawback is, that they stand out if anybody should check the code commit (or alternatively: if anybody should find this line it is obvious what happened...) – example Jan 11 '13 at 00:15
  • A lot of these would be found right away though, and several would probably break the build (`virtual`) – Mooing Duck Jan 11 '13 at 00:52
  • @MooingDuck, `override` for the win, am I right? – chris Jan 11 '13 at 00:52
  • @chris: I was thinking "function declared but not defined" linker error in the abstract base class, but `override` would also highlight the problem. – Mooing Duck Jan 11 '13 at 00:54
  • At least this answer has the side effect that it could brainwash all of the people trying to find it before they succeed, since it reeks of undefined behaviour. – chris Jan 11 '13 at 00:55
  • As a note, I _think_ Clang makes these easy to find. – Mooing Duck Jan 11 '13 at 01:14
16

Not too obvious:

if (foo =! foobar)

And we can add a trick to get rid of compiler warnings:

 if ( (i =! 3) && (j==1))
perreal
  • 94,503
  • 21
  • 155
  • 181
  • Though I've never seen this (until now) I can only imagine what a bear it is to track down, especially burried in a complext if-else-if cluster. I had to glance at the code twice before I picked up on it. – WhozCraig Jan 11 '13 at 00:20
  • and it looks like an innocent slip too :) – perreal Jan 11 '13 at 00:22
  • Love, that this looks so innocent but still might ruin a lot of code (it is an assignment after all!). Too bad that good compilers warn about this (see comments to GuntherFox' answer). – example Jan 11 '13 at 00:42
  • 1
    @example, updated a little to trick the compiler – perreal Jan 11 '13 at 00:47
  • @perreal i dont understand the if (foo =! foobar) and if ( (i =! 3) && (j==1)), could you explain it to me ?? – Computernerd Jan 11 '13 at 01:02
  • 2
    @Lim, That's an assignment, `foo = (!bar)`, i.e., foo gets the negated value of foobar cast to boolean. – perreal Jan 11 '13 at 01:03
  • @perreal so foo is implicit cast to bool? – Computernerd Jan 11 '13 at 01:08
  • @Lim, foobar is cast to bool, and the `not` of this cast is cast back to the type of foo. – perreal Jan 11 '13 at 01:11
9

I was once held up for most of a month because in release builds, sorting our CArray (By Microsoft as part of MFC) would randomly segfault, but debug builds were fine. We replaced it with a std::vector and the issue was solved. It wasn't until months later that someone informed me that CArray does not use the elements assignment operator and instead uses memcpy (source). This clearly corrupts any contained objects with a nontrivial assignment operator, but it's a standard container so everyone assumes it's safe. So if one replaces std::vector with CArray in a few key places...

As a note, Microsoft says not to use the MFC containers and to use the STL containers instead now.

Community
  • 1
  • 1
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • 1
    Wow, that's pure evilness by the author(s). – chris Jan 11 '13 at 01:05
  • @chris By _Microsoft_ and shipped as a key part of the _MFC_ library. – Mooing Duck Jan 11 '13 at 01:06
  • Wasn't sure if that's what you meant, but wow. Anyway, chances are that there weren't 50 people developing that one class :p – chris Jan 11 '13 at 01:07
  • Only vaguely related, while trying to find a source, I stumbled across [this post](http://forums.codeguru.com/showthread.php?391319.html) where the author argues that the ATL container classes are superior to STL. Then Ronald Laeremans (Acting Product Unit Manager of the Vsual C++ Team) replies by saying they only exist for backwards compatability. – Mooing Duck Jan 11 '13 at 01:09
  • Now that's pretty funny. It does wonders for the people arguing anything not to use the standard containers. – chris Jan 11 '13 at 01:18
5

I would say that, by far, the most frustrating thing for me has been using = instead of ==. For example:

while(foo = bar) {}

instead of

while(foo == bar) {}

Essentially, anything that causes the code to function improperly instead of crash really gets me to banging my head against a wall.

Honorable mentions:

  • Using the wrong mathematical operator - + / *
  • Similar to the = vs ==, mistaking & for && or | for ||.
  • Premature optimization in something like vector<bool> (Read Here if you're all like "What?")
  • Having two or more classes with the same name and using the wrong one.
Foggzie
  • 9,691
  • 1
  • 31
  • 48
  • 3
    But any modern compiler would produce a warning about the assignment in the conditional... – Kerrek SB Jan 11 '13 at 00:07
  • 1
    @KerrekSB agreed. the smart ones even squelch that warning if you've taken the time to burry your assignment in an additional set of enclosing parens, regardless of whether you use the result for an eval against a third operand. so `if (x=func())` warns, but `if ((x = func()))` or its intended use, `if ((x=func()) == val)` gives no such warnings. – WhozCraig Jan 11 '13 at 00:15
4

Perhaps a shameless thievery of this question, but I think it fits pretty well in this category.

If you have any hardcoded byte strings, all of the same length (for example, something you might use with networking), you can take the opportunity to disguise it:

const unsigned char someBytes[] = "text\0abc123";

could, with a small switch, become:

const unsigned char someBytes[] = "text\0123abc";

The difference is that the first one has 12 characters, but the second only has 10 characters, due to the octal literal in the middle. If the situation arises, this would most undoubtedly be aggravating to track down.

Community
  • 1
  • 1
chris
  • 60,560
  • 13
  • 143
  • 205
  • why does the octal literal in the middle do to the character array – Computernerd Jan 12 '13 at 01:00
  • @Lim, Octal literals in strings take the form `\0###`, where # is from 0-7. Thus, the second eats up the 0, 1 and 2 as part of the octal literal, and the string becomes shorter. If you have something that always expects the same length of string, it will read past the end of what you give it, causing undefined behaviour. – chris Jan 12 '13 at 02:57
  • @Lim, No problem. I'd never even seen one in a string before that other question came up. – chris Jan 12 '13 at 03:59
3

Have not been a victim of it yet, but implicit conversions can lead to some bad things. Look at this:

class Foo {
public:
    Foo(int a, OtherClass* b = NULL);
};

Now (without an explicit keyword) every method expecting a Foo by value/const reference will also accept an int!!!

lethal-guitar
  • 4,438
  • 1
  • 20
  • 40
2
struct
{ int foo
char bar
}


while (foobar != 10);
{
     //do something here
 } 

1) Forgetting to put a ; after a structure OR put a ; after a WHILE loop

randn() //user created function
rand()  //library function

2) Naming a function which has a similar name to a predefined function

Computernerd
  • 7,378
  • 18
  • 66
  • 95
2

Not really an error, but I do this somewhere in a random source file when I find it annoying when release builds are slower than debug builds. :)

#ifdef NDEBUG
namespace {
    struct foo {
        foo() { sleep(rand() % 4); }
    } bar;
}
#endif