Finding memory bugs with AddressSanitizer

2023-05-16

AddressSanitizer (ASan) is an instrumentation tool created by Google security researchers to identify memory access problems in C and C++ programs.

在这里插入图片描述

When the source code of a C/C++ application is compiled with AddressSanitizer enabled, the program will be instrumented at runtime to identify and report memory access errors.

But what are memory access errors and how can AddressSanitizer help to identify them?

Memory access errors and AddressSanitizer

C and C++ are very insecure and error-prone languages. And one of the main sources of problems is memory access errors.

Different kind of bugs in the source code could trigger a memory access error, including:

  • Buffer overflow or buffer overrun occurs when a program overruns a buffer’s boundary and overwrites adjacent memory locations.
  • Stack overflow is when a program crosses the boundary of function’s stack.
  • Heap overflow is when a program overruns a buffer allocated in the heap.
  • Memory leak is when a program allocates memory but does not deallocate.
  • Use after free (dangling pointer) is when a program uses memory regions already deallocated.
  • Uninitialized variable is when a program reads a memory location before it is initialized.

All these errors are due to programming bugs. They could prevent the application from executing, cause invalid results or expose a vulnerability that could be exploited by a malicious actor. They are usually very hard to reproduce, debug and fix.

That is why we need tools. And AddressSanitizer is one of them.

AddressSanitizer in a nutshell

AddressSanitizer is implemented through compilation flags. To use AddressSanitizer, we need to compile and link the program using the -fsanitize=address switch.

For example, can you find a memory access error in the C program below?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
    char *msg = "Hello world!";
    char *ptr = NULL;

    ptr = malloc(strlen(msg));

    strcpy(ptr, msg);

    printf("%s\n", ptr);

    return 0;
}

This program compiles without any warning and runs:

$ gcc main.c -o main -Wall -Werror -g
% ./main
Hello world!

But the program has a heap buffer overflow error and AddressSanitizer can identify and report the problem. We just need to compile the program with the -fsanitize=address switch:

$ gcc main.c -o main -Wall -Werror -g -fsanitize=address

Now, when we run the application, the memory access problem will be identified and a report of the error will be displayed in the terminal:

$ ./main
=================================================================
==30259==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001c at pc 0x7f64667423a6 bp 0x7ffd069d7380 sp 0x7ffd069d6b28
WRITE of size 13 at 0x60200000001c thread T0
    #0 0x7f64667423a5  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x663a5)
    #1 0x55934507fa26 in main /home/sprado/Temp/main.c:12
    #2 0x7f646630cb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #3 0x55934507f8f9 in _start (/home/sprado/Temp/main+0x8f9)

0x60200000001c is located 0 bytes to the right of 12-byte region [0x602000000010,0x60200000001c)
allocated by thread T0 here:
    #0 0x7f64667bab50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x55934507fa0f in main /home/sprado/Temp/main.c:10
    #2 0x7f646630cb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x663a5)
Shadow bytes around the buggy address:
  0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 00[04]fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==30259==ABORTING

If you compile the application with debugging symbols (-g switch), the tool will also be able to convert addresses into file names and line numbers. That way, we can easily identify the line of the source code that caused the error (see line 6 in the listing above).

