AddressSanitizer (aka ASan) is a memory error detector activated with the
-fsanitize=address
switch. Note that many of the typical memory errors,
such as use after free or buffer overflow, are detected by Ada’s Access_Check
and Index_Check
.
It can detect the following types of problems:
A memory overlay is a situation in which an object of one type is placed at the same memory location as a distinct object of a different type, thus overlaying one object over the other in memory. When there is an overflow because the objects do not overlap (like in the following example), the sanitizer can signal it.
procedure Wrong_Size_Overlay is type Block is array (Natural range <>) of Integer; Block4 : aliased Block := (1 .. 4 => 4); Block5 : Block (1 .. 5) with Address => Block4'Address; begin Block5 (Block5'Last) := 5; -- Outside the object end Wrong_Size_Overlay;
If the code is built with the -fsanitize=address
and -g
options,
the following error is shown at execution time:
... SUMMARY: AddressSanitizer: stack-buffer-overflow wrong_size_overlay.adb:7 in _ada_wrong_size_overlay ...
Ada’s Index_Check
detects buffer overflows caused by out-of-bounds array
access. If run-time checks are disabled, the sanitizer can still detect such
overflows at execution time the same way as it signalled the previous wrong
memory overlay. Note that if both the Ada run-time checks and the sanitizer
are enabled, the Ada run-time exception takes precedence.
procedure Buffer_Overrun is Size : constant := 100; Buffer : array (1 .. Size) of Integer := (others => 0); Wrong_Index : Integer := Size + 1 with Export; begin -- Access outside the boundaries Put_Line ("Value: " & Integer'Image (Buffer (Wrong_Index))); end Buffer_Overrun;
Ada’s Accessibility_Check
helps prevent use-after-return and
use-after-scope errors by enforcing lifetime rules. When these checks are
bypassed using Unchecked_Access
, sanitizers can still detect such
violations during execution.
with Ada.Text_IO; use Ada.Text_IO; procedure Use_After_Return is type Integer_Access is access all Integer; Ptr : Integer_Access; procedure Inner; procedure Inner is Local : aliased Integer := 42; begin Ptr := Local'Unchecked_Access; end Inner; begin Inner; -- Accessing Local after it has gone out of scope Put_Line ("Value: " & Integer'Image (Ptr.all)); end Use_After_Return;
If the code is built with the -fsanitize=address
and -g
options, the following error is shown at execution time:
... ==1793927==ERROR: AddressSanitizer: stack-use-after-return on address 0xf6fa1a409060 at pc 0xb20b6cb6cac0 bp 0xffffcc89c8b0 sp 0xffffcc89c8c8 READ of size 4 at 0xf6fa1a409060 thread T0 #0 0xb20b6cb6cabc in _ada_use_after_return use_after_return.adb:18 ... Address 0xf6fa1a409060 is located in stack of thread T0 at offset 32 in frame #0 0xb20b6cb6c794 in use_after_return__inner use_after_return.adb:9 This frame has 1 object(s): [32, 36) 'local' (line 10) <== Memory access at offset 32 is inside this variable SUMMARY: AddressSanitizer: stack-use-after-return use_after_return.adb:18 in _ada_use_after_return ...
A memory leak happens when a program allocates memory from the heap but fails to release it after it is no longer needed and loses all references to it like in the following example.
procedure Memory_Leak is type Integer_Access is access Integer; procedure Allocate is Ptr : Integer_Access := new Integer'(42); begin null; end Allocate; begin -- Memory leak occurs in the following procedure Allocate; end Memory_Leak;
If the code is built with the -fsanitize=address
and -g
options, the following error is emitted at execution time showing the
location of the offending allocation.
==1810634==ERROR: LeakSanitizer: detected memory leaks Direct leak of 4 byte(s) in 1 object(s) allocated from: #0 0xe3cbee4bb4a8 in __interceptor_malloc asan_malloc_linux.cpp:69 #1 0xc15bb25d0af8 in __gnat_malloc (memory_leak+0x10af8) (BuildId: f5914a6eac10824f81d512de50b514e7d5f733be) #2 0xc15bb25c9060 in memory_leak__allocate memory_leak.adb:5 ... SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).