安卓服务自杀之谜

在把ndstub服务自启动弄好后,格蠹的小伙伴又开始配置gird服务自启动了,按照ndstub的经验来说,配置gird服务应该会比较顺利,但是没想到又踩到了另一个坑里面去,于是写下此文,帮助遇到相同问题的有缘人!

服务为何频频自杀?
dmesg为何深夜惨叫?
logcat为何屡遭毒手?
服务无法启动究竟是何人所为?
SELinux究竟是人是鬼?
究竟是道德的沦丧?还是人性的缺失?
让我们一起来走近科学……

2392与2393

格蠹的小伙伴按照ndstub的方式去配置gird后,”不小心“又踩到了另一个坑内。。。
大致情况就是系统会不停的给服务发SIGKILL,此后演变成了服务不停重启,系统不停的发SIGKILL。
隐约有种感觉,这次的事情好像大了啊!

starting service 'gird'...
03-14 03:44:07.890   148   148 W auditd  : type=1401 audit(0.0:2454): op=security_compute_sid invalid_context=u:r:gird:s0 scontext=u:r:init:s0 tcontext=u:object_r:gird_exec:s0 tclass=process
03-14 03:44:07.924  2392  2392 I gird    : gird rev. 1.0 started with pid 2392
03-14 03:44:07.923   148   148 W auditd  : type=1401 audit(0.0:2455): op=security_compute_sid invalid_context=u:r:gird:s0 scontext=u:r:gird:s0 tcontext=u:r:gird:s0 tclass=unix_dgram_socket
03-14 03:44:07.923   148   148 W auditd  : type=1401 audit(0.0:2456): op=security_compute_sid invalid_context=u:r:gird:s0 scontext=u:r:gird:s0 tcontext=u:r:gird:s0 tclass=unix_dgram_socket
03-14 03:44:07.926  2393  2393 E gird    : gird change pid with 2393
03-14 03:44:07.925   148   148 W auditd  : type=1401 audit(0.0:2457): op=security_compute_sid invalid_context=u:r:gird:s0 scontext=u:r:gird:s0 tcontext=u:r:gird:s0 tclass=unix_dgram_socket
01-01 00:09:12.642     0     0 I init    : Service 'gird' (pid 2392) exited with status 0
01-01 00:09:12.642     0     0 I init    : Sending signal 9 to service 'gird' (pid 2392) process group...

仔细看一下上面的内容,大致可以分成3个部分2392、2393、init。
2392进程起来后,由于是后台服务,所以根据代码逻辑,gird会fork出来一个子进程,创建完子进程后,父进程会退出Service 'gird' (pid 2392) exited with status 0,但是这个时候系统又发送了SIGKILL9给2392进程组,此时2392就连带着2393一块消失了。
这操作瞬间让我想到狼人杀!夜晚猎人被狼刀了,白天猎人开枪带走平民,最后狼人获得胜利!这简直就是一模一样的操作啊,本着狼人杀结束后复盘的惯例,我们也要复盘一下,看看2393它为什么会消失!

Daemon

先手动起一下gird。

rk3328_box:/ # /system/bin/gird ?
/system/bin/gird ?

发现没有问题,再测试一下gird的功能,发现是可以正常工作的。

问题来了啊,凭什么手动起,系统不发SIGKILL,当成服务启动,就会发啊!
经过上面的测试我们可以知道

  1. deamon去fork进程的时候成功了,确实出来一个2393的进程
  2. gird相关功能没有问题,应该不会导致系统发SIGKILL
  3. gird服务结束的是比较早的
  4. 2392父进程父进程退出是应该
  5. 由于系统发送SIGKILL,导致2392的一个进程组都挂掉了

那么现在就该把视野聚焦在main函数内了。看看main函数,在它里面唯一能和内核关联到一起的,就只有这个daemon了。

    if ((argc < 2) && daemon(1, 1) == -1)
    {
        syslog(LOG_LOCAL6 | LOG_ERR, "Fatal Error: Failed to create GEDU IR Control Daemon, error: %d\n", errno);
        exit(EXIT_FAILURE);
    }

但是由于2393的存在,我们可以知道daemon没有fork进程失败。但是手工起gird的时候跳过了daemon,难道通过daemon的方式起来就会有问题?
改下代码,默认就不以daemon的方式启动,加-s参数才以daemon的方式启动,再试一次。
终于没问题了。。。
不过这又是为什么,安卓怎么这么奇怪,后台服务不让用daemon的方式启动,

KillProcessGroup

通过搜索sending signal可以发现,是下面代码做出SIGKILL的动作。看这样子安卓是惯犯了,杀死这些进程,不是因为它们做了什么越界的行为,而是安卓有意为之啊。
考虑到安卓进程保活是一个难题,在这也就不详细展开讲了,对这方面有更多需求的同学,可以继续探究下去。

void Service::KillProcessGroup(int signal, bool report_oneshot) {
    // If we've already seen a successful result from killProcessGroup*(), then we have removed
    // the cgroup already and calling these functions a second time will simply result in an error.
    // This is true regardless of which signal was sent.
    // These functions handle their own logging, so no additional logging is needed.
    if (!process_cgroup_empty_) {
        LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
                  << ") process group...";
        int max_processes = 0;
        int r;
        if (signal == SIGTERM) {
            r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
        } else {
            r = killProcessGroup(proc_attr_.uid, pid_, signal, &max_processes);
        }

        if (report_oneshot && max_processes > 0) {
            LOG(WARNING)
                    << "Killed " << max_processes
                    << " additional processes from a oneshot process group for service '" << name_
                    << "'. This is new behavior, previously child processes would not be killed in "
                       "this case.";
        }

        if (r == 0) process_cgroup_empty_ = true;
    }

    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
        LmkdUnregister(name_, pid_);
    }

参考

https://source.android.google.cn/security/selinux?hl=zh-cn
https://events.static.linuxfound.org/sites/events/files/slides/abs2014_seforandroid_smalley.pdf