1

There is this exceptionally simple program in C (built with gcc on Cygwin)::

// This program is a tutorial to Stack-based overflow, that's why "gets"
    #include <stdio.h>

    void test()
    {
        char buff[4];
        printf("Some input: ");
        gets(buff);
        puts(buff);
    }

    int main(int argc, char** argv)
    {
        test();
        return 0;
    }

Now when I run this on Windows console,printf executes first and waits for the input:: enter image description here

But when I run the same exe under Cygwin terminal, it waits for the input and then printf executes::

enter image description here

Why is this difference?

EDIT:: With \n appended to the string in printf statement, the behaviour remains the same::

enter image description here

Abhineet
  • 5,320
  • 1
  • 25
  • 43
  • 4
    `printf("Some input: "); --> printf("Some input: ");fflush(stdout);` – Ajay Brahmakshatriya Sep 14 '17 at 07:02
  • 1
    The interworking of output to stdout (screen) without a newline at the end (the prompt) and the input from stdin (keyboard) is a delicate topic. It appears that the Windows and Cygwin libraries have different views on what happens. You can force the output with `fflush(stdout)`. Note that your order of presentation is not convincing; the second screen shows a recompilation of `testbuff.exe`, so to be a valid assertion, you'd have to have generated the second screen image first and the first image second. – Jonathan Leffler Sep 14 '17 at 07:02
  • @JonathanLeffler - The sequencing of images is ordered to make my doubt clear to the SO community. I assure you that the exe executed on both consoles are exactly same :) – Abhineet Sep 14 '17 at 07:06
  • @AjayBrahmakshatriya - I figured that out but what I would like to know is the reason behind it. +1 for the comment though :) – Abhineet Sep 14 '17 at 07:10
  • @JonathanLeffler - Do you mean that if I put a newline char in the `printf` statement, the behavior might change? – Abhineet Sep 14 '17 at 07:11
  • 1
    [___DO NOT___ use `gets()`, even if for casual code, it is dangerous. use `fgets()` instead.](https://stackoverflow.com/a/41383540/2173917) – Sourav Ghosh Sep 14 '17 at 07:12
  • Yes; it would change. (The behaviour would change if you added a newline to the `printf()` output.) – Jonathan Leffler Sep 14 '17 at 07:12
  • @SouravGhosh - Thanks for the warning. I am trying to figure out a Stack-based overflow exploit :) – Abhineet Sep 14 '17 at 07:13
  • stdin and stdout are by default line buffered, AFAIR. – Sourav Ghosh Sep 14 '17 at 07:14
  • The input and output streams usually have a buffering character. Some implementation of libc use line buffering while some use character buffering. The code for `printf` (actually `__vfprintf` for windows) is usually dynamically linked. So Cygwin and cmd might be using different versions of the dll and hence you are seeing different behavior. Although, waiting for input should usually flush `stdout`. Also how the contents of `stdin` and `stdout` are interleaved is dependent of the terminal implementation. – Ajay Brahmakshatriya Sep 14 '17 at 07:14
  • Please do not add text output as graphics. That's no artwork, – Gerhardh Sep 14 '17 at 07:18
  • @JonathanLeffler - I did try with the newline char, yet the behaviour remains the same. I have edited the question to include that too. – Abhineet Sep 14 '17 at 07:26
  • That's surprising to me; I don't have a good answer. – Jonathan Leffler Sep 14 '17 at 07:45
  • @JonathanLeffler - That's surprising to me that "you" don't have a good answer :-( – Abhineet Sep 14 '17 at 08:16

1 Answers1

0

So, after some reading and research, I concluded at a final theory for this behaviour.

Short Answer:: stdout is buffered, so doing a

void test()
{
    char buff[4];
    setvbuf(stdout, NULL, _IONBF, 0); // <--- setting stdout to _IONBF
    printf("Some input: ");
    gets(buff);
    puts(buff);
}

before printing anything to console or,

void test()
    {
        char buff[4];
        printf("Some input: ");
        fflush(stdout); // <--- force the stdout to flush
        gets(buff);
        puts(buff);
    }

after the printf statement will solve the issue.

Long Answer::

When I run the same exe with Bash.exe, the stdout is not buffered:: enter image description here

And the same exe with mintty.exe, the stdout is buffered:: enter image description here

Now the question is,

  • why stdout is unbuffered in one and buffered on another?

As you can see the tty output on both, bash shows as console session /dev/cons0, whereas the mintty shows as pseudo-terminal session /dev/pty0.

So,

  • how does it answer the question of unbuffered and buffered stdout?

My browsing re-re-and-re searches landed me here, stating, If I type tty in the respective Cygwin windows, I see that mintty and xterm are both pseudo-terminals whereas bash is a console session - I believe procServ uses forkpty(). So I think it boils down to Windows treating a Cygwin pty as non-interactive and thus buffering both stdout and stderr.

I am not sure, how precise this is but yes, this seems to be a practical and best explanation of this behaviour.

According to Section 7.9.13/7 of c99 states that:

At program start-up, three text streams are predefined and need not be opened explicitly - standard input (for reading conventional input), standard output (for writing conventional output), and standard error (for writing diagnostic output).

As initially opened, the standard error stream is not fully buffered; the standard input and standard output streams are fully buffered if and only if the stream can be determined not to refer to an interactive device.

*The bold emphasis is mine.

Abhineet
  • 5,320
  • 1
  • 25
  • 43