Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does "Puts()" function work without arguments?

Tags:

c

I came across this piece of code on a website.

main(i)
{
  gets(&i);
  puts();
}

This code compiles and runs fine!

It gets a string as input from the user and prints it!!!!

But, my question is, how?

(note that puts() function does not contain any arguments!)

like image 775
Koshimitsu Avatar asked Apr 10 '26 06:04

Koshimitsu


2 Answers

Old versions of C had implicit types for variables and functions, and this code makes use of that and some other stuff. It also was very lax about actually returning values.

main(i) // i is implicitly an integer (the default type for old C), and normally named argc 
// int main(int i) or void main(int i)
{ // The stack (which lives in high memory but grows downward) has any arguments and
  // probably the environmental variables and maybe even other (possibly blank/filler)
  // stuff on it in addition to the return address for whatever called main and possibly
  // the argument i, but at this point that could either be on the stack just under the
  // return address or in a register, depending on the ABI (application binary interface)


// extern int gets(int) or extern void gets(int)
// and sizeof(int) is probably sizeof(char *)
 gets(&i); // By taking the address of i even if it wasn't on the stack it will be pushed to
           // it so that it will have an address (some processors have addressable registers
           // but they are rarely used by C for many reasons that I won't go into).


           // The address of i is either also pushed onto the stack or put into a register
           // that the ABI says should be used for the first argument of a function, and
           // and then a call is made to gets (push next address to stack; jump to gets)

           // The function gets does what it does, but according to the ABI there are
           // some registers that it can do whatever it wants to and some that it must
           // make sure are the same as they were before it was called and possibly one
           // or more where it is supposed to store a return value.
           // If the address of i was passed to it on the stack then it probably would be
           // restricted from changing that, but if it was passed in a register it may
           // have just been luckily left unchanged.
           // Another possiblity is that since gets returns the string address it was
           // passed is that it returns that in the same location as the first argument
           // to functions is passed.  

 puts();   // Since, like gets, puts takes one pointer argument it will be passed this
           // this argument in the same way as gets was passed it's argument.  Since we
           // were somehow lucky enough for gets to not overwrite the argument that we
           // passed to it and since the C compiler doesn't think it has anything new to
           // pass to puts it doesn't change any registers' values or do too much to the
           // stack.  This leaves us in the situation where puts is called with the stack
           // and registers set up in the same way as they would be if it were passed the
           // address of i, just the same as gets.

   // The gets call with the stack variable's address (so an address high on the stack)
   // could have left main's return address intact, but also could have overwritten it
   // with garbage.  Garbage as main's return address would likely result in a jump to
   // a random location (probably not part of your program) and cause the OS to kill the
   // program (possibly with an unhandled SIGSEGV) which may have looked to you like a
   // normal exit.  Since puts appended a '\n' to the string it wrote and stdout is
   // line buffered by default it would have been flushed before returning from puts
   // even if the program did not terminate properly. 
}
like image 94
nategoose Avatar answered Apr 11 '26 18:04

nategoose


That's because you just called gets() with the correct parameter and the call to puts() finds the stack unchanged. On CPUs with many registers this will probably break unless gets() doesn't use the register which contains the first argument. Compile with optimization enabled, that might be enough.

If you put any function call between the two, it will break too.

A clean way with the same amount of code would be:

puts(gets(&i));
like image 29
Aaron Digulla Avatar answered Apr 11 '26 19:04

Aaron Digulla



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!