Android Tombstone Handling

Table of Contents

1. Android Tombstone Handling

以 SIGABORT 为例, 说明如何生成 tombstone

1.1. 进程主动调用 abort()

abort() 最终会调用 tgkill(SIGABORT)

1.2. tgkill 进入 kernel

  1. 当前 user space 的上下文被保存到 kernel stack
  2. SIGABORT 保存在 pending_signals 中

1.3. 调用 signal handler

tgkill syscall 通过 ret_from_intr 返回到 userspace 前, handle_signal 会负责保证 signal handler 被调用

  1. 在 userspace stack 上保存 kernel stack 的 sigframe
  2. 当前 SIGABORT 信号被 block, 保证 signal handler 执行过程中不会再次 handle SIGABORT 信号
  3. branch 到 userspace 的 debugger signal handler

1.4. debugger

  1. 通过 socket 通知 debuggerd
  2. 将 SIGABORT 的 action 修改为 default
  3. 通过 kill(SIGABORT) 向自己再次发送 SIGABORT 信号, 但由于 handle_signal 已经将这个信号 block, 所以并不会在这里马上触发. 后面 debuggerd 部分会看到 debuggerd 会等待这个信号
  4. 调用 sigreturn

1.5. debuggerd

debuggerd 收到来自 debugger 的请求后, 会:

  1. 通过 ptrace attach 到目标进程 (attach 会隐含着发送一个 SIGSTOP)
  2. 通知目标进程继续执行
  3. 通过 waitpid 等待之前发给目标进程的 SIGSTOP
  4. 再次通过 waitpid 等待目标进程第二次收到 SIGABORT
  5. 收到第二个 SIGABORT 后会通过 ptrace 的 PT_GETREGS 等方法获得 tombstone 相关信息
  6. 将 SIGABORT 通过 ptrace 的 CONTINUE 转给目标进程, 后者会最终异常退出

1.6. sigreturn

debugger 的 sigreturn 系统调用会:

  1. 进入到 kernel
  2. 通过 sigframe 恢复 kernel stack 为最初 abort 时的 kernel stack, 最终 tombstone 打印的值均来自这个 sigframe
  3. 将 SIGABORT unblock
  4. 返回到 userspace, 由于此时有一个 pending 的 SIGABORT, 所以会先唤醒 debugger 后再返回到 userspace

1.7. 唤醒 debuggerd

debuggerd 因为第二次 SIGABORT 被唤醒, 这时目标进程的 kernel stack 中保存的是第一次 SIGABORT 的 context, 所以 debuggerd 可以用 ptrace 拿到正确的数据

1.8. 总结

box "target process"
participant debugger as db
participant  app
end box

box "debuggerd"
participant  debuggerd as dd
end box

box "kernel"
participant  kernel
end box
== 1st sigabrt ==
app -> kernel: SIGABRT

activate kernel
kernel -> kernel: save_context
note right: tombstone in kernel stack
kernel -> kernel: construct_sigframe
note right
     kernel stack empty,
     tombstone moved to sigframe
end note
kernel -> kernel: block_SIGABRT
kernel -> db: call signal_handler
deactivate kernel

activate db
db -> dd: send_request
activate dd
db -> db: wait_for_response
deactivate db

dd -> app: ptrace(ATTACH)
app --> dd: ATTACHED
dd -> db: send_response
activate db
dd -> dd: waitpid
app -> dd: SIGSTOP
dd -> dd: waitpid_for_2nd_SIGABRT
deactivate dd

db -> db: signal(SIG_DFL)
db -> db: kill(2nd SIGABRT)
db -> kernel: sigreturn
deactivate db

== 2nd sigabrt ==
activate kernel
kernel -> kernel: unblock_SIGABRT
kernel -> kernel: restore_context_from_sigframe
note right: tombstone moved to kernel stack
kernel -> dd: wakeup (SIGABRT)
activate dd
kernel -> app: ret_to_user

deactivate kernel

dd -> app: PTRACE(PT_GETREGS)
note left: got tombstone from kernel stack
app --> dd: got_regs
dd -> dd: dump_tombstone
dd -> app: PTRACE(continue)
activate app
deactivate dd

app -> app: exit (SIGABRT)
deactivate app

tombstone_handling.png

Author: [email protected]
Date: 2017-02-14 Tue 00:00
Last updated: 2023-11-20 Mon 16:08

知识共享许可协议