关于 Android 8.0 的 debuggerd 与 mediaextractor 进程被 kill 问题

Monkey 测试时出了 ANR , 然后 mediaextractor 进程被 kill 了, 然后大家搞不清是因为 mediaextractor 被杀所以导致 ANR 了,还是 ANR 之后 mediaextractor 被杀 , 于是这个问题终于辗转到征求我的意见了...  // MAGIC1. DO NOT TOUCH.  BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/


因为 LMK 是不至于杀 native 进程的, 所以确实有些奇怪; 对于这种莫名被杀的状况, 开了 strace 然后再 monkey 复现, 抓到如下的 syscall 队列: // MAGIC2. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/
  488   socket(AF_UNIX, SOCK_SEQPACKET, 0) = 12
488   connect(12, {sa_family=AF_UNIX, sun_path=" /dev/socket/tombstoned_crash "}, 31) = 0
488   write(12, "\0\0\0\0\0\0\0\0\350\1\0\0", 12) = 12
488 recvmsg(12, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\200\0\0\0\0\0\0\0\0\0\0\0", iov_len=12}], msg_iovlen=1, msg_control=[{cmsg_len=16, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, 
cmsg_data=[13]}], msg_controllen=16, msg_flags=0}, 0) = 12
488   fcntl64(13, F_GETFL)              = 0x1 (flags O_WRONLY)
488   fcntl64(13, F_SETFL, O_WRONLY|O_APPEND) = 0
488   openat(AT_FDCWD, "/proc/self/cmdline", O_RDONLY|O_LARGEFILE) = 14
488   read(14, "media.extractor\0aextractor\0", 127) = 27
488   close(14)                         = 0
488   --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0xb28004fc, si_syscall=__NR_gettimeofday, si_arch=AUDIT_ARCH_ARM} ---
3384  <... ioctl resumed> <unfinished ...>) = ?
3384  +++ killed by SIGSYS +++

首先是最后显示的 __NR_gettimeofday 似乎受到 seccomp 的限制, 导致了异常;
seccomp 不了解, 不过按  /system/etc/seccomp_policy/mediaextractor.policy 的内容看, 确实 gettimeofday 并未使能; 要验证也很容易, 在 mediaextractor 主函数 main_extractorservice.cpp  里面加一句 gettimeofday  调用试试, 确实会挂...    于是在 mediaextractor.policy  使能 gettimeofday , 好了~    再见

