20

I was running a program (valgrind, actually) on my Ubuntu machine, and had redirected both stdout and stderr to different files. I was surprised to see a short message appear on the screen -- how is that possible? How could I do that myself in a C++ program?

EDIT: Here's the command I used, and the output:

$ valgrind ./myprogram > val.out 2> val.err
*** stack smashing detected ***: ./myprogram terminated

EDIT2: Playing with it a little more, it turns out that myprogram, not valgrind, is causing the message to be printed, and as answered below, it looks like gcc stack smashing detection code is printing to /dev/tty

Chad
  • 1,750
  • 2
  • 16
  • 32

3 Answers3

16

It is not written by valgrind but rather glibc and your ./myprogram is using glibc:

#define _PATH_TTY   "/dev/tty"

/* Open a descriptor for /dev/tty unless the user explicitly
   requests errors on standard error.  */
const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_");
if (on_2 == NULL || *on_2 == '\0')
  fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);

if (fd == -1)
  fd = STDERR_FILENO;

...
written = WRITEV_FOR_FATAL (fd, iov, nlist, total);

Below are some relevant parts of glibc:

void
__attribute__ ((noreturn))
__stack_chk_fail (void)
{
  __fortify_fail ("stack smashing detected");
}

void
__attribute__ ((noreturn))
__fortify_fail (msg)
     const char *msg;
{
  /* The loop is added only to keep gcc happy.  */
  while (1)
    __libc_message (2, "*** %s ***: %s terminated\n",
            msg, __libc_argv[0] ?: "<unknown>");
}


/* Abort with an error message.  */
void
__libc_message (int do_abort, const char *fmt, ...)
{
  va_list ap;
  int fd = -1;

  va_start (ap, fmt);

#ifdef FATAL_PREPARE
  FATAL_PREPARE;
#endif

  /* Open a descriptor for /dev/tty unless the user explicitly
     requests errors on standard error.  */
  const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_");
  if (on_2 == NULL || *on_2 == '\0')
    fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);

  if (fd == -1)
    fd = STDERR_FILENO;

  ...
  written = WRITEV_FOR_FATAL (fd, iov, nlist, total);
  • Is this `./myprogram`'s glibc that is doing the printing? And it doesn't inherit the stderr/stdout of `valgrind`? – Yakk - Adam Nevraumont Jul 08 '15 at 19:49
  • 3
    You might want to add, that it's directory written to the console by opening `_PATH_TTY` which is `"/dev/tty"` on most systems, because that's the actual question. – dhke Jul 08 '15 at 19:49
  • 2
    @Yakk: Yes, `myprogram` uses glibc, so the routine is called in the context of the `./myprogram` process. Yes, it does inherit `valgrind`'s stdout and stderr -- but it doesn't write to stdout or stderr. It appears to explicitly open `_PATH_TTY`, which is probably something like `/dev/tty`. In general, a program can explicitly write to `/dev/tty`, bypassing `stdout` and `stderr`. – Keith Thompson Jul 08 '15 at 19:51
  • Ah, missed that! http://stackoverflow.com/questions/307006/catching-a-direct-redirect-to-dev-tty implies you can get around this feature with judicious use of `screen`. – Yakk - Adam Nevraumont Jul 08 '15 at 19:54
3

The message is most probably from GCC's stack protector feature or from glib itself. If it's from GCC, it is output using the fail() function, which directly opens /dev/tty:

fd = open (_PATH_TTY, O_WRONLY);

_PATH_TTY is not really standard, but SingleUnix actually demands that /dev/tty exists.

dhke
  • 15,008
  • 2
  • 39
  • 56
2

Here is some sample code that does exactly what was asked (thanks to earlier answers pointing me in the right direction). Both are compiled with g++, and will print a message to the screen even when stdout and stderr are redirected.

For Linux (Ubuntu 14):

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main( int, char *[]) {

    printf("This goes to stdout\n");

    fprintf(stderr, "This goes to stderr\n");

    int ttyfd = open("/dev/tty", O_RDWR);
    const char *msg = "This goes to screen\n";
    write(ttyfd, msg, strlen(msg));
}

For Windows 7, using MinGW:

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <conio.h>

void writeConsole( const char *s) {
    while( *s) {
        putch(*(s++));
    }
}

int main( int, char *[]) {  
    printf("This goes to stdout\n");

    fprintf(stderr, "This goes to stderr\n");

    writeConsole( "This goes to screen\n");
}
Chad
  • 1,750
  • 2
  • 16
  • 32