The code below is in C. When given a set of input numbers, it skips the first number, and prints the rest.
main(i)
{
if(~scanf("%d",gets(&i)))
printf("%d\n",i),main();
}
I would like to know, how does this code work?
EDIT: For those of you, who think it
does not work
http://www.ideone.com/cENzy
This code is not legal C.
mainmust take either zero, two, or three parameters. One parameter is not a legal option.getsscribbles over the stack. Quite frankly, it’s a miracle if this works at all – undefined behavior abounds!With that said, let’s look at the typical way C code is compiled on x86 to understand how this works. First,
main(i)is an old, K&R-style declaration. It is interpreted asint main(int i), but does not set up a true prototype – so future calls tomainwon’t have their arguments typechecked. Recall that on x86, arguments are passed by pushing them to the stack just before calling the target function. So if we have the wrong number of arguments, it won’t crash (assuming you’re on this kind of ABI!), but simply give bogus data.Also note that on x86, the stack grows downward – as you call functions, the current stack pointer decreases. This means that if you corrupt memory above one of these parameters, you’ll be corrupting memory belonging to the calling function, and might not notice until you return.
Now let’s look at the flow of execution.
gets(&i)is executed first, and (assuming the compiler ignores the type mismatch!) obtains a line of text, and stores it onto the stack overwriting the caller’s stack frame! This assumes a stack that grows downward in memory; on upward-growing stacks this will, depending on the length of the string, overwrite the return address ofgetsand probably crash.Although
getsgrabbed a line of text, this text will be ignored and discarded. This is because the return value ofgets, which is&i, will be passed toscanf. Soscanfreads an integer and stores it ini. No problem.scanfthen returns1, which is binary negated to some negative nonzero value, which is true, soprintfthen prints the value. The comma operator then serves to recursively call main again, with the wrong number of arguments (the argument will typically be initialized with some bogus value), which acts as a loop.Note that after
scanfreturns, a newline remains in the input unconsumed, sogetsdeals with that next time around. Also note that when EOF occurs,scanfwill return EOF (0xFFFFFFFF), which will be logically negated to 0.mainwill then return, and promptly crash because its caller’s stack has probably been overwritten bygets.All in all, a neat hack, but highly dependent on undefined behavior. Please don’t imitate this in real code.