baidu/openrasp

死锁导致应用启动异常

kindragos opened this issue · 2 comments

环境信息

JDK: OpenJDK Runtime Environment (Zulu 8.33.0.1-linux64) (build 1.8.0_192-b01)
tomcat: 8.5.57-7003
openrasp: 1.3.7

问题描述

tomcat携带rasp启动,启动过程偶尔会卡主不动。
异常时堆栈信息如下:
123
使用不同JDK测试结果如下:
1.8.0_192 (Zulu 8.33.0.2-linux64): 无死锁
1.8.0_281 (Zulu 8.51.0.14-SA-linux64): 偶尔死锁
1.8.0_352 (Zulu 8.66.0.16-SA-linux64): 偶尔死锁

问题原因

agent启动时,心跳线程从云控拉取配置信息后,要载入这些配置。载入配置时,会对配置项进行校验,不在ConfigItem中的配置项,会通过自定义的HttpAppender打印warn级别的日志。此时,心跳线程锁住了log4j.spi.RootLogger(红框)。在打印日志的同时,会调用JDK方法,获取当前线程的PID。获取PID时,需要获取ManagementFactoryHelper的锁(蓝框),但获取不到,一直等待。
agent启动时,main线程中OperatingSystemImpl构造函数调用了jdk.internal.platform.Container.metrics(),这个过程需要锁住ManagementFactoryHelper(蓝框)。随后在transfer的过程中,会打印插桩信息,这时需要获取log4j.spi.RootLogger的锁(红框),但获取不到,一直等待。
形成死锁。

为何偶发的原因

当且仅当agent启动时,main线程和心跳线程在抢锁的过程中出现循环嵌套,才会出现死锁。只要任意一个线程先一步执行结束,就不存在死锁情景。

为何部分JDK没有问题的原因

JDK团队收到了这样一个问题:
JDK-8226575 - OperatingSystemMXBean should be made container aware
当Java在容器中运行时,许多OperatingSystemMXBean访问器方法返回基于主机的信息,而不是特定于容器的数据。

在上述调整之前,OperatingSystemMXBean初始化时是不需要获取ManagementFactoryHelper锁的,所以不存在死锁情景。
该调整上线的版本有:
Oracle JDK 8u261
openjdk8u272、openJDK14
zulu8.50(8.0.272)、zulu11.43(11.0.9.1)

解决方案

在心跳线程中,不再用ManagementFactory.getRuntimeMXBean()获取线程PID,而是将这个过程前置到agent刚启动的时候。
我使用这个解决方案,重启tomcat35000W+次,失败0次。

我现在想提一个PR,请问可以提到哪个分支上?
@lixin1234qqq @spacelan

已合并,刚点错了重新merge了下