小心selinux对应用的影响

🙈 By 单雷, 赵延刚 2024-09-19

1.背景

最近有客户报告在试用NJet时,有环境完全启动不起来,启动时就会产生core,而相同的配置在其他环境没有问题。 具体现象如下:

[root@CDN119 proxy2]# ./sbin/njet -p /data/njet-bo/proxy2/ -c /data/njet-bo/proxy2/conf/njet.conf -e logs/error.log
Segmentation fault (core dumped)

这种现象比较奇怪,肯定是和该机器的某个系统设置有关。因此开发人员进行了远程调试。

2. 问题定位

2.1 定位代码

gdb调试,显示njet 在调用tcc 中的函数时,调用失败,而此时函数指针也是正常的值。同时如果在配置中去掉tcc调用,NJet也可以正常启动,因此可以初步定位core跟tcc代码有关。 img

更进一步分析,发现core出现在tcc 内部的mp_protect. 而查看的dmesg信息 errno 15也明确了这一点(参考附录 errno的解释)

img

更进一步的分析,可以看到audit日志明确了根本原因。

type=PROCTITLE msg=audit(1726625910.924:12956): proctitle=2E2F7362696E2F6E6A6574002D70002F646174612F6E6A65742D626F2F70726F7879322F002D63002F646174612F6E6A65742D626F2F70726F7879322F636F6E662F6E6A65742E636F6E66002D65006C6F67732F6572726F722E6C6F67
type=AVC msg=audit(1726625910.928:12957): avc:  denied  { execheap } for  pid=28790 comm="njet" scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=process permissive=0
type=SYSCALL msg=audit(1726625910.928:12957): arch=c000003e syscall=10 success=no exit=-13 a0=556a3505e000 a1=2000 a2=7 a3=7f1c02e276b0 items=0 ppid=28788 pid=28790 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1186 comm="njet" exe="/data/njet-bo/proxy2/sbin/njet" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)


audit的日志明确是设置“execheap” 操作被阻止了。也就是mp_protect执行失败,导致了core

2.2 根源分析

NJet为了兼顾高性能及灵活性,选择了tcc JIT框架,从而可以利用c 语言脚本来实现业务逻辑,广泛应用在stream处理中的协议识别,协议解析等多处模块。在启动阶段,tcc会把c语言脚本编译产生可执行到代码,并加载到内存中。因为加载到的代码会被作为函数执行,因此tcc需要对该段代码所在的区域利用mp_protect修改为可执行模式。
但selinux 模式中,该操作被阻止了。

3. 解决方案

3.1 尝试验证

  1. 关闭selinux 规则: setenforce 0
    可以解决
  2. 设置相关权限 setsebool -P allow_execheap 1
    可以解决

3.2 最终解决方案

尽管3.1中的两种方案能够解决问题,但存在负面影响(影响了系统安全设置,有可能违反企业的安全规则)。仔细阅读 tcc 代码,tcc有编译选项–with-selinux , 因此在编译tcc时,需要开启该选项,应对selinux的情况

4. 其他信息

  • 本方案适用于centos及其变种

  • SELinux是Linux内核的安全子系统,通过严格的访问控制机制增强系统安全性。一般情况下,建议开启SELinux来限制进程的权限,防止恶意程序通过提权等方式对系统进行攻击;然而,由于SELinux的严格访问控制机制,可能会导致一些应用程序或服务无法启动,请参考如下链接,查看,关闭/启用SELinux https://help.aliyun.com/zh/ecs/use-cases/enable-or-disable-selinux

  • getsebool -a 可以用于查询具体的SELinux控制的属性状态,并利用setsebool进行设置变更

附录1 core后 error 的解释

The RIP value is the instruction pointer register value, the RSP is the stack pointer register value. The error value is a bit mask of page fault error code bits (from arch/x86/mm/fault.c):

  • bit 0 == 0: no page found 1: protection fault
  • bit 1 == 0: read access 1: write access
  • bit 2 == 0: kernel-mode access 1: user-mode access
  • bit 3 == 1: use of reserved bit detected
  • bit 4 == 1: fault was an instruction fetch

Here’s error bit definition:

enum x86_pf_error_code {
PF_PROT = 1 <&lt0,
PF_WRITE = 1 <&lt1,
PF_USER = 1 <&lt2,
PF_RSVD = 1 <&lt3,
PF_INSTR = 1 <&lt4,
};

The error code 15 is 1111 bit. Finally, we can know the meaning of 1111 as follows:

01111
^^^^^
||||+—&gtbit 0
|||+—-&gtbit 1
||+—–&gtbit 2
|+——&gtbit 3
+——-&gtbit 4

This message indicates that the application triggers protection fault because that process tried to write access to a reserved section of memory in user-mode.