3
\$\begingroup\$

This was given to me by a friend of mine, it prints out 0 through 9. We have been quite puzzled over this. Does anyone know why this works?

#include <iostream>

using namespace std;

void x(int a)
{
    *(&a-1) += a;
}

int main()
{
    for(int i = 0; i < 10; ++i)
    {
        x(8);
        i = 0;
        cout << i << endl;
    }
}
\$\endgroup\$
4
  • 3
    \$\begingroup\$ CodeGolf.SE is a place to post programming contests with well defined winning conditions. This would, perhaps have been better on Stack Overflow. \$\endgroup\$ Dec 12, 2011 at 23:52
  • 1
    \$\begingroup\$ @dmckee I would have thought that "Programming puzzles" in the title would reference stuff like this. Sorry :) \$\endgroup\$ Dec 13, 2011 at 10:40
  • \$\begingroup\$ Imho, this clearly belongs here, not stackoverflow, the closure reflects badly on the moderator. \$\endgroup\$ Dec 24, 2011 at 6:54
  • \$\begingroup\$ void(int a) is never printed. That function is just garbage. What is actually being printed is variable i: cout << i << endl; i is started at 0: int i = 0;, then is incremented by 1: ++i;, as long as it is less than 10. The call x(8) does nothing and is unnecessary. \$\endgroup\$
    – user10766
    Mar 10, 2014 at 0:43

2 Answers 2

10
\$\begingroup\$

First of all, the line *(&a-1) += a; is undefined behavior, so this could do anything depending on compiler version, flags used, alignment of the stars, etc.

That said, the reason why it works in your case is probably because &a-1 happens to point to the return address of x, and incrementing it by 8 causes the i = 0; to be skipped.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ oh my god... that's nasty. \$\endgroup\$ Dec 12, 2011 at 12:34
  • \$\begingroup\$ in other words a buffer overflow without the buffer (or a 1 element buffer) \$\endgroup\$ Dec 12, 2011 at 23:31
4
\$\begingroup\$

The reason this works is because your compiler passes the a parameter on the stack. Before the parameter, the code pushes the return address of the method onto the stack:

004A0008 | 00000008 ; a parameter
004A0004 | 00401200 ; return addr
004A0000 | ...

The &a part of the expression points to the address of the parameter on the stack, in this case I've used 004A0008. The a-1 then subtracts sizeof(int) from that address, giving you 004A0004. The * dereferences that address, so its value can be edited. This means that when you add 8 to the value at 004A0004, it alters the return address. When the method returns, it ends up 8 bytes ahead of where it should have been, skipping the i=0.

\$\endgroup\$
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.