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仓库以简化这一过程的方法。 目录组织 我将我的…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...
FOPLP vs CoWoS
以下是 FOPLP(Fan-out panel-level packaging 扇出型面板级封装)与 CoWoS(Chip on Wafer on Substrate)两种先进封装技术的详细对比分析,涵盖技术原理、性能、成本、应用场景及市场趋势等维度: 一、技术原…...