1

In the manual about functions of buffer-using given at http://man7.org/linux/man-pages/man3/setbuf.3.html, a piece of code below is declared as invalid. However, when I try that on my machine, things go well:

 #include <stdio.h>

   int
   main(void)
   {
       char buf[BUFSIZ];
       setbuf(stdin, buf);
       printf("Hello, world!\n");
       return 0;
   }

The reason given by that age is :

You must make sure that the space that buf points to still exists by the time stream is closed, which also happens at program termination.

I can't see any mistake in that piece of code. And it also goes well too. Is it right or not? Can any one explain it to me?

################################################2

as @Lingxi have answered below, the buffer will be destroyed after the main function return. But stdin survive.

But, is there a default buffer left for the stdin ? And also, can we assume that the stdin is a stream, and there is some ways to destroyed or close that stream?

walkerlala
  • 1,599
  • 1
  • 19
  • 32

4 Answers4

2

The error message has explained it quite clearly. In your code snippet, buf is destroyed when main returns, but the lifetime of stdin spans beyond main. During the time period after main has returned but before stdin is closed, you would have an opened stdin with an invalid buffer. This may cause problems sometimes. As an example, what do you think would happen if there is a global class object whose destructor uses stdin for input?

You may also want to have a look at this question.

Community
  • 1
  • 1
Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • I get your point. But isn't there a default buffer left for use for the`stdin`? considered that it's so special – walkerlala May 30 '15 at 02:39
  • @walkerlala: No. Even if it wanted to use one, there is absolutely no way for a typical C implementation to detect that the lifetime of the buffer you gave it has ended. – R.. GitHub STOP HELPING ICE May 30 '15 at 02:43
  • `stdin` does not validate its buffer and automatically fall back to another if the validation failed. To guarantee the validity of the buffer is the responsibility of the programmer. – Lingxi May 30 '15 at 02:43
  • @Lingxi: And unless you have a really heavy/slow C implementation with more complex implementation of automatic storage than a simple stack, it **can't** validate. – R.. GitHub STOP HELPING ICE May 30 '15 at 02:52
2

This is an example of undefined behavior. Undefined behavior is not a magic error message that pops up and tells you your program is wrong. It means your program is dangerous. The worst thing that can happen with UB is exactly what happened to you -- no visible symptoms at all. This is because the bug goes undetected, and could later lead to extremely dangerous things. For example if the read buffer is refilled during the exit code that runs after main returns (atexit handlers, C++ dtors, stdio file shutdown that happens at exit, etc.) then the return address of some other function on the stack could be clobbered, and this could lead to arbitrary code execution vulnerabilities.

When documentation for a C interface tells you you can't do something, trust it. Testing to see if it "works" is not a meaningful activity unless you're trying to figure out how to exploit such bugs.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • A little more irrelevant question: Can I view `stdin` and `stdout` same streams as other normal file streams? And is there some way to destroy it or close it? – walkerlala May 30 '15 at 02:49
  • `fclose(stdin)` is valid, but mildly dangerous since any subsequent use of a function that implicitly uses `stdin` (such as `getchar` or `scanf`), or of course explicit use of it, results in more undefined behavior. Same for `stdout` and `stderr`. – R.. GitHub STOP HELPING ICE May 30 '15 at 02:53
  • @R. Good point: "the worst .." I'd just put a stronger focus on "no **visual** synptoms". There might be servere problems in the background. – too honest for this site May 30 '15 at 03:22
1

The reason this code fails is simply because the buffer is declared inside the main function, when this function ends since the buffer is local to the main function and in the stack it gets freed. This causes the atexit's stream flushing and closing to not comply with the setbuf function requirement of having a valid buffer.

Hope this helps http://en.cppreference.com/w/c/language/main_function

Luis Yanes
  • 347
  • 2
  • 13
1

Point is, main() is actually neither the first, nor the last code executed for your program. There is some "startup" code which calls main() after doing some processing and cleans up after main() returns.

One step of this cleanup is actually flushing buffers and closing files which have not been closed by the appication (these include stdin etc.). So, if you leave main(), the buffer is not yours anymore, but the cleanup might still want to access it. Just think of having the buffer given to someone else, who put something else inside. Anything can happen then, as you have no idea what he's done with memory.

Simply put: The term used in the C standard for "anything can happen" is "undefined behaviour". And take both phrases literally. @R. pointed that out quite well. Think of a car where the brakes don't work, but you get no notification and drive 200km/h (yes - Autobahn;-) downhill.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • That is a good analogy. I checked some documents just now. But what I am kinda confused about is the point **main() is actually neither the first , nor the last code executed for your program**, which you point out. I do know that some functions like `exit()`,,`terminate()`,,`abort()` will or may be executed after the `main()` function return. But I know nothing about what happen before the `main()` function (except the preprocess period). Can you give me more information about that? – walkerlala May 30 '15 at 05:01
  • @walkerlala: 1) `abort()`, exit() are actually called from the regular program (terminate is unfamilar - non-standard?), so they are called somewhat from main or any function called from main(), but as break the calling chain, so any allocated memroy on the stack is also lost (they do not return). Simply put, both will do their own jobs and the jump to the code which called main() _as if_ main() had returned. Just search for "_startup" or `crt.c` for examples of such code examples (they very likely will not work for you; just as an example. ... – too honest for this site May 30 '15 at 14:23
  • ... Two things done before main(): clear the memory of all globally allocated, uninitialized variables. These are required by the standard to be 0/NULL. Another might be to parse the command line for arguments, split them on spaces and create argv/argc for the call to main(). On Embedded system, also the explicitly initialized variables are set to their values here (the variables have to be in RAM, the intial values are in ROM/Flash). So much for some basics; I suppose, if you are really interested, you might serach the web using your preferred search engine. – too honest for this site May 30 '15 at 14:27