keystore问题梳理

新星杯·14天创作挑战营·第17期 10w+人浏览 490人参与

问题分析

1. 直接症状

java.lang.NullPointerException: Attempt to invoke interface method 
'android.os.IBinder android.system.keystore2.IKeystoreOperation.asBinder()' 
on a null object reference

这表明 IKeystoreOperation 对象为 null,通常发生在 Binder 事务失败时。

2. 关键时间线

  • 03:14:45.411184 - keystore2 成功创建了 Operation 并返回了有效的 binder
  • 03:14:45.477 - system_server 在不到 66ms 后就收到了 null 的 binder

这种短时间内 binder 对象失效是典型的资源耗尽表现。

诊断建议

立即检查命令:

# 检查当前binder状态
adb shell cat /proc/binder/stats
adb shell cat /proc/binder/state
adb shell cat /proc/binder/proc/`pidof system_server`

# 检查binder内存使用
adb shell dumpsys meminfo | grep -A10 "Binder:"

重点关注指标:

  1. Binder 内存分配

    • allocated_buffers
    • free_buffers
    • active_transactions
  2. 进程级 Binder 状态

    • pending_async_transaction 数量
    • outstanding_txns 计数

可能的原因

  1. Binder 内存泄漏

    • system_server 中的 binder 对象未及时释放
    • keystore 操作完成后 binder 未清理
  2. 频繁的密钥操作

    • 短时间内大量 HMAC 签名操作
    • Wifi Mac 地址计算频繁触发密钥操作

临时缓解措施

# 重启binder相关服务
adb shell stop keystore2
adb shell start keystore2

# 或者重启system_server (会重启整个系统UI)
adb shell pkill -f system_server

长期解决方案

  1. 增加 Binder 内存限制

    • 检查 /dev/binder 的内存配置
    • 考虑增加 CONFIG_ANDROID_BINDER_IPC_SELFTEST
  2. 优化密钥使用模式

    • MacAddressUtil.calculatePersistentMac 中缓存操作
    • 避免在热点循环中频繁创建 binder 操作
  3. 添加资源监控

    • 监控 system_server 的 binder 使用情况
    • 设置预警阈值

请先运行诊断命令,我可以帮您进一步分析具体的资源瓶颈。

