Android 中 调试和减少内存错误
Android 中 调试和减少内存错误
ASan
概述
官网连接: https://developer.android.com/ndk/guides/asan?hl=zh-cn
- ASan API 27开始
- HWASan(替换AScan)
- 从 NDK r21 和 Android 10(API 级别 29)开始
- 适用于 64 位 Arm 设备
- 性能比AScan更好
主要功能
- 堆栈和堆缓冲区上溢或下溢
- 释放之后的堆使用情况
- 超出范围的堆栈使用情况
- 重复释放或错误释放
示例应用
示例应用展示了如何为 hwasan 配置 build 变体。
模拟出几个内存错误的问题
1. 越界访问 (Buffer Overflow/Underflow)
#### 问题描述: 当程序试图访问数组或缓冲区以外的内存时,就会发生越界访问错误。这类问题往往导致数据损坏,崩溃或安全漏洞。 #### 具体表征:
- 程序崩溃:可能导致应用无预警地关闭。
- 数据损坏:错误地重写了其他变量的值。
- 安全漏洞:可能被利用执行恶意代码。
#### 示例代码:
int array[5] = {0};
int value = array[10]; // 越界读取
array[-1] = 3; // 越界写入
2. 使用后释放 (Use After Free)
#### 问题描述: 当程序释放了一块内存后仍然尝试使用它时,就会发生使用后释放错误。这是一种非常危险的安全漏洞。 #### 具体表征:
- 不可预知的行为:可能导致程序执行一段随机的内存操作。
- 程序崩溃:访问被释放内存可能导致程序无法继续执行。
- 内存损坏:可能不经意间修改了由其他部分的程序占用的内存。
#### 示例代码:
char *ptr = new char[20];
delete [] ptr; // 释放内存
strcpy(ptr, "这是错误的使用!"); // 在释放后使用内存
3. 内存泄露 (Memory Leak)
#### 问题描述: 未能释放不再使用的内存导致内存泄露。虽然它不会立即导致程序崩溃,但会随着时间推移而逐渐消耗系统资源。 #### 具体表征:
- 内存消耗增加:随着程序的运行,预期的内存使用量会不断增加。
- 性能降低:可用内存减少可能导致系统运行变慢,甚至出现延迟。
- 可能的程序崩溃:如果内存泄露严重,系统最终可能耗尽内存,导致应用或系统崩溃。
#### 示例代码:
void func()
{char *ptr = new char[10]; // 分配内存// 这里应该有一些处理// 忘记释放ptr分配的内存
}
这些内存错误问题都是开发过程中应当注意避免的。常规的调试方法和一些专用工具比如Valgrind、AddressSanitizer(ASan)和上文提到的HWAddressSanitizer(HWASan)能够帮助开发者检测并解决这些内存错误。
HWASan输出日志分析
模拟HWAddressSanitizer的日志输出实际会涉及到针对具体错误调用堆栈和内存映射的信息。由于HWASan的具体日志输出会根据实际运行时的环境和错误情境有所不同,下面我将模拟几个内存错误的HWASan日志输出及关键点说明:
1. 越界访问错误
=================================================================
==12345==ERROR: HWAddressSanitizer: tag-mismatch on address 0x00b100004008 at pc 0x000000552abc
WRITE of size 4 at 0x00b100004008 tags: 1c/15 (ptr/mem) in thread T0#0 0x552abc (/path/to/binary+0x552abc)#1 0x55678f (/path/to/binary+0x55678f)#2 0x7f7c85dd9c (/system/lib/hwaddress-sanitizer.so+0x5dd9c)
0x00b100004008 is located 0 bytes to the right of 8-byte region [0x00b100004000,0x00b100004008)
allocated by thread T0 here:#0 0x7f7c85ca58 (/system/lib/hwaddress-sanitizer.so+0x5ca58)#1 0x5556f4 (/path/to/binary+0x5556f4)#2 0x555979 (/path/to/binary+0x555979)
SUMMARY: HWAddressSanitizer: tag-mismatch /path/to/binary (0x552abc) WRITE 0x00b100004008
=================================================================
#### 关键点说明:
tag-mismatch
表示检测到内存标签不匹配,这通常提示内存访问错误。WRITE of size 4
说明试图写入4个字节的数据。address 0x00b100004008 at pc 0x000000552abc
显示了发生错误的内存地址和程序计数器的地址。- 调用堆栈(call stack)提供了错误发生时的函数调用序列。
0x00b100004008 is located 0 bytes to the right of 8-byte region
说明写操作是在8字节区域的右侧进行的,这是越界访问。
2使用后释放错误
================================================================= ==12345==ERROR: HWAddressSanitizer: use-after-free on address 0x00b100004010 at pc 0x000000552efc READ of size 8 at 0x00b100004010 tags: 1c/00 (ptr/mem) in thread T0 #0 0x552efc (/path/to/binary+0x552efc) #1 0x556abc (/path/to/binary+0x556abc) #2 0x7f7c85dd9c (/system/lib/hwaddress-sanitizer.so+0x5dd9c) 0x00b100004010 is located 0 bytes inside of 10-byte region [0x00b100004010,0x00b10000401a) freed by thread T0 here: #0 0x7f7c85caff0 (/system/lib/hwaddress-sanitizer.so+0xcaff0) #1 0x555999 (/path/to/binary+0x555999) #2 0x5.#2 0x7f7c85dd9c (/system/lib/hwaddress-sanitizer.so+0x5dd9c)
0x00b100004010 is located 0 bytes inside of 10-byte region [0x00b100004010,0x00b10000401a)
freed by thread T0 here:#0 0x7f7c85caff0 (/system/lib/hwaddress-sanitizer.so+0xcaff0)#1 0x555999 (/path/to/binary+0x555999)#2 0x556df9 (/path/to/binary+0x556df9)
previously allocated by thread T0 here:#0 0x7f7c85ca58 (/system/lib/hwaddress-sanitizer.so+0x5ca58)#1 0x5557e8 (/path/to/binary+0x5557e8)
SUMMARY: HWAddressSanitizer: use-after-free (/path/to/binary+0x552efc) READ 0x00b100004010
=================================================================
#### 关键点说明:
use-after-free
表示在释放后再次使用了内存,这是一种严重的错误。READ of size 8
说明尝试读取8个字节的数据。address 0x00b100004010 at pc 0x000000552efc
显示了被错误读取的内存地址和相关的程序计数器的地址。- 调用堆栈(call stack)提供了释放内存和随后错误使用该内存的函数调用序列。
freed by thread T0 here:
和previously allocated by thread T0 here:
显示了内存分配及后续释放的位置。
在分析这样的日志时,开发者需要关注堆栈跟踪来确定错误发生的上下文,并修复代码中相应的问题。日志中的内存地址、线程信息以及函数调用序列都是确定问题所在和解决问题的关键信息。在实际的开发工作中,可通过这些详尽的日志来定位问题,优化代码,并进一步强化软件的稳健性和安全性。
Arm 内存标记扩展 (MTE)
- 支持ARMv8.5-A及以上架构的处理器
- 从 Android 13 开始,部分设备支持 MTE
- adb shell grep mte /proc/cpuinfo 出现
Features : [...] mte
则表示设备在运行时启用了 MTE
MTE vs ASan
MTE是 ARM 架构提供的硬件特性,它通过在物理内存中添加一些标记信息来辅助检测内存安全错误,它的优势在于提供硬件层面的检测,开销相较软件层面的检测方式较小,特别是在ASYNC模式下。HWASan 是一种基于软件的解决方案,通过修改编译器和运行时环境来检测内存安全问题,特别是地址错误和内存泄漏等。它是谷歌为ARM64架构设计的,针对Android操作系统进行了优化。HWASan在测试和分析阶段特别有用,因为它能提供详尽的错误报告,包括堆栈追踪和内存访问历史等。
如在支持MTE的ARMv8.5及以上架构的设备上,可以优先选用MTE
SYNC 和ASYNC异同
维度 | SYNC(同步模式) | ASYNC(异步模式) |
---|---|---|
优化目标 | 针对可调试性优化,适用于精确的 bug 检测工具 | 针对 bug 报告的性能优化,偏向于低开销的内存安全检测 |
处理器反应 | 在接收到违规的加载或存储指令时,会立即终止进程 | 处理器会在到达最近的内核入口(如系统调用或计时器中断)时终止进程 |
错误报告 | 返回SIGSEGV,提供内存访问和故障地址的详细信息 | 返回SIGSEGV,但是不记录错误地址或内存访问 |
配合Android分配器 | 分配器会记录每次分配和取消分配的堆栈轨迹以提供更好的错误报告 | 不进行此操作 |
应用场景 | 用作测试阶段的HWASan的更快替代方案,或生产环境中的应用出现漏洞时的安全缓解措施。 | 对经过严格测试的代码库(已知其内存安全 bug 的密度较低)降低内存安全漏洞的生产环境 |
两种模式的相同点包括:
在进行硬件辅助的内存安全错误检测时,SYNC和ASYNC都可以监测到内存安全错误。一旦发现标记不匹配,两种模式均会触发处理器的响应,终止进程并返回SIGSEGV。在这两种模式下,都可以在测试阶段运用来找出内存安全bug。
给自己分配的内存打TAG
scudo 实现代码
setRandomTag
函数:该函数是用来为给定的指针Ptr
设置一个随机的内存标签。untagPointer
函数:这个函数会移除指针Ptr
的内存标签loadTag
函数:此函数似乎用于返回指针Ptr
上的内存标签- addFixedTag 函数:这个函数用于给一个指针 Ptr 添加一个固定的标签 Tag
allocatorSupportsMemoryTagging
模板函数:此函数用于确定是否支持内存标记。
使用场景
- 内存错误检测:在分配和释放内存时为其打上Tag,可以帮助检测内存安全错误,如缓冲区溢出、使用后释放等。当一个内存区域被释放后,您可以为其分配一个新的Tag,如果之后系统尝试使用相同的旧Tag访问这块内存,MTE机制将检测到错误,并产生一个异常。
- 运行时监控和诊断:打标签的内存使得开发者可以追踪内存访问模式,识别内存是如何被程序的不同部分访问的。这在调试过程中尤为有用,因为它可以帮助发现那些隐蔽的内存错误。
- 保护关键数据:通过给敏感数据打个特定的Tag,你可以确保只有被授权的代码能够访问这些数据。如果其他不相关的代码尝试访问,标签不匹配会触发异常,这为敏感数据提供了一层额外的保护。
- 内存使用分析:Tag可以作为分析工具,以了解某类内存分配的分布和生命周期。例如,通过给特定类型的对象或资源分配统一的Tag,可以在运行时分析其分布情况。
- 内存泄露排查:如果一个内存块长时间没有释放,并且带有与之关联的Tag,那么这可能是一个内存泄露的迹象。分析这些留存的Tag可以帮助追踪潜在的内存泄露点。
进阶
Arm 撰写的 Android OS MTE 用户指南
GWP-ASan
GWP-ASan(GWP-ASan Will Provide Allocation SANity) 是一种原生内存分配器功能,可帮助查找释放后使用和堆缓冲区溢出 bug。
- GWP-ASan 不需要源代码或重新编译
- 适用于以 Android 11(API 级别 30)以上
概述
- GWP-ASan启用情况:在进程启动以及zygote派生时,系统会随机选择一些应用和平台可执行文件启用GWP-ASan。
- 作用:GWP-ASan旨在帮助开发者发现与内存相关的错误,并帮助应用准备好对ARM内存标记扩展(MTE)的支持。
- 内存分配拦截:一旦启用,GWP-ASan会随机拦截堆分配的子集,并将它们移入特殊区域,以发现通常难以检测的堆内存损坏错误。
- 低采样率的效果:即使是低采样率,只要用户基数足够大,也能够发现常规测试中未能发现的堆内存安全错误。
- 错误检测示例:GWP-ASan已在Chrome中发现大量错误。
- 信息收集:GWP-ASan为它拦截的所有分配收集额外信息,这些信息有助于内存安全违规的调试,且会被自动加入到原生代码的崩溃报告中。
- 性能影响:启用GWP-ASan后,会产生较小的CPU开销。
- 资源消耗:GWP-ASan会带来一定的固定RAM开销,目前累计每个受影响进程大约70KiB。
获取检测到“释放后堆使用”或“堆缓冲区溢出” bug
ActivityManager#getHistoricalProcessExitReasons
json格式
[{"processName": "com.example.app","pid": 12345,"reason": "CRASH","timestamp": 1617998745000,"description": "NullPointer exception in MainActivity","trace": "java.lang.NullPointerException: Attempt to invoke virtual method on a null object reference\n\tat com.example.MainActivity.onCreate(MainActivity.java:85)\n\t...","importance": "FOREGROUND_SERVICE","status": "SIGNAL 9"},{"processName": "com.example.serviceapp","pid": 12346,"reason": "USER_REQUESTED","timestamp": 1617998746000,"description": "User requested force stop","trace": "","importance": "VISIBLE","status": "SIGNAL 9"},
相关文章:
Android 中 调试和减少内存错误
Android 中 调试和减少内存错误 ASan 概述 官网连接: https://developer.android.com/ndk/guides/asan?hlzh-cn ASan API 27开始HWASan(替换AScan) 从 NDK r21 和 Android 10(API 级别 29)开始适用于 64 位 Arm 设…...
证券市场概述
证券市场 证券市场参与者证券发行市场(一级市场)证券发行方式(按发行对象)证券发行方式(按有无中介)证券交易市场(二级市场)证券交易所场外交易市场(店头市场、柜台市场&…...

什么是数据结构
一、什么是数据结构 1.数据结构研究计算机数据间的关系 2.包括数据的逻辑结构和储存结构及其操作 数据的逻辑结构:表示数据运算之间的抽象关系 按每个元素可能具有的直接前趋数和后继数将逻辑结构分为“线性结构”和“非线性结构”两大类 数据的储存结构&#…...

基于springboot+vue实现的学校田径运动会管理系统
作者主页:Java码库 主营内容:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】:Java 【框架】:spring…...

HarmonyOS 应用开发之FA模型绑定Stage模型ServiceExtensionAbility
本文介绍FA模型的三种应用组件如何绑定Stage模型的ServiceExtensionAbility组件。 PageAbility关联访问ServiceExtensionAbility PageAbility关联访问ServiceExtensionAbility和PageAbility关联访问ServiceAbility的方式完全相同。 import featureAbility from ohos.ability…...

Java 中的单例模式
引言: 在 Java 编程中,单例模式是一种常见的设计模式,它保证一个类只能创建一个实例,并提供一个全局访问点。单例模式在很多场景下都非常有用,比如线程池、日志系统、数据库连接池等。本文将详细介绍 Java 中单例模式的…...

鸿蒙OS开发实例:【ArkTS类库多线程I/O密集型任务开发】
使用异步并发可以解决单次I/O任务阻塞的问题,但是如果遇到I/O密集型任务,同样会阻塞线程中其它任务的执行,这时需要使用多线程并发能力来进行解决。 I/O密集型任务的性能重点通常不在于CPU的处理能力,而在于I/O操作的速度和效率。…...

OpenStack部署
目录 一、安装环境 1.无网络使用该命令 2.修改主机名 3.配置hosts解析 4.配置本机免密 5.关闭防火墙和SElinux策略 6.关闭NewworkManager 7.修改yum源 7.1下载阿里源 7.2清空并加载缓存yum源 8.安装基本工具 9.系统升级 10.安装OPenStack的yum仓库 11.修改OPenSt…...

Java中的多线程和线程安全问题
线程 线程是操作系统进行调度的最小单位。一个进程至少包含一个主线程,而一个线程可以启动多个子线程。线程之间共享进程的资源,但也有自己的局部变量。多线程程序和普通程序的区别:每个线程都是一个独立的执行流;多个线程之间是…...

java Web会议信息管理系统 用eclipse定制开发mysql数据库BS模式java编程jdbc
一、源码特点 jsp 会议信息管理系统是一套完善的web设计系统,对理解JSP java SERLVET mvc编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发,数据库为Mysql5.0&am…...
lock4j学习记录
一种简单的,支持不同方案的高性能分布式锁 简介 lock4j是一个分布式锁组件,其提供了多种不同的支持以满足不同性能和环境的需求。 立志打造一个简单但富有内涵的分布式锁组件。 特性 简单易用,功能强大,扩展性强。支持redis…...

【C++庖丁解牛】自平衡二叉搜索树--AVL树
🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 前言1 AVL树的概念2. AVL…...

ES5和ES6的深拷贝问题
深拷贝我们知道是引用值的一个问题,因为在拷贝的时候,拷贝的是在内存中同一个引用。所以当其中的一个应用值发生改变的时候,其他的同一个引用值也会发生变化。那么针对于这种情况,我们需要进行深度拷贝,这样就可以做到…...
阿里云发送短信配置
依赖 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.2.1</version> </dependency> <dependency><groupId>org.apache.httpcomponents</groupId&g…...
axios封装,请求取消和重试,请求头公共参数传递
axios本身功能已经很强大了,封装也无需过度,只要能满足自己项目的需求即可。 常规axios封装,只需要设置: 实现请求拦截实现响应拦截常见错误信息处理请求头设置 import axios from axios;// 创建axios实例 const service axios…...

隐私计算实训营学习五:隐语PSI介绍及开发指南
文章目录 一、SPU 实现的PSI介绍1.1 PSI定义和种类1.1.1 PSI定义和种类1.1.2 隐语PSI功能分层 1.2 SPU 实现的PSI介绍1.2.1 半诚实模型1.2.2 PSI实现位置 二、SPU PSI调度架构三、Secretflow PSI开发指南四、隐语PSI后续计划 一、SPU 实现的PSI介绍 1.1 PSI定义和种类 1.1.1 …...

ES的RestClient相关操作
ES的RestClient相关操作 Elasticsearch使用Java操作。 本文仅介绍CURD索引库和文档!!! Elasticsearch基础:https://blog.csdn.net/weixin_46533577/article/details/137207222 Elasticsearch Clients官网:https://ww…...
linux通用命令 ssh命令连接慢问题排查
系列文章目录 文章目录 系列文章目录一、 ssh 连接慢3.1 查找原因3.2 解决方案 一、 ssh 连接慢 最近的 koji 服务器 使用 ssh 连接很慢。 3.1 查找原因 可以通过 ssh -vvv 192.168.0.123 或 time ssh root192.168.0.123 exit 查找原因如下: SERVER的SSHD会去DN…...

7.卷积神经网络与计算机视觉
计算机视觉是一门研究如何使计算机识别图片的学科,也是深度学习的主要应用领域之一。 在众多深度模型中,卷积神经网络“独领风骚”,已经被称为计算机视觉的主要研究根据之一。 一、卷积神经网络的基本思想 卷积神经网络最初由 Yann LeCun&a…...
Linux|如何管理多个Git身份
摘要 关于如何管理不同项目和多个Git身份。 作为一名通用软件开发者,我经常发现自己在处理各种各样的项目,每个项目都有自己的要求和期望。这包括为个人、工作和客户项目管理不同的Git身份。以下是我组织Git仓库以简化这一过程的方法。 目录组织 我将我的…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...