浅析vendor_init

浅析 VENDOR INIT

Vic.LUO@TINNO.COM

 

https://source.android.google.cn/security/selinux/vendor-init

推荐先阅读一下以上Google官方文章

 

 

定义vendor_init context,从这里可以看出,在odm和vendor分区的文件相关操作,其和vendor_init的关联性比较大

 

 

使用场景:

1.SystemProperties 属性加载相关

开机过程在load system property的时候,其都会走到该函数load_properties_from_file,随后会调用到LoadProperties,其有如下代码

 

就是当prop文件是在vendor/odm分区的时候,系统会用vendor_init的context去设置该perperity,在HandlePropertySet 函数中,会调用如下代码做selinux检查

其中source_context就是vendor_init,target_context就是要设置的属性对应的secontext(定义在property_contexts中),最后调用selinux_check_access函数来检查vendor_init对该属性的set操作是否allow

所以如果通过如下方式添加或者修改属性值

PRODUCT_PROPERTY_OVERRIDES += persist.vendor.bluetooth.modem_nv_support=true

有可能在实际操作中出现通过adb shell getprop 获取不到该值,需要注意,此时有2种可能性。

①代码中功能生效,但是adb shell getprop获取不到

针对该情况,说明属性值已经被正确的设置到系统中,只是通过adb shell getprop不能获取,原因在于shell环境的secontext是u:r:shell:s0,如果没有加上对应的selinux policy,或者有些属性本身就对shell neverallow get,那么就读取不到,譬如上文提到的

type=1400 audit(0.0:35835): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=16504 scontext=u:r:shell:s0 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0

对于vendor_default_prop而言,vendor_init有设置权限,通过以下selinux 规则可以知道

set_prop(vendor_init, vendor_default_prop)

但是对于shell context而言,其没有明确声明权限读取,而SELINXU policy中就是 权限默认是不允许的,除非主动声明(allow subject object:class operation),否则都视为不允许,所以如果没有声明,就是不被允许,也就会出现上面log中描述的权限被拒绝。

②代码中功能不生效,adb shell getprop也获取不到

针对该情况,说明该属性值在开机过程中并没有被成功设置到property service中,这个时候就要抓到开机log,一般情况下,说明vendor_init 没有权限去设置该属性,那么此时就需要添加vendor_init 对该属性的set权限的对应规则了。

 

 

2.service相关

在init中,我们可以发现如下代码

所以这里可以预估到在service相关以及on section相关的流程中,vendor_init也会有相应作用。

 

这里我们先来看一下init进程中的InitializeSubcontexts,请注意该函数的返回值std::vector<Subcontext> subcontexts; 是Subcontext的vector,不是string类型。其中

注意下,使用的是c++ 11新特新 auto结合for来使用,这里就会从paths_and_secontexts的二维数组中取出来其中一行,其中的2个元素分别为path_prefix和secontext,然后通过

subcontexts.emplace_back(path_prefix, secontext);直接将path_prefix和secontext new成一个subcontext对象,然后加入到subcontexts这个vector集合中,这里的话有2个subcontext对应,需要注意subcontext对象

New的时候会执行for函数

看完该函数我们可以发现该函数主要做了以下事情

① 创建一对socket  socket_和subcontext_socket

② 在当前进程上下文(init进程)下执行fork操作,创建子进程

③ 在子进程中,重新设置进程的context,这里就是u:r:vendor_init:s0了(同时由于paths_and_secontexts是有2个,所以这里会创建2次,这样也就最终会有2个vendor_init,也就最终导致了系统有2个vendot_init context的init进程),并且传入参数(fd,secontext,init进程的路径,以及subcontext),重新执行init(vendor_init)进程

 

Init(vendor_init)进程中会执行以下code逻辑

将context(这里就是vendor_init)以及subcontext_socket和function_map作为变量传入SubcontextProcess,subcontext_socket保存到SubcontextProcess的init_fd_中,然后就执行死循环MainLoop,也就是2个子进程已经进入了死循环,等待命令来处理的状态,那么命令从何而来?也就是我们平时怎么切入到vendor_init的上下文环境

④上述是子进程的逻辑,init主进程fork之前,会先创建socket_和subcontext_socket一对sockerpair,可以猜想到,init与vendor_init之间的通讯,很有可能就是通过他们俩,socket_保存在subcontext对象的变量中,这里要注意,2个subcontext对象的上下文还是在init进程,而非vendor_init子进程中,SubcontextProcess是运行在vendor_init子进程上下文里。

 

以上讲述了vendor_init子进程创建的过程,接下来我们就讲和subcontext紧密关联的部分code

先来看service部分(system/core/init/service.cpp),在service ParseSection的环节中

