二、实现
chunk header 的组成

如何判断进程是否可以访问一片内存区域?一个直观的想法是:给每个字节做个记号(poison state
),将进程不能够访问的字节标记为 poisoned(中毒),然后在每次访问内存之前先检查他的 poison state
,如果是 poisoned
,那么就可以判断发生了非法访问。
ASAN 的核心算法就是这个原理。ASAN 会使用一片专门的内存来保存 application memory
(应用内存) 每个字节的 poison state
。这片内存的专业名称为 shadow memory
,在访问地址之前,首先在 shadow memory
中检查地址是否中毒(poison state
),如果是中毒的(poisoned
),那么就可以判定发生了非法访问,ASAN 会及时报错错误。
ASAN 的实现会分为两个大部分:
一个是检查模块,在编译时发挥作用。在编译时,将程序中每个内存访问都按照上面描述的方式进行转换;并且在 stack object
和 global object
周围创建中毒区域 poisoned redzones
以检测是否发生溢出行为。
另一个是运行时库,在运行时发挥作用。在开启 ASAN 后,编译器会让程序动态链接 ASAN 运行时库。ASAN 的运行时库有如下作用:
本文着重谈就 heap-profiler 的实现原理。
1 | src/base/googleinit.h |
这个代码通过宏,C++ 的静态全局成员,使其可以在初始化阶段调用到类的构造函数,从而初始化到用户函数。
1 | src/heap-profiler.cc |
1
2
3
4
5
6
7在C++程序中使用ASAN(Address Sanitizer)和OpenSSL是可能存在兼容性问题的。ASAN是一种内存错误检测工具,它会对程序的内存使用情况进行跟踪和监测,以检测潜在的内存错误。OpenSSL是一个加密库,用于提供安全通信协议的实现,包括SSL和TLS。
由于ASAN会在程序运行时修改内存布局,可能会影响到OpenSSL的内存管理。这可能导致一些问题,例如OpenSSL中的指针可能会指向已经被ASAN修改过的地址,从而导致内存访问错误。
为了解决这个问题,可以使用特殊的编译选项来启用ASAN和OpenSSL的兼容性支持。例如,可以使用“-fsanitize=address -fno-omit-frame-pointer”编译选项来启用ASAN,同时使用“-DOPENSSL_NO_ASM -DOPENSSL_NO_INLINE_ASM”编译选项来禁用OpenSSL中的汇编代码。这些选项可以确保ASAN和OpenSSL之间的兼容性。
但是需要注意的是,即使启用了兼容性支持,仍然可能存在其他问题。因此,在使用ASAN和OpenSSL的组合时,建议仔细测试和调试程序,以确保程序的安全性和稳定性。1
2
3
4
5
6
7
8
9
10-DOPENSSL_NO_ASM -DOPENSSL_NO_INLINE_ASM
这些选项会禁用OpenSSL库中的汇编代码,并使用C代码替代。这可能会影响OpenSSL库的性能,但可以确保在使用AddressSanitizer(ASAN)等工具时的兼容性。
./config -d no-ssl2 no-ssl3 no-comp no-hw no-engine --prefix=<install_path> -DOPENSSL_NO_ASM -DOPENSSL_NO_INLINE_ASM
make
make install
这将使用禁用了汇编代码的选项进行编译,生成OpenSSL库并将其安装在指定的安装路径下。
需要注意的是,禁用汇编代码可能会影响OpenSSL库的性能。因此,在实际使用中,建议在评估性能和安全性之间做出权衡,根据具体情况选择是否禁用汇编代码。1
2
3
4
5
6
7
8
9
10OPENSSL_NO_ASAN是一个OpenSSL库中的编译选项,它用于禁用AddressSanitizer(ASAN)内存错误检测工具。
AddressSanitizer是一种内存错误检测工具,它可以帮助开发者在程序运行时发现内存错误,例如使用未初始化的内存、内存泄漏、缓冲区溢出等等。在C++程序中,使用ASAN工具可以帮助开发者检测潜在的内存错误,提高程序的安全性和稳定性。
然而,当OpenSSL库与ASAN工具一起使用时,可能会出现一些问题。因为ASAN工具会修改程序的内存布局,这可能会影响到OpenSSL库的内存管理。为了避免这种问题,OpenSSL库中提供了OPENSSL_NO_ASAN编译选项,可以禁用ASAN工具。
如果在编译OpenSSL库时定义了OPENSSL_NO_ASAN宏,那么OpenSSL库将不会使用ASAN工具进行内存错误检测。这可以避免ASAN工具可能会对OpenSSL库的影响,确保OpenSSL库的稳定性和安全性。
需要注意的是,禁用ASAN工具可能会降低OpenSSL库的安全性。因此,在使用OPENSSL_NO_ASAN编译选项时,建议使用其他安全检测工具或策略来确保程序的安全性。
选项:
1 | export ASAN_OPTIONS="quarantine_size_mb=15:malloc_context_size=5:detect_leaks=false:alloc_dealloc_mismatch=0:max_redzone=1024:report_globals=0" |
本文简单介绍线程局部缓存的原理,以及实现。本文代码来自于 glibc 2.28
。
线程局部缓存(Thread Local Storage
),他的特性就是线程私有的内存数据,每个线程都有,多线程场景中不会产生竞争,效率较高。
显式的 TLS 的 API 定义:
1 | /* Functions for handling thread-specific data. */ |
我曾经在编码开发时遇到过这样一个问题,代码中产生了死锁,可是我 review 了多次加锁、解锁的地方,却暂时没有发现有什么问题。最终发现是因为在代码中使用了 popen 系统调用,我把遇到的这个问题浓缩成如下的代码。
1 | pthread_mutex_t mtx; |
代码很简单,正常的业务加锁、解锁逻辑中,出现过一个 my_fork()
函数,而这个函数中会调用 fork,并且也同样使用了锁。此时就导致了死锁。如下会输出:
1 | id 18923 (parent) |