So can you see the error now? We overflowed the ptr pointer because we forgot to allocate an extra byte for the null character ('\0’).

In this other example, we have a memory leak problem:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void alloc()
{
    char *ptr;

    ptr = malloc(10);

    memset(ptr, 0, 10);
}

int main(int argc, const char *argv[])
{
    int i;

    for (i = 0; i < 3; i++)
        alloc();

    printf("OK!\n");

    return 0;
}

Again, the program is compiled without warnings and runs:

$ gcc main.c -o main -Wall -Werror -g
sprado@sprado-office:~/Temp$ ./main
OK!

But the memory leak problems are quickly identified when we compile and run the program with AddressSanitizer enabled:

$ gcc main.c -o main -Wall -Werror -g -fsanitize=address

$ ./main
==20677==WARNING: Trying to symbolize code, but external symbolizer is not initialized!

=================================================================
==20677==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 30 byte(s) in 3 object(s) allocated from:
#0 0x465319 (/tmp/main+0x465319)
#1 0x47b588 (/tmp/main+0x47b588)
#2 0x47b8c7 (/tmp/main+0x47b8c7)
#3 0x7f5e28457f44 (/lib/x86_64-linux-gnu/libc.so.6+0x21f44)

SUMMARY: AddressSanitizer: 30 byte(s) leaked in 3 allocation(s).

The memory leak check is enabled by default on x86_64. But depending on the architecture, to check for a memory leak we may need to add detect_leaks=1 to the environment variable ASAN_OPTIONS. Check the documentation for more information about this feature.

More about AddressSanitizer

AddressSanitizer works on x86 (32-bit and 64-bit), ARM (32-bit and 64-bit), MIPS (32-bit and 64-bit) and PowerPC64. The supported operating systems are Linux, Darwin (OS X and iOS Simulator), FreeBSD and Android.

Another interesting fact is that AddressSanitizer is implemented via some libraries, which are kept in the LLVM repository and shared with the GCC project. This is a clear example that, in recent years, there has been an increasing collaboration between the communities of GCC and Clang.

It is also important to note that these memory checks add considerable processing overhead to the application, and should only be used during development and testing.

According to the benchmarks published in the project documentation, AddressSanitizer could decrease the application’s execution time by up to 2x. Which is not good for production code, but not that bad for testing purposes. In fact, the performance is impressive, considering that the tool intercepts and checks every memory access made by the application.

So are you curious about how AddressSanitizer works?

How does AddressSanitizer work?

To instrument memory allocation and identify leaks, the malloc and free family of functions are replaced, so every memory allocation/deallocation is monitored by the tool.

Easy, right? But how to identify buffer overflows?

First, all memory that shouldn’t be accessed is poisoned. That includes memory around allocated regions, deallocated memory, and memory around variables in the stack.

Then every read or write memory access …

*ptr = ...;

… will be compiled to a code that will check if that memory address is poisoned or not. If poisoned, it will report an error.

if (IsPoisoned(ptr)) {
  ReportError(ptr, kAccessSize, kIsWrite);
}
*ptr = ...;

According to the documentation, the tricky part is how to implement IsPoisoned() very fast and ReportError() very compact.

Basically, the virtual address space of an application is divided into the main application memory that is used by the application code and a shadow memory that stores metadata about poisoned (not addressable) memory.

AddressSanitizer maps every 8 bytes of application memory into 1 byte of shadow memory. If a memory address is unpoisoned (i.e. addressable) the bit in the shadow memory is 0. If a memory address is poisoned (i.e. not addressable) the bit in the shadow memory is 1. That way, AddressSanitizer can identify which memory access is allowed or not and report errors.

If you want to get into the details about the implementation, read the documentation of the AddressSanitizer algorithm.

What about Valgrind?

You may know Valgrind, a very popular instrumentation framework that is also able to identify and report memory access problems.

The great advantage of Valgrind is that it can instrument the code without the need to recompile it.

However, the tradeoff is a big performance hit. According to this presentation, while AddressSanitizer execution overhead is around 2x, Valgrind’s overhead could be more than 20x!

In addition to AddressSanitizer, there are also another sanitizers provided by the project:

  • ThreadSanitizer is capable of identifying concurrency problems (data races and deadlocks).
  • MemorySanitizer is a detector of uninitialized memory reads in C/C++ programs.
  • Hardware-assisted AddressSanitizer is a newer variant of AddressSanitizer that is based on partial hardware assistance, consuming much less memory.
  • UndefinedBehaviorSanitizer is a fast undefined behavior detector.
  • The Kernel Address Sanitizer (KASAN) is a dynamic memory error detector for the Linux kernel (subject for a future article).

Support for AddressSanitizer exists in Clang since version 3.1 and GCC since version 4.8. If any of your projects use GCC or Clang, you should really stop what you are doing right now, enable the -fsanitize=address compiler switch and test your code. Do it. You may be surprised with the result!

About the author: Sergio Prado has been working with embedded systems for more than 20 years. If you want to know more about his work, please visit the About page or Embedded Labworks website.

Please email your comments to sergio at embeddedbits.org or sign up the newsletter to receive updates.


See also

  • GCC extensions to the C language
  • Extracting firmware from devices using JTAG
  • Bug hunting with static analysis tools
  • Reverse engineering my router’s firmware with binwalk
  • A new blog about embedded systems
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Finding memory bugs with AddressSanitizer 的相关文章

随机推荐

  • docker基础用法

    什么是docker Docker 是一个开源的应用容器引擎 xff0c 让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中 xff0c 然后发布到任何流行的 Linux或Windows 机器上 xff0c 也可以实现虚拟化 容器是完全
  • docker容器虚拟化

    虚拟化网络 Network Namespace 是 Linux 内核提供的功能 xff0c 是实现网络虚拟化的重要功能 xff0c 它能创建多个隔离的网络空间 xff0c 它们有独自网络栈信息 不管是虚拟机还是容器 xff0c 运行的时候仿
  • docker容器网络

    docker容器网络 Docker在安装后自动提供3种网络 xff0c 可以使用docker network ls命令查看 root 64 localhost docker network ls NETWORK ID NAME DRIVER
  • docker容器网络配置

    Linux内核实现名称空间的创建 ip netns命令 可以借助ip netns命令来完成对 Network Namespace 的各种操作 ip netns命令来自于iproute安装包 xff0c 一般系统会默认安装 xff0c 如果没
  • docker存储卷

    cow机制 cow意思是写时复制 xff0c 在对页表进程读操作时 xff0c 不会产生异常 xff0c 对页表进程写操作时 xff0c 会触发page fault异常 xff0c 通常这种机制的产生是通过对页表设置read only 在l
  • Docker Private Registry(docker私有仓库)

    Docker Registry 网上有很多的Registry服务器都支持第三方用户注册 xff0c 而后基于用户名去做自己的仓库 xff0c 但是使用互联网上的Registry有一个缺陷 xff0c 那就是我们去推送和下载镜像时都不会很快
  • harbor

    harbor简介 无论是使用Docker distribution去自建仓库 xff0c 还是通过官方镜像跑容器的方式去自建仓库 xff0c 通过前面的演示我们可以发现其是非常的简陋的 xff0c 还不如直接使用官方的Docker Hub去
  • Ansible介绍与安装

    1 介绍Ansible 什么是ansible xff1f ansible是新出现的自动化运维工具 xff0c 基于Python开发 xff0c 集合了众多运维工具 xff08 puppet cfengine chef func fabric
  • 定时打印进程信息shell脚本

    定时打印进程信息脚本 span class token shebang important bin sh span span class token comment 输入参数 span span class token assign lef
  • 部署ansible

    部署ansible 1 构建ansible清单1 1定义清单1 2使用静态清单指定受管主机1 3验证清单1 4覆盖清单的位置1 5构建ansible清单1 6自定义清单文件 2 管理Ansible配置文件2 1配置Ansible2 2配置文
  • ansible常用模块

    ansible常用模块 1 ansible常用模块使用详解2 ansible常用模块之ping3 ansible常用模块之command4 ansible常用模块之raw5 ansible常用模块之shell6 ansible常用模块之sc
  • ansible之playbook

    playbook 1 实施playbook1 1Ansible Playbook与临时命令1 2格式化Ansible Playbook1 3运行playbook1 4提高输出的详细程度1 5语法验证1 6执行空运行 2 实施多个play2
  • LAMP-手动架构部署

    1 lamp简介 有了前面学习的知识的铺垫 xff0c 今天可以来学习下第一个常用的web架构了 所谓lamp xff0c 其实就是由Linux 43 Apache 43 Mysql MariaDB 43 Php Perl Python的一
  • 华为云欧拉操作系统(OpenEuler)部署K8S集群

    华为云欧拉操作系统 xff08 OpenEuler xff09 部署K8S集群 需要提前准备好OpenEular操作系统虚拟机3台 xff0c 本文使用模板机创建 一 主机硬件要求 1 1 主机操作系统说明 序号操作系统及版本备注1open
  • 虚拟机centos关闭繁忙导致无法操作(已解决)

    虚拟机关闭提示虚拟机繁忙导致关闭失败的问题 最近复制了一台虚拟机 xff0c 但是在关闭的时候总是遇到关不上的问题 xff0c 虚拟机提示繁忙 xff0c 开关机和重启按钮一直是灰色图标 xff0c 不能点击 解决办法倒是找到了 xff0c
  • ubuntu桌面图标全部消失,快捷键也打不开终端解决办法

    前几天远程todesk连接实验室服务器过程中出现了ubuntu桌面图标全部消失的现象 按照C到的很多方法操作过都不行 奇怪的是快捷键终端也打不开 界面是下面这个样子的 xff1a 整个人束手无策 然后 xff01 解决方法就是 重启实验室的
  • Ubuntu系统进去之后黑屏是什么问题,及解决办法【最全】

    出现黑屏可能有多种原因 xff0c 以下是一些常见的解决方法 xff1a 1 检查电源线和视频连接线是否牢固 有时候连接线松动或者断开都可能导致黑屏 2 检查显卡驱动是否正确安装 如果显卡驱动没有正确安装 xff0c 可能会导致黑屏 你可以
  • 【在Ubuntu中安装Pycharm(Ubuntu22.04,Pycharm2022.3.3)】详细如何解压文件

    本文讲述了在Ubuntu中安装pycharm的具体步骤 准备环境 xff1a Ubuntu22 04 xff0c Pycharm2022 3 3 具体步骤 xff1a 1 首先下载pycharm xff1a Pycharm官方下载地址 我在
  • 算法—求x的平方根,只保留整数部分(Java)

    需求 给你一个非负整数 x xff0c 计算并返回 x 的 算术平方根 由于返回类型是整数 xff0c 结果只保留 整数部分 xff0c 小数部分将被 舍去 注意 xff1a 不允许使用任何内置指数函数和算符 xff0c 例如 pow x
  • Finding memory bugs with AddressSanitizer

    AddressSanitizer ASan is an instrumentation tool created by Google security researchers to identify memory access proble