title: LD_PRELOAD-ed open() + __xstat() + syslog() result into EBADF
body: <p>I am on a Fedora 30 box with GLIBC 2.29 and kernel 5.2.18-200.fc30.x86_64</p>
  
  <pre class="lang-sh prettyprint-override"><code>$ rpm -qf /usr/lib64/libc.so.6
  glibc-2.29-28.fc30.x86_64
  </code></pre>
  
  <p>override.c :</p>
  
  <pre><code>#define open Oopen
  #define __xstat __Xxstat
  
  #define _GNU_SOURCE
  #include &lt;dlfcn.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;sys/stat.h&gt;
  #include &lt;syslog.h&gt;
  #include &lt;errno.h&gt;
  
  #undef open
  #undef __xstat
  
  #ifndef DEBUG
  #define DEBUG 1
  #endif
  
  #define LOG(fmt, ...)                                                          \
    do {                                                                         \
      if (DEBUG) {                                                               \
        int errno_ = errno;                                                      \
        /* fprintf(stderr, "override|%s: " fmt, __func__, __VA_ARGS__); */       \
        syslog(LOG_INFO | LOG_USER, "override|%s: " fmt, __func__, __VA_ARGS__); \
        errno = errno_;                                                          \
      }                                                                          \
    } while (0)
  
  /* Function pointers to hold the value of the glibc functions */
  static int (*real_open)(const char *str, int flags, mode_t mode);
  static int (*real___xstat)(int ver, const char *str, struct stat *buf);
  
  int open(const char *str, int flags, mode_t mode) {
    LOG("%s\n", str);
    real_open = dlsym(RTLD_NEXT, __func__);
    return real_open(str, flags, mode);
  }
  
  int __xstat(int ver, const char *str, struct stat *buf) {
    LOG("%s\n", str);
    real___xstat = dlsym(RTLD_NEXT, __func__);
    return real___xstat(ver, str, buf);
  }
  </code></pre>
  
  <p>It works in all cases I could think of, but not this one:</p>
  
  <pre class="lang-sh prettyprint-override"><code>$ gcc -DDEBUG=1 -fPIC -shared -o liboverride.so override.c -ldl -Wall -Wextra -Werror
  $ LD_PRELOAD=$PWD/liboverride.so bash -c "echo blah | xargs -I{} sh -c 'echo {} | rev'"
  rev: stdin: Bad file descriptor
  </code></pre>
  
  <p>However, if I comment out <code>syslog()</code> in favour of <code>fprintf()</code>, it works:</p>
  
  <pre><code>$ gcc -DDEBUG=1 -fPIC -shared -o liboverride.so override.c -ldl -Wall -Wextra -Werror
  $ LD_PRELOAD=$PWD/liboverride.so bash -c "echo blah | xargs -I{} sh -c 'echo {} | rev'"
  override|open: /dev/tty
  override|__xstat: /tmp/nwani_1587079071
  override|__xstat: .
  ...
  ... yada ...
  ... yada ...
  ... yada ...
  ...
  halb &lt;----------------------------- !
  ...
  ... yada ...
  ... yada ...
  ... yada ...
  override|__xstat: /usr/share/terminfo
  </code></pre>
  
  <p>So, my dear friends, how do I debug why using <code>syslog()</code> results into a <code>EBADF</code>?</p>
  
  <p>=========================================================================</p>
  
  <p>Updates:</p>
  
  <ol>
  <li>Unable to reproduce on Fedora 32-beta</li>
  <li>The following command also reproduces the same problem: 
  
  <pre><code>$ LD_PRELOAD=$PWD/liboverride.so bash -c "echo blah | xargs -I{} sh -c 'echo {} | cat'"
  </code></pre></li>
  <li>Interestingly, if I replace <code>cat</code> with <code>/usr/bin/cat</code> the problem goes away.</li>
  </ol>
  
  <p>=========================================================================</p>
  
  <p>Update: Based on Carlos's answer, I ran a git bisect on findutils (xargs) and found that my scenario was (unintentionally?) fixed with a feature addition:</p>
  
  <pre><code>commit 40cd25147b4461979c0d992299f2c101f9034f7a
  Author: Bernhard Voelker &lt;mail@bernhard-voelker.de&gt;
  Date:   Tue Jun 6 08:19:29 2017 +0200
  
      xargs: add -o, --open-tty option
  
      This option is available in the xargs implementation of FreeBSD, NetBSD,
      OpenBSD and in the Apple variant.  Add it for compatibility.
  
      * xargs/xargs.c (open_tty): Add static flag for the new option.
      (longopts): Add member.
      (main): Handle the 'o' case in the getopt_long() loop.
      (prep_child_for_exec): Redirect stdin of the child to /dev/tty when
      the -o option is given.  Furthermore, move the just-opened file
      descriptor to STDIN_FILENO.
      (usage): Document the new option.
      * bootstrap.conf (gnulib_modules): Add dup2.
      * xargs/xargs.1 (SYNOPSIS): Replace the too-long list of options by
      "[options]" - they are listed later anyway.
      (OPTIONS): Document the new option.
      (STANDARDS CONFORMANCE): Mention that the -o option is an extension.
      * doc/find.texi (xargs options): Document the new option.
      (Invoking the shell from xargs): Amend the explanation of the
      redirection example with a note about the -o option.
      (Viewing And Editing): Likewise.
      (Error Messages From xargs): Add the message when dup2() fails.
      (NEWS): Mention the new option.
  
      Fixes http://savannah.gnu.org/bugs/?51151
  </code></pre>