浅析 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相关内容进行的初步分析,其中内容可能有误,如果发现,请帮忙告知,不甚感激,谢谢。

浅析vendor_init相关推荐

  1. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  2. 浅析Python中bytes和str区别

    本博转载自:Chown-Jane-Y的浅析Python3中的bytes和str类型 Python 3最重要的新特性之一是对字符串和二进制数据流做了明确的区分.文本总是Unicode,由str类型表示, ...

  3. 学习《Linux设备模型浅析之设备篇》笔记(深挖一)

    这篇文章既然说了是浅析,那就是跳过了一些东西,我们把这些跳过的东西给它尽可能的补回来 今天登陆 lxr.free-electrons.com 发现内核版本已经升级到3.15了,那以后都使用3.15的源 ...

  4. 学习《Linux设备模型浅析之设备篇》笔记(一)

    最近在学习Linux设备模型,前面几篇文章也是读这篇的时候遇到问题,然后为了搞清楚先转去摸索才写出来的. 当然了,刚开始是先读到<Linux那些事儿之我是Sysfs>,搞不清楚才去读的&l ...

  5. 架构周报| 浅析MySQL JDBC连接配置上的两个误区

    经典案例 \\ 浅析MySQL JDBC连接配置上的两个误区:相信使用MySQL的同学都配置过它的JDBC驱动,多数人会直接从哪里贴一段URL过来,然后稍作修改就上去了,对应的连接池配置也是一样的,很 ...

  6. 超级账本(Hyperledger Fabric)之权限管理浅析

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 超级账本(Hyperledger Fabric)之权限管理浅析 超级账本是联盟链的代表,而其相对于共链(例如比特币,以太 ...

  7. linux内核SMP负载均衡浅析

    需求       在<linux进程调度浅析>一文中提到,在SMP(对称多处理器)环境下,每个CPU对应一个run_queue(可执行队列).如果一个进程处于TASK_RUNNING状态( ...

  8. CAS、原子操作类的应用与浅析及Java8对其的优化

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:CoderBear juejin.im/post/5c7a8 ...

  9. Python标准库queue模块原理浅析

    Python标准库queue模块原理浅析 本文环境python3.5.2 queue模块的实现思路 作为一个线程安全的队列模块,该模块提供了线程安全的一个队列,该队列底层的实现基于Python线程th ...

  10. Python标准库threading模块Condition原理浅析

    Python标准库threading模块Condition原理浅析 本文环境python3.5.2 threading模块Condition的实现思路 在Python的多线程实现过程中,在Linux平 ...

最新文章

  1. 100c之36:不同鸡的数量
  2. Action访问Servlet API的三种方法
  3. 用标准dl,dt,dd标签抛弃table列表
  4. Normal-Inverse Gamma Mixture简介
  5. 【Arduino】按键按下执行不同模式程序
  6. JavaScript——判断undefined解决方案
  7. python的常量和变量_python中的常量和变量代码详解
  8. WebBrowser提交submit后界面不刷新的解决办法
  9. 您的计算机究竟如何像美味的Reuben三明治
  10. 青蒿素发现者与两外籍科学家共获诺奖
  11. matlab 冲激偶,冲激函数符号怎么念 什么是冲激函数?
  12. 希腊字母,阿拉伯字母的读音表
  13. HA高可用集群与RHCS集群套件
  14. 【转】期限结构Carry收益 期货多品种对冲模型
  15. 大话设计模式-第12章 牛市股票还会亏钱?--外观模式
  16. 爬虫爬取中国大学排名
  17. 从“二等公民”到正职,我真正理解了外包的价值
  18. dNet图像处理组件学习总结
  19. CleanMyMac X4.11苹果电脑系统清理工具
  20. Java异常处理的普遍误解

热门文章

  1. 现代通用计算机雏形是,科技知识:什么是现代通用计算机的雏形
  2. 内外兼修 高阶游戏鼠标达尔优A970黑武士评测
  3. DP转HDMI方案设计/DP转HDMI/VGA方案CS5216/CS5218/CS5262方案应用参数详细对比
  4. Capstone CS5218转接设计|DP转HDMI 4K 30HZ转接电路
  5. ImprovedGAN论文略读
  6. Element is missing end tag
  7. 【转载】阿里云域名解析将一个域名指向另一个域名
  8. Scala学习笔记(1)-基本类型归纳
  9. 随手写程序——分布拟合的卡方检验法
  10. 梦幻109鸿蒙量星太难杀,梦幻西游:维摩诘杀怪最少攻略,20分钟轻松做完!