分析流程:
我们遇到了一个NullPointerException,发生在system_server进程中,具体是在KeyStoreOperation的构造函数中,尝试调用IKeystoreOperation.asBinder()时,因为IKeystoreOperation为null。
从日志中可以看到,在keystore2中,create_operation操作已经完成,并且返回了一个IKeystoreOperation对象(见日志中的"mtkdebug
create_operation …finish… op_binder=IKeystoreOperation")。
但是,在system_server中,当尝试使用这个操作时,却得到了null。

可能的原因:

在binder传输过程中,可能发生了异常,导致返回的binder对象为null。

可能由于binder驱动资源耗尽,导致binder对象无法创建或传输。

但是,从日志中我们并没有直接看到binder资源耗尽的错误信息。通常binder资源耗尽会有明确的错误日志,比如"Binder
transaction failed"等。

另外,我们注意到在keystore2的日志中,创建操作是成功的,并且返回了IKeystoreOperation对象。所以,问题可能出现在binder传输过程中,或者是在客户端(system_server)解包时。

由于这个错误是随机发生的,而且发生在系统服务中,我们需要检查系统当前的状态,特别是binder的状态。

我们可以通过以下命令检查binder状态: cat /proc/binder/stats cat /proc/binder/state

但是,在给定的日志中,我们没有这些信息。因此,我们需要根据已有的日志进行推测。

从日志中,我们还可以看到在发生崩溃之前,系统已经运行了283214557毫秒(约3.27天),这可能是一个线索,因为长时间运行可能导致资源泄漏。

建议:

检查系统当前binder的使用情况,特别是binder缓冲区和binder线程的使用。

检查系统内存状态,因为binder内存不足也会导致传输失败。

查看是否有其他相关的错误日志,特别是binder相关的错误。

由于我们无法直接获取系统状态,我们可以从代码层面分析可能的原因。

在KeyStoreSecurityLevel.createOperation方法中,我们调用了securityLevel.createOperation方法,该方法返回一个IKeystoreOperation对象。
然后,我们使用这个对象创建KeyStoreOperation对象。在KeyStoreOperation的构造函数中,我们调用了asBinder()方法。

如果createOperation返回null,那么就会导致NullPointerException。

因此,我们需要确保securityLevel.createOperation不返回null。但是,从keystore2的日志来看,它确实返回了一个非null的对象(因为日志中打印了op_binder=IKeystoreOperation)。

所以,可能的原因是在binder传输过程中,这个对象丢失了,或者被设置为null了。这可能是由于binder驱动的问题,或者客户端在接收binder对象时出现了问题。

由于我们怀疑是binder资源耗尽,我们可以检查一下系统中binder的分配情况。但是,在日志中没有直接证据。

我们可以尝试在设备上运行以下命令来检查binder状态: adb shell cat /proc/binder/stats adb
shell cat /proc/binder/state

如果发现binder缓冲区不足或者binder线程数达到上限,那么可能是binder资源耗尽。

另外,我们也可以查看系统日志中是否有以下错误: “binder: [FAILED_TRANSACTION] …” “binder:
transaction failed …”

在给定的日志中,我们没有看到这样的错误。但是,我们看到了一个崩溃,所以可能没有捕获到binder传输失败的错误。

另一个可能性是,在客户端解包binder对象时出现了问题。可能是在客户端代码中,没有正确地从Parcel中读取binder对象。

由于我们无法看到完整的代码,我们只能根据日志进行推测。

总结:
我们需要更多的信息来确认问题,特别是binder的状态。但是,从现有的日志来看,我们无法直接断定是binder资源耗尽。建议收集以下信息:

系统binder状态(/proc/binder/state和/proc/binder/stats)

系统内存信息

系统日志中是否有binder相关的错误

如果确实发现binder资源耗尽,那么需要分析是什么原因导致binder资源耗尽,比如是否有泄漏,或者是否需要调整binder缓冲区大小。

如果binder资源正常,那么可能需要检查keystore2服务以及system_server之间的binder通信是否正常,特别是跨进程传递binder对象时是否出现了问题。

由于问题发生在系统服务中,并且可能影响系统稳定性,建议尽快收集更多信息并进行分析。

分析流程:
根据提供的日志,我们看到了一个NullPointerException,发生在system_server进程中,尝试调用IKeystoreOperation.asBinder()时,因为IKeystoreOperation对象为null。

从日志中,我们还可以看到在崩溃之前,keystore2服务(进程675)中的线程24320成功创建了一个IKeystoreOperation,并打印了日志,包括操作的具体参数。然后,在system_server中尝试使用这个操作时,却得到了null。

同时,我们看到了很多binder线程在等待工作(binder_wait_for_work),这包括keystore2进程中的多个binder线程(675进程有10个binder线程)和其他进程的binder线程。

可能的原因分析:

Binder驱动资源耗尽:虽然我们看到了很多binder线程,但这并不直接表示资源耗尽。需要查看binder驱动统计信息(如/proc/binder/stats)来确认。

内存不足:如果系统内存不足,可能导致binder内存分配失败。

进程间通信超时或异常:可能由于某些原因,binder事务失败,返回了null。

但是,从日志中我们看到keystore2服务确实创建了操作并返回了IKeystoreOperation,然后system_server在初始化KeyStoreOperation时,传入的IKeystoreOperation为null。

可能的情况是,在createOperation返回后,在将IKeystoreOperation传递给system_server的过程中出现了问题。或者,在keystore2服务中,虽然创建了操作,但在返回binder对象时出现了问题。

另外,我们注意到崩溃发生在system_server的KeyStoreOperation构造函数中,它试图调用asBinder(),说明传入的IKeystoreOperation对象是null。

可能的原因包括:

keystore2服务在创建操作后,在返回binder对象时发生了异常,但日志没有捕获到。

binder传输过程中出现了问题,比如binder缓冲区不足,或者传输大型对象时失败。

根据提供的日志,我们还可以看到系统运行时间(SystemUptimeMs)为283214557毫秒(大约3天7小时),可能系统长时间运行导致资源泄漏。

建议的检查步骤:

检查binder驱动统计信息,特别是:

当前分配的缓冲区数量

空闲缓冲区数量

活动事务数量

缓冲区大小设置

检查系统内存状态,特别是binder内存的使用情况。

检查keystore2服务的日志,看看在创建操作后是否有异常。

检查system_server的日志,看看在调用keystore2服务时是否有异常。

考虑增加binder调试日志,以跟踪binder事务的详细过程。

由于我们目前只有部分日志,无法看到完整的情况。但是,从已有的信息来看,binder资源耗尽的可能性是存在的。

另外,从堆栈跟踪来看,崩溃发生在计算持久化MAC地址时,这可能是由于WiFi服务频繁调用密钥操作,导致binder资源被大量使用。

临时解决方案可能是重启system_server或keystore2服务,但长期解决方案需要优化密钥操作的使用,避免频繁调用,并确保binder资源得到正确释放。

如果可能,请提供/proc/binder/stats和/proc/binder/state的内容,以便进一步分析。

根据您提供的完整日志,我来详细分析这个 Binder 资源耗尽 问题:

关键问题分析

1. Binder 线程池饱和

从进程状态可以看到:

keystore2 (PID 675) 有 10个 binder 线程:
binder:675_1, binder:675_2, ..., binder:675_9, binder:675_A

问题:keystore2 的 binder 线程池已经达到最大限制(通常是 15-16 个),但仍有新的 binder 请求。

2. 时间线分析

03:14:45.411184 - keystore2 成功创建 Operation,返回有效 binder
03:14:45.477 - system_server 收到 null binder (66ms 后)

关键发现:binder 对象在极短时间内失效,表明:

  • Binder 驱动无法分配新的 binder 对象
  • 或者 binder 内存池已满

详细的 Binder 状态分析

keystore2 进程 (PID 675)

binder:675_1 到 binder:675_A (共10个线程)
状态: binder_wait_for_work
  • 所有线程都在等待工作,但系统无法分配新的 binder 资源
  • 线程池饱和,无法处理新的异步请求

其他相关进程

  • vold: 8个 binder 线程
  • system_suspend: 8个 binder 线程
  • netd: 6个 binder 线程
  • statsd: 7个 binder 线程

根本原因

1. Binder 内存泄漏

MacAddressUtil.calculatePersistentMacInternal 中:

// 可能的泄漏点
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);  // 这会创建 binder 操作
byte[] result = mac.doFinal(input);  // 使用后可能未正确清理