会通过service初始化传入到onrestart变量中(onrestart是一个action变量

也就是该subcontext会和位于vendor/odm 的 rc文件中的service的onrestart操作相关联,其中vendor分区rc文件对应的vendor subcontext,odm分区对应rc文件对应odm subcontext。

当service某些原因发生了重启

ReapOneProcess->service->Reap(siginfo)->onrestart_.ExecuteAllCommands()->

command.InvokeFunc(subcontext_);

注意下invokefunc函数

其中subcontext_就是onrestart传入进去的subcontext,execute_in_subcontext_则是new command时候传入的

从代码得知,当前new command应该是在AddCommand中产生的

第二种情况传入了f的话,execute_in_subcontext_默认就是false,第一种情况,如果没有传入f的话,那么会在function_map_中去查找,其最终对应的是builtins.cpp中的

其中第一个代表command,第二,三2个数字代表可传入参数个数的范围,第四个代表就是表示execute_in_subcontext_,第五个表示最终command执行的函数。

 

我们再回到InvokeFunc

①如果当前subcontext_为空,也就是action对应的rc文件是在system分区,那么直接直接调用RunBuiltinFunction(func_, args_, kInitContext);就是在init进程(u:r:init:s0)中执行该函数。

②如果subcontext_,但是execute_in_subcontext_为空(也就是function_map_中那些第四个参数为false的情况),这里会先执行subcontext->ExpandArgs,请注意ExpandArgs和③中一样,也会最终调用TransmitMessage,然后会在vendor_init(SubcontextProcess)中执行,但是通过代码可以发现,其实SubcontextProcess的ExpandArgs函数并没有真正执行什么,只是做了一些strings的检查和转换,随后在SubcontextProcess返回后,又在init中执行

RunBuiltinFunction(func_, *expanded_args, subcontext->context()) ,这里我们可以看到在init进程中执行这些函数用到subcontext->context()的地方其实很少,当前只有do_restorecon_recursive,do_installkey和do_init_user0会应到args.context

 

③subcontext_不为null和execute_in_subcontext_为true(就是command对应的rc为odm or vendor,并且在function_map中的参数为true),这种情况下会执行到

subcontext->Execute(args_)  ---->subcontext->TransmitMessage

这里可以看到,文章一开始讲的socket_在这里排上用场了,这里就直接将args参数相关的command通过socket_传输,通讯方就是socketpair的另一个,也就是传入到SubcontextProcess中的init_fd_,这样SubcontextProcess子进程MainLoop就会从ReadMessage中唤醒过来

然后解析参数,这里subcontext_command.command_case()的类型为SubcontextCommand::kExecuteCommand,原因是在subcontext->Execute中,以下代码

mutable_execute_command会通过set_has_execute_command()将subcontext_command类型设置为kExecuteCommand(这些代码都是protobuf自动生成的,在out目录),所以这里执行Runcommand

在该函数中,可以看到熟悉的init中的执行代码,RunBuiltinFunction,也就是在vendor_init中执行command,可以发现,vendor_init中设置属性(command中setprop)的操作通过

reply->add_properties_to_set()重新回传到init进程,然后在init的subcontext->Execute函数

中重新设置,当然这里的context_是vendor_init。

这里有2点值得思考

①为什么不在SubcontextProcess子进程中执行真正的设置属性的操作,而是要回传到init中去做该操作

我个人的理解是 propertyservice是运行在init父进程中,其最终设置只能在init父进程,子进程已经没有propertyservice的运行环境。

②上述操作可否不用传来传去,直接在init进程操作,过滤对应command传入到SubcontextProcess子进程?

这个我觉得应该可行

TransmitMessage里面的数据传输使用了Google Protocol Buffer格式,使用方法等可参见

https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html

https://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html

 

 

3. On section相关

上面章节讨论了service相关,该节我们来看一下on section相关,rc中,on section相关的解析函数为ActionParser,其解析主函数ParseSection实现如下

通过代码我们很明显的发现,subcontexts_和2章节中一致,这里的代码涉及到2部分,on event section 以及on property section,不管是event 还是property,最终都会生成action,只是on event section是event_trigger,而on properton是property_triggers。这里说明一下event_trigger,譬如on boot,那么event_trigger就是boot。

ParseTriggers函数就是用来解析event_trigger和property_triggers,从代码逻辑来看,可以发现一个section,可以是event_trigger或者property_triggers,或

event_trigger+property_triggers;一个event section只能有一个event_trigger+0个或者多个property_triggers,譬如(on)boot 或者(on) fs等;而property_triggers可以由多个property_trigger一起触发,譬如如下代码

on property:sys.usb.config=none && property:sys.usb.configfs=0

stop adbd

在ParsePropertyTrigger中,我们可以看到有如下逻辑

这里可以看到,如果subcontext不为空(也就是rc文件不在/system/),那么property_trigger是有合法性判断的,只有属性名字在kExportedActionableProperties或者属性是以kPartnerPrefixes开头的,才被允许进行property_trigger。

Section被解析正确后,会new一个action来保存

随后解析section的command,通过以下函数将其加入到action中

最终加入到action_managerde action队列中

 

我们来简单看一下action的执行流程

在init的main函数中

am.ExecuteOneCommand() -> action->ExecuteOneCommand(current_command_)

->action->ExecuteCommand

流程就走到了command.InvokeFunc(subcontext_),这里是不是很熟悉?对了,在上面service流程中已经讲述了整个InvokeFunc流程,这里就不累述了。

 

 

通过以上我们可以总结以下几点

①整个android系统action(command)执行入口是在init中(am.ExecuteOneCommand())。

②subcontext code的运行上下文是在init进程,SubcontextProcess 的运行上下文是在vendor_init子进程(vendor_init context)

③对于action(section command)而言,如果其定义是在vendor/odm分区,其会有subcontext关联,但是其command是否执行在vendor_init,需要看该command的execute_in_subcontext_是否为true,也就是builtin_functions中定义的第四个参数。

④对于service而言,我们可以看到do_start以及do_class_restart的execute_in_subcontext_都为false,所以service本身的执行域都是在init中,这里可以从service起来后的ppid可以看到,其都为1;但是onrestart的command话会根据③的规则选择运行的init,vendor_init进程

⑤可以看到setprop command {"setprop",{2,     2,    {true,   do_setprop}}},说明setprop命令如果subcontex存在,那么其会选择vendor_init作为运行域,但是通过Page7的分析我们可以得知,其最终还是会回传给init域去做处理,这里是我不太明白的地方,这里希望你们能够帮忙解答疑惑。

 

以上是鄙人对VENDOR_INIT相关内容进行的初步分析,其中内容可能有误,如果发现,请帮忙告知,不甚感激,谢谢。

 

 

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值