LD_PRELOAD-ed open()+ __xstat()+ syslog()结果进入EBADF

问题描述 投票:2回答:1

我在装有GLIBC 2.29和内核5.2.18-200.fc30.x86_64的Fedora 30机器上

$ rpm -qf /usr/lib64/libc.so.6
glibc-2.29-28.fc30.x86_64

override.c:

#define open Oopen
#define __xstat __Xxstat

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <sys/stat.h>
#include <syslog.h>
#include <errno.h>

#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);
}

它在我能想到的所有情况下都有效,但不适用于此情况:

$ 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

但是,如果我以syslog()的名义注释掉fprintf(),它会起作用:

$ 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 <----------------------------- !
...
... yada ...
... yada ...
... yada ...
override|__xstat: /usr/share/terminfo

所以,亲爱的朋友,我该如何调试为什么将syslog()结果转换为EBADF

================================================ =========================>

更新:

  1. 无法在Fedora 32-beta上复制
  2. 以下命令也重现相同的问题:
    $ LD_PRELOAD=$PWD/liboverride.so bash -c "echo blah | xargs -I{} sh -c 'echo {} | cat'"
    
  3. 有趣的是,如果我将cat替换为/usr/bin/cat,问题将消失。
  4. ================================================ =========================>

    更新:基于卡洛斯的回答,我在findutils(xargs)上运行了git bisect,发现(无意间)我的场景已添加功能:

commit 40cd25147b4461979c0d992299f2c101f9034f7a
Author: Bernhard Voelker <[email protected]>
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

我在装有GLIBC 2.29和内核5.2.18-200.fc30.x86_64 $ rpm -qf /usr/lib64/libc.so.6 glibc-2.29-28.fc30.x86_64 overlay.c的Fedora 30机器上: #define open Oopen #define __xstat __Xxstat #define ...

c bash glibc syslog ld-preload
1个回答
1
投票

您覆盖的open__xstat一定不能有正在运行的进程可以看到的副作用。

[没有进程期望open__xstat关闭并重新打开编号最小的文件描述符,也不应该打开O_CLOEXEC,但这确实是syslog的工作,如果它发现日志记录套接字已失败。 >

解决方案是您必须在调用closelog之后再调用syslog,以避免任何副作用在过程中变得可见。

© www.soinside.com 2019 - 2024. All rights reserved.