2. 频繁的密钥操作

  • Wifi 模块频繁计算 MAC 地址
  • 每次计算都创建新的 HMAC 操作
  • binder 对象快速累积且未及时释放

紧急诊断命令

请立即运行以下命令确认资源状态:

# 1. 检查binder内存统计
adb shell cat /proc/binder/stats

# 2. 查看system_server的binder状态
adb shell cat /proc/binder/proc/`pidof system_server`

# 3. 检查内存碎片
adb shell cat /proc/binder/state | grep -A20 "buffer"

# 4. 监控binder事务
adb shell "while true; do date; cat /proc/binder/stats | grep -E '(active_transactions|bc_alloc|bc_free)'; sleep 1; done"

解决方案

短期修复

# 重启keystore服务
adb shell stop keystore2
adb shell start keystore2

# 清理binder内存
adb shell "echo 3 > /proc/sys/vm/drop_caches"

代码级修复

MacAddressUtil.java 中:

public class MacAddressUtil {
    private static Mac sCachedMac = null;
    
    private static synchronized Mac getCachedMac() throws NoSuchAlgorithmException {
        if (sCachedMac == null) {
            sCachedMac = Mac.getInstance("HmacSHA256");
        }
        return sCachedMac;
    }
    
    public static byte[] calculatePersistentMacInternal(byte[] input, Key key) {
        try {
            Mac mac = getCachedMac();
            synchronized (mac) {
                mac.init(key);
                return mac.doFinal(input);
            }
        } catch (Exception e) {
            // 错误处理
        }
    }
}

系统级优化

  1. 增加 binder 线程池限制

    # 在设备配置中增加
    persist.vendor.binder_max_threads=32
    
  2. 监控 binder 使用

    • 添加 binder 资源监控告警
    • 设置自动恢复机制

建议的验证步骤

  1. 立即执行诊断命令确认资源状态
  2. 应用缓存修复减少 binder 操作频率
  3. 增加监控防止问题复发
  4. 压力测试验证修复效果

请先运行诊断命令,我可以帮您分析具体的 /proc/binder/stats 输出,确认是内存耗尽还是线程池饱和的问题。

D:\Program Files\elasticsearch-8.9.0\bin>elasticsearch-setup-passwords.bat interactive 09:23:57.180 [main] WARN org.elasticsearch.deprecation.common.settings.Settings - data_stream.dataset="deprecation.elasticsearch" data_stream.namespace="default" data_stream.type="logs" elasticsearch.event.category="settings" event.code="keystore.password" message="[keystore.password] setting was deprecated in Elasticsearch and will be removed in a future release." Exception in thread "main" org.elasticsearch.ElasticsearchSecurityException: failed to load SSL configuration [xpack.security.transport.ssl] - cannot specify both [keystore.secure_password] and [keystore.password] at org.elasticsearch.xpack.core.ssl.SSLService.lambda$getSSLConfigurations$4(SSLService.java:550) at java.base/java.util.HashMap.forEach(HashMap.java:1429) at java.base/java.util.Collections$UnmodifiableMap.forEach(Collections.java:1553) at org.elasticsearch.xpack.core.ssl.SSLService.getSSLConfigurations(SSLService.java:542) at org.elasticsearch.xpack.core.ssl.SSLService.<init>(SSLService.java:147) at org.elasticsearch.xpack.core.security.CommandLineHttpClient.execute(CommandLineHttpClient.java:149) at org.elasticsearch.xpack.core.security.CommandLineHttpClient.execute(CommandLineHttpClient.java:112) at org.elasticsearch.xpack.security.authc.esnative.tool.SetupPasswordTool$SetupCommand.checkElasticKeystorePasswordValid(SetupPasswordTool.java:340) at org.elasticsearch.xpack.security.authc.esnative.tool.SetupPasswordTool$InteractiveSetup.execute(SetupPasswordTool.java:203) at org.elasticsearch.common.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:54) at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:85) at org.elasticsearch.cli.MultiCommand.execute(MultiCommand.java:94) at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:85) at org.elasticsearch.cli.Command.main(Command.java:50) at org.elasticsearch.launcher.CliToolLauncher.main(CliToolLauncher.java:64) Caused by: org.elasticsearch.common.ssl.SslConfigException: cannot specify both [keystore.secure_password] and [keystore.password]
06-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值