所以这补丁打起来很快, 可是 gettimeofday 的出处是哪?  为什么 syscall 队列中有 /dev/socket/tombstoned_crash 的请求? // MAGIC3. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/
既然有 tombstoned_crash 的字段, 那就看看 system/core/debuggerd 吧, 其实那句 “openat(AT_FDCWD, "/proc/self/cmdline"  的出处也指向了 debuggerd ;

其中 libdebuggerd/backtrace.cpp 的

static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {

  time_t t = time(NULL);
  struct tm tm;
  localtime_r(&t, &tm);
  char timestr[64];
  strftime(timestr, sizeof(timestr), "%F %T", &tm);
  _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
  if (process_name) {
    _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", process_name);
  }
  _LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
}

正体现了 gettimeofday 的 syscall, 这里是在打印调用栈, 比如 ANR 发生之后, systemserver 会使用 debuggerd_client  接口, 发 SIGQUIT 信号, 打印一些进程的 backtrace 到 /data/anr/trace 文件中, 查看这个 trace 里面的调用栈是 Android 多年来追查 ANR 的最典型方式: // MAGIC4. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/

----- pid 497 at 2018-06-04 01:10:31 -----
Cmd line: media.extractor
ABI: 'arm'

"mediaextractor" sysTid=497
  #00 pc 00049794  /system/lib/libc.so (__ioctl+8)
  #01 pc 0001e233  /system/lib/libc.so (ioctl+38)
  #02 pc 0004242f  /system/lib/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+170)
  #03 pc 00042529  /system/lib/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+8)
  #04 pc 00042a7b  /system/lib/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)
  #05 pc 00001121  /system/bin/mediaextractor
  #06 pc 0007795d  /system/lib/libc.so (__libc_init+48)
  #07 pc 00000f04  /system/bin/mediaextractor

可以看到, 第一行就是打印当时的系统时间 !

而这个 dump_process_header 是层层调用, 在 handler/debuggerd_handler.cpp 中通过 debuggerd_init 配置 sa_sigaction , 最终设置了 debuggerd_signal_handler, 使得被打印进程比如 mediaextractor 进程能够执行到 dump_process_header 的; // MAGIC5. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/
而这个 debuggerd_init 是 mediaextractor 进程什么时候调的?  是 linker 的时候!   这个找的可有点辛苦 ...  

bionic/linker/linker_main.cpp
/*
 * This code is called after the linker has linked itself and
 * fixed it's own GOT. It is safe to make references to externs
 * and other non-local data at this point.
 */
static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) {
  ProtectedDataGuard guard;
  // Sanitize the environment.
  __libc_init_AT_SECURE(args);

  // Initialize system properties
  __system_properties_init(); // may use 'environ'

  // Register the debuggerd signal handler.
#ifdef __ANDROID__
  debuggerd_callbacks_t callbacks = {
    .get_abort_message = []() {
      return g_abort_message;
    },
    .post_dump = &notify_gdb_of_libraries,
  };
  debuggerd_init(&callbacks);
#endif


简单来说就是打印 backtrace 会有 gettimeofday 的 syscall,  mediaextractor 进程由于有 seccomp 没有使能该权限, 所以被 kill 了 ; // MAGIC6. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/
所以更简单的确认方法就是 dumpstate 或者 debuggerd -b pid_mediaextractor , 直接就挂了  骂人



So... 这又是个谷歌原生的问题喽?  debuggerd 一跑就挂, 这看起来很 low 啊...
但是, 在 Android 8.0 之前, 似乎并不是这样;  // MAGIC7. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/
在 Android 8.0 之前的版本, debuggerd 是一直常驻在系统的:

root@lc1881phone_v2:/data/anr # ps | grep debug
root      1577  1     3972   1652  __skb_recv 00f6f0bc20 S /system/bin/debuggerd
root      1578  1     7172   2076  __skb_recv 7f89933158 S /system/bin/debuggerd64


这个时候的工作原理, 是 debuggerd 进程去 dump_backtrace, 也就是去 dump_process_header ,   就是说,  执行 gettimeofday 系统调用的是 debuggerd 进程, 不是被打印的进程,   这个时候捕获 mediaextractor  进程 syscall 的话是不会有  gettimeofday  的; // MAGIC8. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/

但是 Android 8.0 后,  这个 debuggerd 进程没有了, 取而代之是个常驻的  tombstoned 进程,  这个 tombstoned 不做 dump_backtrace 的操作, 没有细看, 可能只是处理  /data/anr  和  /data/tombstone  的文件,    真正执行 dump_backtrace 的是被打印进程 ;  所以, 只有 Android 8.0 之后 mediaextractor 进程才会因为自身进程打印调用栈而执行  gettimeofday , 然后因为加入了权限管理被禁用了 gettimeofday ;  敲打


msm8909_benz:/sdcard # ps -A | grep debug
1|msm8909_benz:/sdcard #
1|msm8909_benz:/sdcard # ps -A | grep tomb
tombstoned    1156     1    4888   1436 SyS_epoll_wait a766a658 S tombstoned



看来 Android O 的升级, 真是个有趣的事情啊! // MAGIC9. DO NOT TOUCH.  BY 冗戈微言  http://blog.csdn.net/leonxu_sjtu/


















猜你喜欢

转载自blog.csdn.net/leonxu_sjtu/article/details/80576461