[PATCH v3 01/11] sampling-asan: A sampling-based address sanitization for on-line memory bug detection
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Tue Oct 14 13:19:37 GMT 2025
On 30/09/25 03:46, Sung-hun Kim wrote:
> This patch introduces sampling-asan, a lightweight implementation of address sanitization (ASan)
> designed to reduce performance overhead of ASan.
> ASan implementation imposes significant performance overhead, discouraging developers from using
> ASan to verify their codes. As a result, developers use ASan only after a memory bug is discovered.
>
> Sampling-asan addresses this limitation by leveraging a sampling-based approach to track memory
> allocations and deallocations. This design significantly reduces performance overhead, so making it
> easier for developers to apply address sanitization for application in use.
> Users can configure sampling conditions through environment variables, which provides flexibility
> in balancing bug detection accuracy and performance.
>
> Sampling-asan is highly inspired and influenced by GWP-ASan in LLVM [1] and a published paper
> by Google [2]. Especially, a sampling-base approach used in sampling-asan is borrowed from the key
> concept of GWP-ASan. You can regard, this series of patches is an effort how to apply GWP-ASan
> into glibc malloc implementation. Of course, there are some different features and implementation
> from GWP-ASan's.
>
> Please refer a simple report of sampling-asan in Glibc Bugzilla:
>
> url: https://sourceware.org/bugzilla/show_bug.cgi?id=33461
>
> [1] https://llvm.org/docs/GwpAsan.html
> [2] Kostya Serebryany et al., GWP-ASan: Sampling-Based Detection of Memory-Safety Bugs in Production
> https://arxiv.org/abs/2311.09394
>
> Signed-off-by: Sung-hun Kim <sfoon.kim@samsung.com>
The idea is sound, does ths library add better coverage or debug options
compared to libasan or GWP-Asan? I am asking because this seems to
duplicate of what GWP-Asan already provides and it is not clear that
this proposal is trying to overcome some GWP-Asan limitation or
reimplement it with a different license.
I am trying to build on the supported platforms and it fails even with
the atomic_force_read change to atomic_load_relaxed:
samasan_report.c: In function ‘__report_printf’:
samasan_report.c:104:3: error: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
104 | write (fd, buffer, sizeof (buffer));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
I am not sure which option you used, but I think it would be good to use
as many option as possible (--enable-stack-protector=all, --enable-profile=yes,
and --enable-fortify-source=2 at least).
Below some comments to make this move forward. This is a *very* large proposal,
so I am focusing on the maintainability parts instead of the code itself.
> ---
> include/samasan.h | 23 +
> sampling-asan/Makefile | 29 ++
> sampling-asan/Versions | 17 +
> sampling-asan/samasan.h | 44 ++
> sampling-asan/samasan_allocate.c | 648 ++++++++++++++++++++++++
> sampling-asan/samasan_allocate.h | 69 +++
> sampling-asan/samasan_backtrace.c | 58 +++
> sampling-asan/samasan_backtrace.h | 30 ++
> sampling-asan/samasan_common.c | 25 +
> sampling-asan/samasan_common.h | 100 ++++
> sampling-asan/samasan_error.c | 117 +++++
> sampling-asan/samasan_error.h | 48 ++
> sampling-asan/samasan_fault_handler.c | 125 +++++
> sampling-asan/samasan_fault_handler.h | 24 +
> sampling-asan/samasan_init.c | 353 +++++++++++++
> sampling-asan/samasan_init.h | 34 ++
> sampling-asan/samasan_report.c | 244 +++++++++
> sampling-asan/samasan_report.h | 35 ++
> sampling-asan/samasan_sampling.c | 67 +++
> sampling-asan/samasan_sampling.h | 28 +
> sampling-asan/samasan_variable_init.def | 37 ++
> 21 files changed, 2155 insertions(+)
> create mode 100644 include/samasan.h
> create mode 100644 sampling-asan/Makefile
> create mode 100644 sampling-asan/Versions
> create mode 100644 sampling-asan/samasan.h
> create mode 100644 sampling-asan/samasan_allocate.c
> create mode 100644 sampling-asan/samasan_allocate.h
> create mode 100644 sampling-asan/samasan_backtrace.c
> create mode 100644 sampling-asan/samasan_backtrace.h
> create mode 100644 sampling-asan/samasan_common.c
> create mode 100644 sampling-asan/samasan_common.h
> create mode 100644 sampling-asan/samasan_error.c
> create mode 100644 sampling-asan/samasan_error.h
> create mode 100644 sampling-asan/samasan_fault_handler.c
> create mode 100644 sampling-asan/samasan_fault_handler.h
> create mode 100644 sampling-asan/samasan_init.c
> create mode 100644 sampling-asan/samasan_init.h
> create mode 100644 sampling-asan/samasan_report.c
> create mode 100644 sampling-asan/samasan_report.h
> create mode 100644 sampling-asan/samasan_sampling.c
> create mode 100644 sampling-asan/samasan_sampling.h
> create mode 100644 sampling-asan/samasan_variable_init.def
>
> diff --git a/include/samasan.h b/include/samasan.h
> new file mode 100644
> index 0000000000..cf8a445950
> --- /dev/null
> +++ b/include/samasan.h
> @@ -0,0 +1,23 @@
> +/* Sampling-asan for debugging memory allocation.
> + Copyright (C) 2015-2024 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +#ifndef _SAMASAN_H
> +
> +/* _SAMASAN_H will be defined in the below header. */
> +#include <sampling-asan/samasan.h>
> +
> +#endif /* samasan.h */
I think for internal header is simpler to just include the
sampling-asan/samasan.h instead of checking for a guard header that is
defined in a different file.
Also, since this is a new file the Copyright year should only the 2025
(same for other new files).
> diff --git a/sampling-asan/Makefile b/sampling-asan/Makefile
> new file mode 100644
> index 0000000000..70f639dab5
> --- /dev/null
> +++ b/sampling-asan/Makefile
> @@ -0,0 +1,29 @@
> +# Copyright (C) 2024-2025 Free Software Foundation, Inc.
> +# This file is part of the GNU C Library.
> +
> +# The GNU C Library is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU Lesser General Public
> +# License as published by the Free Software Foundation; either
> +# version 2.1 of the License, or (at your option) any later version.
> +
> +# The GNU C Library is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> +# Lesser General Public License for more details.
> +
> +# You should have received a copy of the GNU Lesser General Public
> +# License along with the GNU C Library; if not, see
> +# <http://www.gnu.org/licenses/>.
> +
> +# Makefile for sampling-asan routines
> +
> +subdir := sampling-asan
> +
> +include ../Makeconfig
> +
> +dist-headers := samasan.h
> +headers := $(dist-headers)
> +routines := samasan_allocate samasan_error samasan_sampling samasan_common \
> + samasan_init samasan_report samasan_backtrace samasan_fault_handler
Only file per line in alphabetical order:
routines := \
samasan_allocate \
...
samasan_sampling \
# routines
> +
> +include ../Rules
> diff --git a/sampling-asan/Versions b/sampling-asan/Versions
> new file mode 100644
> index 0000000000..0d7499558c
> --- /dev/null
> +++ b/sampling-asan/Versions
> @@ -0,0 +1,17 @@
> +libc {
> + GLIBC_PRIVATE {
> + #functions
> + samasan_allocate;
> + samasan_free;
> + samasan_init;
> + samasan_deinit;
> + samasan_sampling_ok;
> + samasan_is_pointer_in_sampling_pool;
> + samasan_get_size;
> + samasan_is_enabled;
> +
> + #testing
> + samasan_memory_pool_pause;
> + samasan_memory_pool_resume;
Why these function need to be in libc.so? I think a self-contained library, similar
to libasan, even we some suboptimal integration with the libc.so would be better
as an initial one than adding a lot of internal symbols on libc that exposes a
lot of functionalities.
> + }
> +}
> diff --git a/sampling-asan/samasan.h b/sampling-asan/samasan.h
> new file mode 100644
> index 0000000000..f5d8443dfa
> --- /dev/null
> +++ b/sampling-asan/samasan.h
> @@ -0,0 +1,44 @@
> +/* Prototypes for the sampling-asan interfaces.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_H
> +#define _SAMASAN_H
> +
> +#include <stddef.h> /* for size_t */
> +#include <stdbool.h> /* for bool type */
> +
> +/* a call for initializing samasan */
> +extern void samasan_init (void);
> +/* a call for sanitized malloc/realloc (see samasan_allocate.c) */
> +extern void *samasan_allocate (size_t size);
> +/* a call for free of sanitized allocation (see samasan_allocate.c) */
> +extern void samasan_free (void *ptr);
> +/* a call used to decide the allocation should be sampled or not */
> +extern bool samasan_sampling_ok (size_t size);
> +/* a call returns the size of the given memory chunk */
> +extern size_t samasan_get_size (void *ptr);
> +/* a call to determine the given pointer is located in the sampling pool */
> +extern bool samasan_is_pointer_in_sampling_pool (void *ptr);
> +/* a call to query whether the sampling-asan module is enabled or not */
> +extern bool samasan_is_enabled (void);
> +/* a call to pause the memory pool */
> +extern void samasan_memory_pool_pause (void);
> +/* a call to resume the memory pool */
> +extern void samasan_memory_pool_resume (void);
> +
> +#endif /* samasan.h */
> diff --git a/sampling-asan/samasan_allocate.c b/sampling-asan/samasan_allocate.c
> new file mode 100644
> index 0000000000..c021523e56
> --- /dev/null
> +++ b/sampling-asan/samasan_allocate.c
> @@ -0,0 +1,648 @@
> +/* Definitions for the sanitized memory allocation implementation.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +/* Sampling-asan (in short, samasan) tries to find memory bugs, such as
> + use-after-free, unaligned access, double free, unallocated free, and
> + so on with negligible performance degradation. Samasan uses a sampling
> + -based address sanitization to find memory bugs. That's why this mechanism
> + is called sampling-asan. */
Minor nit: double space after period.
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <sys/mman.h> /* for mmap/munmap */
> +#include <dso_handle.h> /* for __dso_handle */
> +#include <register-atfork.h> /* for __register_atfork */
> +
> +#include "samasan_init.h"
> +#include "samasan_common.h"
> +#include "samasan_error.h"
> +#include "samasan_sampling.h"
> +#include "samasan_allocate.h"
> +#include "samasan_backtrace.h"
> +
> +/* Definitions of some terms used in sampling-asan:
> + - memory_pool: A memory_pool is a reserved memory space to serve sanitized
> + memory allocation. Its size can be configured by environmental variables
> + (please refer samasan_init.c). The size of the memory pool is decided by
> + the below fomula:
> + size = SAMASAN_MAX_ALLOC_SIZE * SAMASAN_MAX_ON_GOING_ALLOCATIONS
> + + (SAMASAN_PARTITION_SIZE + 1) * SAMASAN_MAX_ON_GOING_ALLOCATIONS
> + - memory_pool_block: A memory_pool_block is composed of a number of pages.
> + It serves a sanitized memory allocation to users. The size of a
> + memory_pool_block is decided by SAMASAN_MAX_ALLOC_SIZE.
> + - chunk: A served memory area to users is called a chunk. A chunk should
> + be located in a memory_pool_block and must be less than a memory_pool_block.
> + - memory_pool_partition: A memory_pool_partition is a protected zone which
> + is located in the first and last places of the memory pool and between two
> + memory_pool_blocks. Its size is decided by SAMASAN_PARTITION_SIZE. If an
> + user tries to access a memory_pool_partition, the program will get the
> + segmentation fault.
> + - memory_pool_entry: It means that a pair of a memory_pool_partition and
> + a memory_pool_block. It is only used for the address calculation. */
> +
> +static size_t memory_pool_size;
> +static size_t memory_page_size;
> +
> +static size_t memory_pool_block_size;
> +static size_t memory_pool_partition_size;
> +static size_t memory_pool_entry_size;
> +
> +static void *memory_pool;
> +static struct memory_pool_entry_info *memory_pool_metadata;
> +static int memory_pool_entry_in_use;
> +
> +#define DEFAULT_GUARD_PATTERN 0xFF
> +const static char guard_pattern = DEFAULT_GUARD_PATTERN;
This is user a constexpr, there is no need to add on .data. Just use
DEFAULT_GUARD_PATTERN instead.
> +
> +struct samasan_mutex free_list_mutex;
> +struct memory_pool_entry_info *free_list_head;
> +struct memory_pool_entry_info *free_list_tail;
> +
> +static uintptr_t memory_pool_begin;
> +static uintptr_t memory_pool_end;
> +/* memory_pool_begin is aligned in a page-width. Because of that, if
> + memory_pool_block_size is a multiple of a page, calculating the address
> + of a memory chunk should be offsetted by a relative address of
> + memory_pool_begin in a memory_pool_block_size. */
> +static uintptr_t memory_pool_offset;
> +
> +/* TODO: Recursive calling of samasan functions should be prevented.
> + It can be tested when sampling-asan is applied malloc code base.
> + So, the test code should be written after applying sampling-asan.*/
> +SAMASAN_TLS_SPECIFIER bool is_in_samasan = false;
You use a similar strategy of sysdeps/unix/sysv/linux/getrandom.c vDSO
handling to detect recursive calls.
> +
> +#define get_memory_pool_index_by_entry_info(entry) ((uintptr_t) entry - \
> + (uintptr_t) memory_pool_metadata) / sizeof (struct memory_pool_entry_info)
> +#define get_partition_bytes_by_entry_info(entry) \
> + ((get_memory_pool_index_by_entry_info (entry) + 1) \
> + * memory_pool_partition_size)
> +#define get_block_bytes_by_entry_info(entry) \
> + (get_memory_pool_index_by_entry_info (entry) * memory_pool_block_size)
> +#define get_memory_pool_block_address_by_entry_info(entry) \
> + (void *) (memory_pool_begin \
> + + get_partition_bytes_by_entry_info (entry) \
> + + get_block_bytes_by_entry_info (entry))
> +
> +#define get_blocks(ptr) ((uintptr_t) ptr - memory_pool_begin) \
> + / memory_pool_entry_size
> +#define get_partitions(ptr) get_blocks (ptr)
> +#define get_block_bytes_by_pointer(ptr) (get_blocks (ptr) \
> + * memory_pool_block_size)
> +#define get_partition_bytes_by_pointer(ptr) (get_partitions (ptr) \
> + * memory_pool_partition_size)
> +#define get_memory_pool_block_offset(ptr) (get_block_bytes_by_pointer (ptr) \
> + + get_partition_bytes_by_pointer (ptr))
> +#define get_memory_pool_index_by_pointer(ptr) \
> + (get_memory_pool_block_offset (ptr) / memory_pool_entry_size)
> +
> +bool is_pointer_in_partition (void *ptr)
I think this should be a static inline/__always_inline.
> +{
> + return (uintptr_t) ptr - memory_pool_begin
> + - get_memory_pool_block_offset (ptr) < memory_pool_partition_size;
> +}
> +
> +static inline struct memory_pool_entry_info *
> +get_relative_memory_pool_entry_from_pointer (void *ptr, ssize_t relative)
> +{
> + ssize_t index = (ssize_t) get_memory_pool_index_by_pointer (ptr) + relative;
> +
> + if (SAMASAN_UNLIKELY (index < 0))
> + return NULL;
> + if (SAMASAN_UNLIKELY ((size_t) index >= memory_pool_size))
> + return NULL;
> + return &memory_pool_metadata[index];
> +}
> +
> +struct memory_pool_entry_info *
> +get_memory_pool_entry_from_pointer (void *ptr)
> +{
> + if (is_pointer_in_partition (ptr))
> + /* invalid access */
> + return NULL;
> + return get_relative_memory_pool_entry_from_pointer (ptr, 0);
> +}
> +
> +struct memory_pool_entry_info *
> +get_previous_memory_pool_entry_from_pointer (void *ptr)
> +{
> + return get_relative_memory_pool_entry_from_pointer (ptr, 0);
> +}
> +
> +struct memory_pool_entry_info *
> +get_next_memory_pool_entry_from_pointer (void *ptr)
> +{
> + return get_relative_memory_pool_entry_from_pointer (ptr, 1);
> +}
> +
> +/* A memory_pool_entry is detached from the free list */
> +static inline struct memory_pool_entry_info *
> +get_memory_pool_entry_from_free_list (void)
> +{
> + struct memory_pool_entry_info *entry;
> +
> + samasan_assert (free_list_head != NULL, "free list is emptied!\n");
> + if (free_list_head == free_list_tail)
> + free_list_tail = NULL;
> + entry = free_list_head;
> + free_list_head = entry->list;
> + entry->list = NULL;
> + return entry;
> +}
> +
> +static inline void
> +put_memory_pool_entry_in_free_list (struct memory_pool_entry_info *entry)
> +{
> + if (free_list_tail)
> + {
> + free_list_tail->list = entry;
> + free_list_tail = entry;
> + }
> + else if (free_list_tail == NULL && free_list_head == NULL)
> + free_list_head = free_list_tail = entry;
> +}
> +
> +static struct memory_pool_entry_info *
> +get_free_memory_pool_entry (void)
> +{
> + struct memory_pool_entry_info *entry;
> +
> + if (SAMASAN_UNLIKELY (samasan_atomic_read (memory_pool_entry_in_use)
> + >= memory_pool_size))
> + return NULL;
> + samasan_mutex_lock (&free_list_mutex);
> + entry = get_memory_pool_entry_from_free_list ();
> + samasan_mutex_unlock (&free_list_mutex);
> + entry->is_free = false;
> + samasan_atomic_inc (&memory_pool_entry_in_use);
> + return entry;
> +}
> +
> +#define get_pointer_offset(ptr) ((uintptr_t) ptr % memory_page_size)
> +#define get_page_address(ptr) ((uintptr_t) ptr - get_pointer_offset (ptr))
> +#define get_page_pointer(ptr) (void *) get_page_address (ptr)
> +#define round_up(size, bound) ((size + bound - 1) & ~(bound - 1))
> +
> +static inline size_t
> +get_page_size (void *ptr, size_t size)
> +{
> + size_t offset = (uintptr_t) ptr - get_page_address (ptr);
> + return round_up (offset + size, memory_page_size);
> +}
> +
> +static bool
> +protect_range_in_block (void *start, size_t size)
> +{
> + return mprotect (get_page_pointer (start), get_page_size (start, size),
> + PROT_NONE) == 0;
> +}
> +
> +static bool
> +unprotect_range_in_block (void *start, size_t size)
> +{
> + return mprotect (get_page_pointer (start), get_page_size (start, size),
> + PROT_READ | PROT_WRITE) == 0;
> +}
> +
> +/* Styles for picking a memory chunk in a memory block:
> + Mostly, a memory chunk is smaller than a memory block. So, samasan should
> + pick the address in a selected memory block to provide a memory chunk.
> + The picker can take the leftmost address (left-aligned) or the rightmost
> + address (right-aligned) while the memory chunk does not exceed the boundary
> + of the memory block. Of course, it can pick the center address of the memory
> + block (center-aligned). The picking style can be configured by using a
> + environmental variable (SAMASAN_CHUNK_PICK). For details, please refer
> + samasan_init.c. */
> +static memory_chunk_pick_style_t chunk_pick_style;
> +
> +#define pick_from_left(block, size) (void *) ((uintptr_t) block)
> +#define pick_from_right(block, size) (void *) ((uintptr_t) block \
> + + (uintptr_t) ((memory_pool_block_size)) - size)
> +#define pick_center(block, size) (void *) ((uintptr_t) block \
> + + (uintptr_t) ((memory_pool_block_size - size) >> 1))
> +#define pick_chunk(block, size) \
> + (chunk_pick_style == PICK_FROM_LEFT ? pick_from_left (block, size) : \
> + chunk_pick_style == PICK_FROM_RIGHT ? pick_from_right (block, size) : \
> + pick_center (block, size))
> +#define get_aligned(ptr) (void *) ((uintptr_t) ptr & ~(sizeof (size_t) - 1))
> +
> +static inline void *
> +allocate_in_memory_pool_block (void *block, size_t size)
> +{
> + void *chunk = get_aligned (pick_chunk (block, size));
> + samasan_assert (unprotect_range_in_block (chunk, size),
> + "mprotect on the allocated range is failed");
> + return chunk;
> +}
> +
> +static void *
> +allocate_in_memory_pool (size_t size)
> +{
> + struct memory_pool_entry_info *entry;
> + void *block;
> + void *chunk = NULL;
> +
> + check_in_samasan ();
> + entry = get_free_memory_pool_entry ();
> + if (!entry)
> + goto no_entry_out;
> +
> + block = get_memory_pool_block_address_by_entry_info (entry);
> + chunk = allocate_in_memory_pool_block (block, size);
> + entry->chunk_size = size;
> + entry->address = (uintptr_t) chunk;
> + get_backtrace (&entry->allocation_trace);
> +
> +no_entry_out:
> + check_out_samasan();
> + return chunk;
> +}
> +
> +/* A guard pattern is a predefined character to detect a modification in
> + invalid memory area. If the illegal memory modification occurrs in the
> + unprotected memory block, the kernel cannot raise the fault due to the
> + page-wise resolution of the mprotect() call. So, samasan fills the rest
> + of the memory block by the guard pattern. If the pattern is modified,
> + samasan regards it as an invalid modification and raises an error. */
> +
> +static void
> +fill_guard_pattern (void *start, size_t size)
> +{
> + memset(start, guard_pattern, size);
> +}
> +
> +static void
> +check_guard_pattern (void *chunk, size_t size)
> +{
> + char *ptr = (char *) get_page_pointer (chunk);
> + size_t chunk_page_size = get_page_size (chunk, size);
> +
> + for (size_t i = 0; i < chunk_page_size;)
> + {
> + if (&ptr[i] == chunk)
> + {
> + i += size;
> + continue;
> + }
> + if (ptr[i] != guard_pattern)
> + raise_fault_with_address(INVALID_WRITE, (uintptr_t) (ptr + i));
> + i++;
> + }
> +}
> +
> +#define is_address_in_chunk(address, entry) (address >= entry->address && \
> + address < (entry->address + entry->chunk_size))
> +
> +bool
> +is_out_of_chunk_access (uintptr_t address, struct memory_pool_entry_info *entry)
> +{
> + if (!is_address_in_chunk (address, entry))
> + return true;
> + return false;
> +}
> +
> +#define is_address_in_memory_pool(address) ((address >= memory_pool_begin) \
> + && (address < memory_pool_end))
> +
> +bool
> +is_pointer_in_memory_pool (void *ptr)
> +{
> + return is_address_in_memory_pool ((uintptr_t) ptr);
> +}
> +
> +static void
> +deallocate_memory_chunk (void *ptr)
> +{
> + struct memory_pool_entry_info *entry =
> + &memory_pool_metadata[get_memory_pool_index_by_pointer (ptr)];
> + uintptr_t address = (uintptr_t) ptr;
> +
> + if (entry->is_free)
> + {
> + /* Double free: A case that the program tries to free
> + on the freed object. */
> + raise_fault_with_address (DOUBLE_FREE, (uintptr_t) ptr);
> + }
> + if (entry->address != address)
> + {
> + /* Invalid free: The pointer does not point the start address
> + of the chunk. */
> + raise_fault_with_address (INVALID_FREE, address);
> + }
> + check_guard_pattern(ptr, entry->chunk_size);
> + fill_guard_pattern(ptr, entry->chunk_size);
> + samasan_assert (protect_range_in_block (ptr, entry->chunk_size) == true,
> + "mprotect on deallocated range is failed");
> + entry->is_free = true;
> + entry->chunk_size = 0;
> + entry->address = 0;
> +
> + get_backtrace (&entry->deallocation_trace);
> +
> + samasan_mutex_lock (&free_list_mutex);
> + put_memory_pool_entry_in_free_list (entry);
> + samasan_mutex_unlock (&free_list_mutex);
> + samasan_atomic_dec (&memory_pool_entry_in_use);
> +}
> +
> +/* Sampling-asan API - samasan_allocate (size_t):
> + Allocate a memory chunk in the preallocated memory pool. If a given
> + allocation size is larger than memory_pool_block_size (or zero), it
> + does not allow to allocate a memory chunk in the memory pool. */
> +
> +void *
> +samasan_allocate (size_t size)
> +{
> + if (SAMASAN_UNLIKELY (!is_samasan_enabled ()))
> + return NULL;
> + if (SAMASAN_UNLIKELY (is_call_in_samasan ()))
> + return NULL;
> + if (SAMASAN_UNLIKELY (size > memory_pool_block_size || size == 0))
> + return NULL;
> + if (SAMASAN_UNLIKELY (samasan_atomic_read (memory_pool_entry_in_use)
> + >= memory_pool_size))
> + return NULL;
> + return allocate_in_memory_pool (size);
> +}
> +
> +/* Sampling-asan API - samasan_free (void *):
> + Return an allocated memory chunk to the memory pool.
> + Before free, samasan checks the given pointer is a memory chunk
> + located in the memory pool. If not, samasan just returns without
> + free. */
> +
> +void
> +samasan_free (void *ptr)
> +{
> + uintptr_t address = (uintptr_t) ptr;
> + if (SAMASAN_UNLIKELY (!is_address_in_memory_pool (address)))
> + return;
> + check_in_samasan ();
> + deallocate_memory_chunk (ptr);
> + check_out_samasan();
> +}
> +
> +/* Sampling-asan API - samasan_sampling_ok (size_t)
> + The purpose of sampling-asan is to provide address sanitization
> + without paying the expensive cost. To this end, this function is given
> + to decide whether this memory allocation is sanitized or not. */
> +
> +bool
> +samasan_sampling_ok (size_t size)
> +{
> + if (SAMASAN_LIKELY (!is_samasan_enabled ()))
> + return false;
> + if (SAMASAN_UNLIKELY (is_call_in_samasan ()))
> + return false;
> + if (SAMASAN_UNLIKELY (samasan_atomic_read (memory_pool_entry_in_use)
> + >= memory_pool_size))
> + return false;
> + if (SAMASAN_UNLIKELY (size > memory_pool_block_size || size == 0))
> + return false;
> + if (SAMASAN_LIKELY (!decide_allocation_sampling ()))
> + return false;
> + return true;
> +}
> +
> +/* Sampling-asan API: samasan_get_size (void *)
> + This function returns the size of the given memory chunk. If the given
> + pointer is not an address in the memory pool or an already freed memory
> + chunk, it returns zero. */
> +
> +size_t
> +samasan_get_size (void *ptr)
> +{
> + struct memory_pool_entry_info *entry;
> +
> + if (SAMASAN_UNLIKELY (!is_pointer_in_memory_pool (ptr)))
> + return 0;
> + entry = get_memory_pool_entry_from_pointer (ptr);
> + if (SAMASAN_UNLIKELY (!entry))
> + return 0;
> + if (entry->is_free)
> + return 0;
> + return entry->chunk_size;
> +}
> +
> +/* Sampling-asan API: samasan_is_pointer_in_sampling_pool (void *)
> + It returns true if the given pointer is an address in the memory pool
> + even though it is invalid. */
> +
> +bool
> +samasan_is_pointer_in_sampling_pool (void *ptr)
> +{
> + /* We do not check the pointer is valid to detect double-free cases. */
> + if (SAMASAN_UNLIKELY (is_pointer_in_memory_pool (ptr)))
> + return true;
> + return false;
> +}
> +
> +/* A memory pool is composed of memory blocks and partitions.
> + Sampling-asan uses memory blocks to serve sanitized memory allocation while
> + partitions are used to detect invalid memory accesses.
> +
> + The below figure depicts a case that a memory block is composed of two pages.
> +
> + |--<partition>--|----------<memory block>-------------|--<partition>--|--
> + |--<partition>--|--<block page-1>--|--<block page-2>--|--<partition>--|--
> +
> + If a partition is accessed, the kernel raises a segmentation fault because
> + it is protected by a mprotect call. Block pages are allowed to be accessed
> + but the check-on-free will detect invalid updates on unallocated area in
> + block pages. */
> +
> +static void *
> +memory_pool_allocate (size_t size, size_t block_size)
> +{
> + void *pool = mmap (NULL, size, PROT_READ | PROT_WRITE,
> + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
> + samasan_assert (pool != MAP_FAILED, "memory pool allocation is failed");
> + for (void *block = pool + memory_pool_partition_size; block < pool + size;
> + block += (block_size + memory_pool_partition_size))
> + fill_guard_pattern (block, block_size);
> + samasan_assert (mprotect (pool, size, PROT_NONE) == 0, "mprotect failed");
> + memory_pool_begin = (uintptr_t) pool;
> + memory_pool_end = (uintptr_t) pool + size;
> + memory_pool_offset = (uintptr_t) pool % memory_pool_block_size;
> + return pool;
> +}
> +
> +static void
> +memory_pool_deallocate (void *pool)
> +{
> + samasan_assert (munmap (pool, memory_pool_block_size * memory_pool_size
> + + memory_pool_partition_size * (memory_pool_size + 1)) == 0,
> + "memory pool deallocation is failed");
> +}
> +
> +static void *
> +memory_pool_metadata_allocate (size_t size)
> +{
> + void *metadata = mmap (NULL, size, PROT_READ | PROT_WRITE,
> + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
> + samasan_assert (metadata != MAP_FAILED,
> + "memory pool metadata allocation is failed");
> + return metadata;
> +}
> +
> +static void
> +memory_pool_metadata_deallocate (void *metadata)
> +{
> + samasan_assert (munmap (metadata, sizeof (struct memory_pool_entry_info)
> + * memory_pool_size) == 0,
> + "memory pool metadata deallocation is failed");
> +}
> +
> +static void
> +memory_pool_metadata_init (void)
> +{
> + memset (memory_pool_metadata, 0, sizeof (struct memory_pool_entry_info)
> + * memory_pool_size);
> + for (size_t i = 0; i < memory_pool_size; i++)
> + {
> + memory_pool_metadata[i].is_free = true;
> + if (SAMASAN_UNLIKELY (!free_list_head))
> + free_list_head = &memory_pool_metadata[i];
> + else
> + {
> + memory_pool_metadata[i].list = free_list_head;
> + free_list_head = &memory_pool_metadata[i];
> + }
> + if (SAMASAN_UNLIKELY (!free_list_tail))
> + free_list_tail = &memory_pool_metadata[i];
> + }
> +}
> +
> +static size_t
> +get_page_aligned_alloc_size (uint32_t size)
> +{
> + if (size % memory_page_size > 0)
> + return size - (size % memory_page_size) + memory_page_size;
> + return size;
> +}
> +
> +/* Functions for pthread_atfork() */
> +static bool fork_handler_installed = false;
> +
> +/* Sampling-asan API: samasan_memory_pool_pause (void)
> + It stops the memory pool to synchronize with the child. */
> +
> +void
> +samasan_memory_pool_pause (void)
> +{
> + samasan_mutex_lock (&free_list_mutex);
> +}
> +
> +/* Sampling-asan API: samasan_memory_pool_resume (void)
> + It restarts the memory pool to synchronize with the child. */
> +
> +void
> +samasan_memory_pool_resume (void)
> +{
> + samasan_mutex_unlock (&free_list_mutex);
> +}
> +
> +void
> +samasan_install_fork_handler (void)
> +{
> + if (!fork_handler_installed)
> + {
> + __register_atfork (samasan_memory_pool_pause,
> + samasan_memory_pool_resume,
> + samasan_memory_pool_resume,
> + __dso_handle);
> + fork_handler_installed = true;
> + }
> +}
> +
> +void
> +samasan_uninstall_fork_handler (void)
> +{
> + if (fork_handler_installed)
> + {
> + UNREGISTER_ATFORK (__dso_handle);
> + fork_handler_installed = false;
> + }
> +}
> +
> +bool
> +samasan_memory_pool_init (uint32_t pool_size,
> + uint32_t alloc_size,
> + uint32_t partition_size,
> + memory_chunk_pick_style_t style)
> +{
> + size_t memory_pool_size_in_bytes;
> +
> + memory_page_size = get_system_page_size ();
> + memory_pool_size = pool_size;
> + if (alloc_size >= memory_page_size)
> + memory_pool_block_size = get_page_aligned_alloc_size (alloc_size);
> + else
> + memory_pool_block_size = memory_page_size;
> + memory_pool_partition_size = partition_size;
> + memory_pool_entry_size = memory_pool_block_size + memory_pool_partition_size;
> + memory_pool_size_in_bytes = memory_pool_block_size
> + * memory_pool_size
> + + (memory_pool_size + 1)
> + * memory_pool_partition_size;
> +
> + memory_pool = memory_pool_allocate (memory_pool_size_in_bytes,
> + memory_pool_block_size);
> + if (SAMASAN_UNLIKELY (!memory_pool))
> + return false;
> + memory_pool_metadata = memory_pool_metadata_allocate (
> + sizeof (struct memory_pool_entry_info)
> + * memory_pool_size);
> + if (SAMASAN_UNLIKELY (!memory_pool_metadata))
> + {
> + memory_pool_deallocate (memory_pool);
> + return false;
> + }
> +
> + chunk_pick_style = style;
> + memory_pool_metadata_init ();
> + samasan_mutex_init (&free_list_mutex);
> + samasan_atomic_write (&memory_pool_entry_in_use, 0);
> + return true;
> +}
> +
> +static inline void
> +memory_pool_free_list_deinit (void)
> +{
> + free_list_head = free_list_tail = NULL;
> +}
> +
> +void
> +samasan_memory_pool_deinit (void)
> +{
> + if (memory_pool)
> + memory_pool_deallocate (memory_pool);
> + memory_pool = NULL;
> + if (memory_pool_metadata)
> + memory_pool_metadata_deallocate (memory_pool_metadata);
> + memory_pool_metadata = NULL;
> +
> + memory_pool_free_list_deinit ();
> +
> + memory_pool_block_size = 0;
> + memory_pool_partition_size = 0;
> + memory_pool_entry_size = 0;
> + memory_page_size = 0;
> + memory_pool_size = 0;
> + chunk_pick_style = PICK_END;
> +}
> diff --git a/sampling-asan/samasan_allocate.h b/sampling-asan/samasan_allocate.h
> new file mode 100644
> index 0000000000..ce611cbeba
> --- /dev/null
> +++ b/sampling-asan/samasan_allocate.h
> @@ -0,0 +1,69 @@
> +/* Prototypes and type definitions for sanitized allocation in the
> + sampling-asan implementation.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_ALLOCATE_H
> +#define _SAMASAN_ALLOCATE_H
> +
> +#include <stdbool.h>
> +
> +/* For allocation/deallocation traces */
> +#define INVALID_TID 0UL
> +
> +#define ALLOCATION_TRACE_SIZE 256
> +struct memory_pool_trace {
> + void *stacktrace[ALLOCATION_TRACE_SIZE];
> + size_t trace_size;
> + uint64_t tid;
> +};
> +
> +// TODO: change the type of chunk_size
> +struct memory_pool_entry_info {
> + struct memory_pool_trace allocation_trace;
> + struct memory_pool_trace deallocation_trace;
> + struct memory_pool_entry_info *list; /* listed in a free list */
> + uintptr_t address;
> + uint32_t chunk_size:31;
> + bool is_free:1;
> +};
> +
> +typedef enum memory_chunk_pick_style {
> + PICK_FROM_LEFT = 0,
> + PICK_CENTER,
> + PICK_FROM_RIGHT,
> + PICK_END,
> +} memory_chunk_pick_style_t;
> +
> +extern struct memory_pool_entry_info
> + *get_memory_pool_entry_from_pointer (void *ptr);
> +extern struct memory_pool_entry_info
> + *get_previous_memory_pool_entry_from_pointer (void *ptr);
> +extern struct memory_pool_entry_info
> + *get_next_memory_pool_entry_from_pointer (void *ptr);
> +extern bool is_pointer_in_partition (void *ptr);
> +extern bool is_out_of_chunk_access (uintptr_t address,
> + struct memory_pool_entry_info *entry);
> +extern bool is_pointer_in_memory_pool (void *ptr);
> +extern void samasan_install_fork_handler (void);
> +extern void samasan_uninstall_fork_handler (void);
> +extern bool samasan_memory_pool_init (uint32_t pool_size,
> + uint32_t alloc_size, uint32_t partition_size,
> + memory_chunk_pick_style_t style);
> +extern void samasan_memory_pool_deinit (void);
> +
> +#endif /* samasan_allocate.h */
> diff --git a/sampling-asan/samasan_backtrace.c b/sampling-asan/samasan_backtrace.c
> new file mode 100644
> index 0000000000..9317025b14
> --- /dev/null
> +++ b/sampling-asan/samasan_backtrace.c
> @@ -0,0 +1,58 @@
> +/* Definitions for the backtracing feature used in sampling-asan.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <execinfo.h>
> +#include <unistd.h>
> +
> +#include "samasan_allocate.h"
> +
> +/* By default, we use glibc's backtrace to get backtrace. */
> +void
> +get_backtrace (struct memory_pool_trace *trace)
> +{
> + trace->trace_size =
> + backtrace ((void **) trace->stacktrace, ALLOCATION_TRACE_SIZE);
> + trace->tid = gettid ();
> +}
> +
> +void
> +print_backtrace (struct memory_pool_trace *trace, int stream,
> + void (*report_printf) (int, const char *, ...))
> +{
> + char **symbols;
> +
> + if (trace->trace_size == 0)
> + {
> + report_printf(stream, "samasan error - Cannot track the stack trace\n");
> + return;
> + }
> +
> + symbols = backtrace_symbols ((void **) trace->stacktrace, trace->trace_size);
> + for (size_t i = 0; i < trace->trace_size; i++)
> + {
> + if (!symbols)
> + report_printf (stream, " #%zu %p\n", i, trace->stacktrace[i]);
> + else
> + report_printf (stream, " #%zu %s\n", i, symbols[i]);
> + }
> + if (symbols)
> + free (symbols);
> +}
Is this really safe? Both function call malloc itself (backtrack through libgcc_s.so
dlopen) and sampling-asan issues them on multiple places including in a signal
handler (segfault_handler).
> diff --git a/sampling-asan/samasan_backtrace.h b/sampling-asan/samasan_backtrace.h
> new file mode 100644
> index 0000000000..cbbf4280b4
> --- /dev/null
> +++ b/sampling-asan/samasan_backtrace.h
> @@ -0,0 +1,30 @@
> +/* Prototypes for the backtrace feature used in sampling-asan.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_BACKTRACE_H
> +#define _SAMASAN_BACKTRACE_H
> +
> +#include <stdio.h>
> +
> +#include "samasan_allocate.h"
> +
> +extern void get_backtrace (struct memory_pool_trace *trace);
> +extern void print_backtrace (struct memory_pool_trace *trace, int stream,
> + void (report_printf) (int, const char *, ...));
> +
> +#endif /* samasan_backtrace.h */
> diff --git a/sampling-asan/samasan_common.c b/sampling-asan/samasan_common.c
> new file mode 100644
> index 0000000000..472eebb1f0
> --- /dev/null
> +++ b/sampling-asan/samasan_common.c
> @@ -0,0 +1,25 @@
> +/* Definitions for sampling-asan common functions.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include "samasan_common.h"
> +
> +bool
> +samasan_mutex_trylock (struct samasan_mutex *mutex)
> +{
> + return __libc_lock_trylock (mutex->lock);
> +}
> diff --git a/sampling-asan/samasan_common.h b/sampling-asan/samasan_common.h
> new file mode 100644
> index 0000000000..e15559c907
> --- /dev/null
> +++ b/sampling-asan/samasan_common.h
> @@ -0,0 +1,100 @@
> +/* Prototypes and definitions for sampling-asan common functions.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_COMMON_H
> +#define _SAMASAN_COMMON_H
> +
> +#include <stdio.h> /* for fprintf */
> +#include <stddef.h> /* for size_t */
> +#include <stdbool.h> /* for bool type */
> +#include <unistd.h> /* for sysconf */
> +#include <libc-lock.h> /* for mutex */
> +#include <atomic.h> /* for atomic operations */
> +
> +#define SAMASAN_TLS_SPECIFIER __thread \
> + __attribute__((tls_model("initial-exec")))
> +
> +#define SAMASAN_UNLIKELY(cond) __builtin_expect (!!(cond), 0)
> +#define SAMASAN_LIKELY(cond) __builtin_expect (!!(cond), 1)
Use __glibc_likely / __glibc_unlikely.
> +
> +#define samasan_atomic_read(var) atomic_forced_read (var)
We don't have atomic_forced_read anymore (adbd3ba137e940c2858d5dbd210335fdc703d3eb),
please check if atomic_read_relaxed is correct on the place it usses.
> +#define samasan_atomic_write(var, val) atomic_store_relaxed (var, val)
> +#define samasan_atomic_inc(var) atomic_fetch_add_release (var, 1)
> +#define samasan_atomic_dec(var) atomic_fetch_add_release (var, -1)
I think these kind of macro that redefine some primitive confuses more
than help readibility (like the old atomic_compare_and_exchange_bool_acq,
which that different argument order than the C11-atomic like macro).
It is better to just use the atomic macros directly.
> +
> +extern SAMASAN_TLS_SPECIFIER bool is_in_samasan; /* defined in samasan_allocate.c */
> +#define check_in_samasan() is_in_samasan = true
> +#define check_out_samasan() is_in_samasan = false
> +#define is_call_in_samasan() is_in_samasan == true
> +
> +static inline size_t
> +get_system_page_size (void)
> +{
> + return sysconf(_SC_PAGESIZE);
> +}
> +
> +static inline void
> +__samasan_exit (void)
> +{
> + __builtin_trap ();
> +}
> +
> +#define samasan_exit_with_message(...) do \
> +{ \
> + fprintf (stderr, __VA_ARGS__); \
> + __samasan_exit (); \
> +} while (0); \
> +
> +static inline void
> +__samasan_assert
> +(bool condition, const char *msg, const char *file, unsigned long line)
> +{
> + if (condition)
> + return;
> + samasan_exit_with_message ("%s: %s: %lu: %s %m\n", file, __func__, line, msg);
> + /* never reached */
> +}
> +
> +#define samasan_assert(condition, msg) \
> + __samasan_assert(condition, msg, __FILE__, __LINE__)
> +
> +struct samasan_mutex {
> + __libc_lock_define (, lock);
> +};
> +
> +static inline void
> +samasan_mutex_init (struct samasan_mutex *mutex)
> +{
> + __libc_lock_init (mutex->lock);
> +}
> +
> +static inline void
> +samasan_mutex_lock (struct samasan_mutex *mutex)
> +{
> + __libc_lock_lock (mutex->lock);
> +}
> +
> +static inline void
> +samasan_mutex_unlock (struct samasan_mutex *mutex)
> +{
> + __libc_lock_unlock (mutex->lock);
> +}
> +
> +extern bool samasan_mutex_trylock (struct samasan_mutex *mutex);
> +
> +#endif /* samasan_common.h */
> diff --git a/sampling-asan/samasan_error.c b/sampling-asan/samasan_error.c
> new file mode 100644
> index 0000000000..f2c37f5b1f
> --- /dev/null
> +++ b/sampling-asan/samasan_error.c
> @@ -0,0 +1,117 @@
> +/* Definitions for the error functions in sampling-asan.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <stdint.h>
> +#include <sys/mman.h> /* for mmap/munmap */
> +
> +#include "samasan_common.h"
> +#include "samasan_error.h"
> +#include "samasan_report.h"
> +#include "samasan_allocate.h"
> +
> +/* tracking error */
> +static samasan_error_t current_error = NO_ERROR;
> +static uintptr_t fault_address;
> +static void *fault_area;
> +
> +static const char *error_to_string[] = {
> + "INVALID_FREE",
> + "DOUBLE_FREE",
> + "USE_AFTER_FREE",
> + "INVALID_ACCESS",
> + "INVALID_WRITE",
> + "OUT_OF_MEMORY_POOL",
> + "UNKNOWN_ERROR",
> +};
> +
> +const char *
> +get_error_name (samasan_error_t error)
> +{
> + return error_to_string[error];
> +}
> +
> +samasan_error_t
> +get_current_error (void)
> +{
> + return current_error;
> +}
> +
> +uintptr_t
> +get_fault_address (void)
> +{
> + return fault_address;
> +}
> +
> +void
> +raise_fault_with_address (samasan_error_t error, uintptr_t address)
> +{
> + volatile char *fault;
> +
> + current_error = error;
> + fault_address = address;
> +
> + fault = (char *) fault_area;
> + *fault = 0; /* raise segmentation fault */
> +
> + /* unreachable */
> + __builtin_trap ();
> +}
> +
> +samasan_error_t
> +diagnose_error (void *ptr, struct memory_pool_entry_info *entry)
> +{
> + if (!is_pointer_in_memory_pool (ptr))
> + return OUT_OF_MEMORY_POOL;
> + if (!entry)
> + {
> + if (is_pointer_in_partition (ptr))
> + return INVALID_ACCESS;
> + return UNKNOWN_ERROR;
> + }
> + if (entry->is_free)
> + {
> + if (entry->deallocation_trace.tid == INVALID_TID)
> + /* The entry has never been allocated. */
> + return INVALID_ACCESS;
> + return USE_AFTER_FREE;
> + }
> + if (is_out_of_chunk_access ((uintptr_t) ptr, entry))
> + return INVALID_ACCESS;
> + return UNKNOWN_ERROR;
> +}
> +
> +bool
> +samasan_error_init (void)
> +{
> + fault_area = mmap (NULL, get_system_page_size (), PROT_NONE,
> + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
> + samasan_assert (fault_area != MAP_FAILED,
> + "fault area allocation is failed\n");
> + return true;
> +}
> +
> +void
> +samasan_error_deinit (void)
> +{
> + if (fault_area)
> + {
> + samasan_assert (munmap (fault_area, get_system_page_size ()) == 0,
> + "failed to unmap fault area");
> + fault_area = NULL;
> + }
> +}
> diff --git a/sampling-asan/samasan_error.h b/sampling-asan/samasan_error.h
> new file mode 100644
> index 0000000000..270b244f10
> --- /dev/null
> +++ b/sampling-asan/samasan_error.h
> @@ -0,0 +1,48 @@
> +/* Prototypes for the error functions in sampling-asan.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_ERROR_H
> +#define _SAMASAN_ERROR_H
> +
> +#include <stdint.h> /* for uintptr_t type */
> +#include <stdbool.h> /* for bool type */
> +
> +#include "samasan_allocate.h"
> +
> +typedef enum samasan_error {
> + NO_ERROR = -1, /* no error occurred */
> + INVALID_FREE = 0, /* try to free an address at out of the allocated chunk */
> + DOUBLE_FREE, /* try to free already freed one */
> + USE_AFTER_FREE, /* try to access the freed memory chunk */
> + INVALID_ACCESS, /* try to access the area out of the allocated chunk */
> + INVALID_WRITE, /* write on invalid range, detected by check-on-free */
> + OUT_OF_MEMORY_POOL, /* a given address is out of the memory pool */
> + UNKNOWN_ERROR, /* an unindentified error */
> +} samasan_error_t;
> +
> +extern const char *get_error_name (samasan_error_t error);
> +extern samasan_error_t get_current_error (void);
> +extern uintptr_t get_fault_address (void);
> +extern samasan_error_t diagnose_error (void *ptr,
> + struct memory_pool_entry_info *entry);
> +extern void raise_fault_with_address (samasan_error_t error,
> + uintptr_t address);
> +extern bool samasan_error_init (void);
> +extern void samasan_error_deinit (void);
> +
> +#endif /* samasan_error.h */
> diff --git a/sampling-asan/samasan_fault_handler.c b/sampling-asan/samasan_fault_handler.c
> new file mode 100644
> index 0000000000..1e88e9b07f
> --- /dev/null
> +++ b/sampling-asan/samasan_fault_handler.c
> @@ -0,0 +1,125 @@
> +/* Definitions for the sampling-asan fault handler.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <signal.h>
> +
> +#include "samasan_allocate.h"
> +#include "samasan_error.h"
> +#include "samasan_common.h"
> +#include "samasan_report.h"
> +#include "samasan_backtrace.h"
> +
> +static struct sigaction default_handler;
> +static bool handler_installed;
> +
> +static void
> +segfault_handler (int sig, siginfo_t *info, void *context)
> +{
> + struct memory_pool_entry_info *entry;
> + struct memory_pool_entry_info *entry_at_next = NULL;
> + samasan_error_t error;
> + uintptr_t fault_address;
> + struct memory_pool_trace fault_stack_trace;
> + void *fault_ptr;
> +
> + check_in_samasan ();
> +
> + fault_address = get_fault_address ();
> + fault_ptr = (void *) fault_address;
> + if (fault_address == 0) {
> + fault_ptr = info->si_addr;
> + fault_address = (uintptr_t) fault_ptr;
> + }
> + entry = get_memory_pool_entry_from_pointer (fault_ptr);
> + error = get_current_error ();
> + if (error == NO_ERROR)
> + error = diagnose_error (fault_ptr, entry);
> + if (error == INVALID_ACCESS)
> + {
> + entry = get_previous_memory_pool_entry_from_pointer (fault_ptr);
> + entry_at_next = get_next_memory_pool_entry_from_pointer (fault_ptr);
> + }
> + /* get a stack trace of the faulted instruction */
> + get_backtrace (&fault_stack_trace);
> + samasan_report_write (error, fault_address, entry, entry_at_next,
> + &fault_stack_trace);
> +
> + /* Crash forwarding */
> + if (default_handler.sa_handler == SIG_DFL)
> + {
> + signal (SIGSEGV, SIG_DFL);
> + raise (SIGSEGV);
> + }
> + else if (default_handler.sa_handler == SIG_IGN)
> + {
> + /* This error is not reported by sampling-asan */
> + if (error == OUT_OF_MEMORY_POOL || error == UNKNOWN_ERROR)
> + {
> + /* Sampling-asan is intended to be used in production system.
> + So, we don't need to abort the execution of the program.
> + XXX: is this meaningful? or can be removed? */
> + signal (SIGSEGV, SIG_IGN);
> + raise (SIGSEGV);
> + }
> + }
> + else
> + {
> + if (default_handler.sa_flags & SA_SIGINFO)
> + default_handler.sa_sigaction (sig, info, context);
> + else
> + default_handler.sa_handler (sig);
> + }
> +
> + check_out_samasan ();
> +}
This does a lot of computation after SIGSEGV, including potentially allocation
memory (get_backtrace). I think it should ok since it is a debug tool and
libasan does something similar.
> +
> +static void
> +install_signal_handler (void)
> +{
> + struct sigaction action;
> +
> + if (SAMASAN_UNLIKELY (handler_installed))
> + return;
> +
> + action.sa_sigaction = segfault_handler;
> + action.sa_flags = SA_SIGINFO;
> + sigaction (SIGSEGV, &action, &default_handler);
> + handler_installed = true;
> +}
> +
> +static void
> +uninstall_signal_handler (void)
> +{
> + if (SAMASAN_UNLIKELY (!handler_installed))
> + return;
> + sigaction (SIGSEGV, &default_handler, NULL);
> + handler_installed = false;
> +}
> +
> +bool
> +samasan_fault_handler_init (void)
> +{
> + install_signal_handler ();
> + return true;
> +}
> +
> +void
> +samasan_fault_handler_deinit (void)
> +{
> + uninstall_signal_handler ();
> +}
> diff --git a/sampling-asan/samasan_fault_handler.h b/sampling-asan/samasan_fault_handler.h
> new file mode 100644
> index 0000000000..a3c6f2966d
> --- /dev/null
> +++ b/sampling-asan/samasan_fault_handler.h
> @@ -0,0 +1,24 @@
> +/* Prototypes and definition for a sampling-asan fault handler.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_FAULT_HANDLER_H
> +#define _SAMASAN_FAULT_HANDLER_H
> +
> +extern bool samasan_fault_handler_init (void);
> +
> +#endif /* samasan_fault_handler.h */
> diff --git a/sampling-asan/samasan_init.c b/sampling-asan/samasan_init.c
> new file mode 100644
> index 0000000000..8cf1df849b
> --- /dev/null
> +++ b/sampling-asan/samasan_init.c
> @@ -0,0 +1,353 @@
> +/* Definitions for sampling-asan initialization.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +
> +#include "samasan_common.h"
> +#include "samasan_allocate.h"
> +#include "samasan_sampling.h"
> +#include "samasan_report.h"
> +#include "samasan_error.h"
> +#include "samasan_fault_handler.h"
> +
> +typedef enum samasan_option {
> + SAMASAN_ENABLE,
> + SAMASAN_MAX_ALLOC_SIZE,
> + SAMASAN_MAX_ON_GOING_ALLOCATIONS,
> + SAMASAN_SAMPLING_RATE,
> + SAMASAN_OUTPUT_PATH,
> + SAMASAN_PARTITION_SIZE,
> + SAMASAN_PAUSE_ON_FORK,
> + SAMASAN_CHUNK_PICK,
> + SAMASAN_OPTIONS,
> +} samasan_option_t;
> +
> +static const char *options_string[SAMASAN_OPTIONS + 1] = {
> + "SAMASAN_ENABLE",
> + "SAMASAN_MAX_ALLOC_SIZE",
> + "SAMASAN_MAX_ON_GOING_ALLOCATIONS",
> + "SAMASAN_SAMPLING_RATE",
> + "SAMASAN_OUTPUT_PATH",
> + "SAMASAN_PARTITION_SIZE",
> + "SAMASAN_PAUSE_ON_FORK",
> + "SAMASAN_CHUNK_PICK",
> + "SAMASAN_OPTIONS",
> +};
> +
> +union samasan_variable {
> + uint32_t i_value;
> + bool b_value;
> + const char *c_value;
> +};
> +
> +struct samasan_configurable {
> + union samasan_variable value;
> + /* if the value should be located in [min, max), the configurable
> + can define a range. */
> + struct {
> + uint32_t max; /* a value should be smaller than max */
> + uint32_t min; /* a value should be larger (or equal) than (to) min*/
> + } range;
> +};
> +
> +static struct samasan_configurable *samasan_configurables;
> +bool samasan_enabled; /* sampling-asan is on-going or not */
> +
> +bool
> +samasan_is_enabled (void)
> +{
> + return samasan_enabled;
> +}
> +
> +static inline void
> +samasan_set_variable_uint (samasan_option_t option, uint32_t value)
> +{
> + samasan_configurables[option].value.i_value = value;
> +}
> +
> +static inline void
> +samasan_set_variable_bool (samasan_option_t option, bool value)
> +{
> + samasan_configurables[option].value.b_value = value;
> +}
> +
> +static inline void
> +samasan_set_variable_char (samasan_option_t option, const char *value)
> +{
> + samasan_configurables[option].value.c_value = value;
> +}
> +
> +static inline void
> +samasan_set_variable_range (samasan_option_t option, uint32_t max,
> + uint32_t min)
> +{
> + samasan_configurables[option].range.max = max;
> + samasan_configurables[option].range.min = min;
> +}
> +
> +#define samasan_set_variable(index, value) \
> + _Generic((value), \
> + uint32_t: samasan_set_variable_uint, \
> + const char *: samasan_set_variable_char, \
> + char *: samasan_set_variable_char, \
> + bool: samasan_set_variable_bool) (index, value)
> +
> +#define samasan_get_variable(index) samasan_configurables[index].value
> +
> +#define samasan_check_variable_range(index, value) \
> + (value >= samasan_configurables[index].range.min && \
> + value <= samasan_configurables[index].range.max)
> +
> +#define samasan_get_enabled() samasan_get_variable (SAMASAN_ENABLE).b_value
> +#define samasan_get_output_path() \
> + samasan_get_variable (SAMASAN_OUTPUT_PATH).c_value
> +#define samasan_get_sampling_rate() \
> + samasan_get_variable (SAMASAN_SAMPLING_RATE).i_value
> +#define samasan_get_max_on_going_allocations() \
> + samasan_get_variable (SAMASAN_MAX_ON_GOING_ALLOCATIONS).i_value
> +#define samasan_get_max_alloc_size() \
> + samasan_get_variable (SAMASAN_MAX_ALLOC_SIZE).i_value
> +#define samasan_get_partition_size() \
> + samasan_get_variable (SAMASAN_PARTITION_SIZE).i_value
> +#define samasan_get_pause_on_fork() \
> + samasan_get_variable (SAMASAN_PAUSE_ON_FORK).b_value
> +#define samasan_get_chunk_pick_style() \
> + samasan_get_variable (SAMASAN_CHUNK_PICK).i_value
> +
> +static bool
> +import_samasan_variable (int type, const char *val)
> +{
> + switch (type)
> + {
> + case SAMASAN_ENABLE:
> + {
> + bool var = false;
> + if (!strcmp (val, "on") || !strcmp (val, "enable")
> + || !strcmp (val, "yes") || !strcmp (val, "true"))
Use '== 0' instead here.
> + var = true;
> + samasan_set_variable (SAMASAN_ENABLE, var);
> + break;
> + }
> + case SAMASAN_MAX_ALLOC_SIZE:
> + {
> + uint32_t converted = (uint32_t) atoi (val);
> + /* on error */
> + if (!converted)
> + return false;
> + if (!samasan_check_variable_range (SAMASAN_MAX_ALLOC_SIZE, converted))
> + return false;
> + samasan_set_variable (SAMASAN_MAX_ALLOC_SIZE, converted);
> + break;
> + }
> + case SAMASAN_MAX_ON_GOING_ALLOCATIONS:
> + {
> + uint32_t converted = (uint32_t) atoi (val);
> + /* on error */
> + if (!converted)
> + return false;
> + if (!samasan_check_variable_range (SAMASAN_MAX_ON_GOING_ALLOCATIONS,
> + converted))
> + return false;
> + samasan_set_variable (SAMASAN_MAX_ON_GOING_ALLOCATIONS, converted);
> + break;
> + }
> + case SAMASAN_SAMPLING_RATE:
> + {
> + uint32_t converted = (uint32_t) (atof (val) * 100000.0);
> + if (!samasan_check_variable_range (SAMASAN_SAMPLING_RATE, converted))
> + return false;
> + samasan_set_variable (SAMASAN_SAMPLING_RATE, converted);
> + break;
> + }
> + case SAMASAN_OUTPUT_PATH:
> + {
> + if (!val)
> + return false;
> + samasan_set_variable (SAMASAN_OUTPUT_PATH, val);
> + break;
> + }
> + case SAMASAN_PARTITION_SIZE:
> + {
> + uint32_t converted = (uint32_t) atoi (val);
> + if (!samasan_check_variable_range (SAMASAN_PARTITION_SIZE,
> + converted))
> + return false;
> + samasan_set_variable (SAMASAN_PARTITION_SIZE, converted);
> + break;
> + }
> + case SAMASAN_PAUSE_ON_FORK:
> + {
> + bool var = false;
> + if (!strcmp (val, "on") || !strcmp (val, "enable")
> + || !strcmp (val, "yes") || !strcmp (val, "true"))
> + var = true;
> + samasan_set_variable (SAMASAN_PAUSE_ON_FORK, var);
> + break;
> + }
> + case SAMASAN_CHUNK_PICK:
> + {
> + uint32_t pick;
> + if (!strcmp (val, "left") || !strcmp (val, "LEFT"))
> + pick = (uint32_t) PICK_FROM_LEFT;
> + else if (!strcmp (val, "right") || !strcmp (val, "RIGHT"))
> + pick = (uint32_t) PICK_FROM_RIGHT;
> + else if (!strcmp (val, "center") || !strcmp (val, "CENTER"))
> + pick = (uint32_t) PICK_CENTER;
> + else
> + return false;
> + samasan_set_variable (SAMASAN_CHUNK_PICK, pick);
> + break;
> + }
> + default:
> + break;
> + }
> + return true;
> +}
> +
> +static inline bool import_samasan_variable_range
> +(samasan_option_t option, const char *max, const char *min)
> +{
> + uint32_t minval, maxval;
> +
> + minval = (uint32_t) atoi (min);
> + maxval = (uint32_t) atoi (max);
> + if (maxval < minval)
> + return false;
> +
> + samasan_set_variable_range (option, maxval, minval);
> + return true;
> +}
> +
> +#include "samasan_variable_init.def"
> +
> +static void
> +samasan_variable_init (void)
> +{
> + if (SAMASAN_UNLIKELY (!samasan_configurables))
> + return;
> +
> + /* variable range initialization */
> + import_samasan_variable_range (SAMASAN_SAMPLING_RATE,
> + MAX_SAMASAN_SAMPLING_RATE,
> + MIN_SAMASAN_SAMPLING_RATE);
> + import_samasan_variable_range (SAMASAN_MAX_ALLOC_SIZE,
> + MAX_SAMASAN_MAX_ALLOC_SIZE,
> + MIN_SAMASAN_MAX_ALLOC_SIZE);
> + import_samasan_variable_range (SAMASAN_MAX_ON_GOING_ALLOCATIONS,
> + MAX_SAMASAN_MAX_ON_GOING_ALLOCATIONS,
> + MIN_SAMASAN_MAX_ON_GOING_ALLOCATIONS);
> + import_samasan_variable_range (SAMASAN_PARTITION_SIZE,
> + MAX_SAMASAN_PARTITION_SIZE,
> + MIN_SAMASAN_PARTITION_SIZE);
> +
> + /* configurable variables initialization */
> + import_samasan_variable (SAMASAN_ENABLE,
> + DEFAULT_SAMASAN_ENABLED);
> + import_samasan_variable (SAMASAN_SAMPLING_RATE,
> + DEFAULT_SAMASAN_SAMPLING_RATE);
> + import_samasan_variable (SAMASAN_MAX_ALLOC_SIZE,
> + DEFAULT_SAMASAN_MAX_ALLOC_SIZE);
> + import_samasan_variable (SAMASAN_MAX_ON_GOING_ALLOCATIONS,
> + DEFAULT_SAMASAN_MAX_ON_GOING_ALLOCATIONS);
> + import_samasan_variable (SAMASAN_OUTPUT_PATH,
> + DEFAULT_SAMASAN_OUTPUT_PATH);
> + import_samasan_variable (SAMASAN_PARTITION_SIZE,
> + DEFAULT_SAMASAN_PARTITION_SIZE);
> + import_samasan_variable (SAMASAN_PAUSE_ON_FORK,
> + DEFAULT_SAMASAN_PAUSE_ON_FORK);
> + import_samasan_variable (SAMASAN_CHUNK_PICK,
> + DEFAULT_SAMASAN_CHUNK_PICK);
> +}
> +
> +void
> +samasan_disable (void)
> +{
> + if (samasan_enabled)
> + samasan_enabled = false;
> +}
> +
> +void
> +samasan_init (void)
> +{
> + struct samasan_configurable configurables[SAMASAN_OPTIONS];
> +
> + if (SAMASAN_UNLIKELY (samasan_enabled))
> + return;
> +
> + memset (configurables, 0, sizeof (configurables));
> + samasan_configurables = configurables;
> +
> + samasan_variable_init ();
> +
> + for (int i = 0; i < SAMASAN_OPTIONS; i++)
> + {
> + const char *val = getenv (options_string[i]);
> + if (val)
> + {
> + if (!import_samasan_variable (i, val))
> + /* some variable includes a value out-of-range */
> + goto err_out1;
> + }
> + }
> + if (!samasan_memory_pool_init (samasan_get_max_on_going_allocations (),
> + samasan_get_max_alloc_size (),
> + samasan_get_partition_size (),
> + samasan_get_chunk_pick_style ()))
> + goto err_out1;
> + if (!samasan_report_init (samasan_get_output_path ()))
> + goto err_out2;
> + if (!samasan_sampling_init (samasan_get_sampling_rate ()))
> + goto err_out3;
> + if (!samasan_error_init ())
> + goto err_out3;
> + if (!samasan_fault_handler_init ())
> + goto err_out4;
> + if (samasan_get_pause_on_fork ())
> + samasan_install_fork_handler ();
> + if (samasan_get_enabled ())
> + samasan_enabled = true;
> + samasan_configurables = NULL;
> + return;
> +
> +err_out4:
> + samasan_error_deinit ();
> +err_out3:
> + samasan_report_deinit ();
> +err_out2:
> + samasan_memory_pool_deinit ();
> +err_out1:
> + samasan_configurables = NULL;
> +}
> +
> +void
> +samasan_deinit (void)
> +{
> + if (SAMASAN_LIKELY(samasan_enabled))
> + {
> + samasan_error_deinit ();
> + samasan_memory_pool_deinit ();
> + samasan_report_deinit ();
> + samasan_uninstall_fork_handler();
> + }
> + samasan_enabled = false;
> +}
> diff --git a/sampling-asan/samasan_init.h b/sampling-asan/samasan_init.h
> new file mode 100644
> index 0000000000..607131cf25
> --- /dev/null
> +++ b/sampling-asan/samasan_init.h
> @@ -0,0 +1,34 @@
> +/* Prototypes and definition for sampling-asan initialization.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_INIT_H
> +#define _SAMASAN_INIT_H
> +
> +#include <stdbool.h>
> +
> +extern bool samasan_enabled;
> +
> +static __inline __attribute__ ((always_inline)) bool
> +is_samasan_enabled (void)
> +{
> + return samasan_enabled;
> +}
> +
> +extern void samasan_deinit (void);
> +
> +#endif /* samasan_init.h */
> diff --git a/sampling-asan/samasan_report.c b/sampling-asan/samasan_report.c
> new file mode 100644
> index 0000000000..b1bd28f639
> --- /dev/null
> +++ b/sampling-asan/samasan_report.c
> @@ -0,0 +1,244 @@
> +/* Definitions for sampling-asan reporting.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <stdbool.h>
> +#include <stdarg.h>
> +#include <string.h>
> +#include <inttypes.h>
> +#include <fcntl.h>
> +
> +#include "samasan_common.h"
> +#include "samasan_error.h"
> +#include "samasan_allocate.h"
> +#include "samasan_backtrace.h"
> +
> +static const char *report_path;
> +static const char *report_head = "<Sampling-ASAN report begin>";
> +static const char *report_foot = "<Sampling-ASAN report end>";
> +
> +#define NAMELEN 255
> +
> +static void
> +get_command_line (char *str)
> +{
> + const char *path = "/proc/self/cmdline";
> + int comm_fd = open (path, O_RDONLY);
> + size_t pos = 0;
> +
> + if (comm_fd < 0)
> + goto comm_error;
> +
> + while (1)
> + {
> + size_t ret = read (comm_fd, &str[pos], NAMELEN - pos);
> + if (ret == 0)
> + break;
> + if (ret < 0)
> + {
> + close (comm_fd);
> + memset (str, 0, pos);
> + goto comm_error;
> + }
> + pos += ret;
> + if (pos == NAMELEN)
> + {
> + pos--;
> + break;
> + }
> + }
> + close (comm_fd);
> + str[pos] = '\0';
> +
> + return;
> +
> +comm_error:
> + strcpy (str, "unknown");
> + return;
> +}
> +
> +static char *
> +strip_command_line (char *str)
> +{
> + char *pos;
> +
> + if (!str)
> + return NULL;
> + pos = strrchr (str, '/');
> + if (pos)
> + return pos + 1;
> + return str;
> +}
> +
> +static struct samasan_mutex report_lock;
> +
> +static void __report_printf (int fd, const char *format, ...)
> +{
> +#define BUF_LEN 255
> + char buffer[BUF_LEN];
> + memset (buffer, 0, BUF_LEN);
> + va_list ap;
> +
> + samasan_mutex_lock (&report_lock);
> + va_start (ap, format);
> + vsnprintf (buffer, BUF_LEN, format, ap);
> + va_end (ap);
> + samasan_mutex_unlock (&report_lock);
> +
> + write (fd, buffer, sizeof (buffer));
> +#undef BUF_LEN
> +}
> +
> +static void (*report_printf) (int, const char *, ...) =
> + __report_printf;
> +
> +void
> +samasan_report_write (samasan_error_t error, uintptr_t address,
> + struct memory_pool_entry_info *entry,
> + struct memory_pool_entry_info *entry_at_next,
> + struct memory_pool_trace *fault_stack_trace)
> +{
> + int stream = STDERR_FILENO;
> + char program_invocation_name[NAMELEN], *process_name;
> + const char *error_name = get_error_name (error);
> +
> + if (strcmp (report_path, "stderr"))
> + stream = open (report_path, O_CREAT | O_WRONLY, 0644);
> +
> + if (SAMASAN_UNLIKELY (!stream))
> + return;
> + memset (program_invocation_name, 0, sizeof (program_invocation_name));
> + get_command_line (program_invocation_name);
> + process_name = strip_command_line (program_invocation_name);
> + report_printf (stream, "%s\n", report_head);
> + report_printf (stream, "A crash is occurred in %s\n", process_name);
> + report_printf (stream, "A %s fault raised on 0x%" PRIxPTR "\n", error_name, address);
> + report_printf (stream, "<Stack trace of the faulted instruction>\n");
> + print_backtrace (fault_stack_trace, stream, report_printf);
> +
> + if (entry == NULL)
> + {
> + report_printf (stream, "=============================================\n");
> + report_printf (stream, "Cannot find allocation metadata!\n");
> + report_printf (stream, "If an OUT_OF_MEMORY_POOL fault is raised, ");
> + report_printf (stream, "it can be a problem caused by other memory bugs.\n");
> + report_printf (stream, "You can recheck the problem with ");
> + report_printf (stream, "the increased sampling rate by setting ");
> + report_printf (stream, "\"SAMASAN_SAMPLING_RATE=\".\n");
> + report_printf (stream, "============================================\n");
> + goto report_end;
> + }
> +
> + if (error != INVALID_ACCESS && !entry->is_free)
> + {
> + report_printf (stream, "=============================================\n");
> + report_printf (stream, "An allocated chunk is located at 0x%" PRIxPTR " ",
> + entry->address);
> + report_printf (stream, "and occupies %" PRIu32 " bytes.\n", entry->chunk_size);
> +
> + if (error == INVALID_FREE)
> + {
> + const bool address_after = address > entry->address;
> + size_t diff = address_after ? address - entry->address
> + : entry->address - address;
> + const char *direction = address_after ? "after" : "before";
> +
> + report_printf (stream, "You tried to free on 0x%" PRIxPTR " ", address);
> + report_printf (stream, "and it was %zu bytes %s the allocated chunk.\n",
> + diff, direction);
> + }
> + report_printf (stream, "=============================================\n");
> + }
> +
> + if (error == INVALID_ACCESS)
> + {
> + if (!entry->is_free)
> + {
> + report_printf (stream, "=============================================\n");
> + report_printf (stream, "The previous allocated chunk is located at 0x%" PRIxPTR " ",
> + entry->address);
> + report_printf (stream, "and occupies %" PRIu32 " bytes.\n", entry->chunk_size);
> + report_printf (stream, "=============================================\n");
> + }
> + report_printf (stream, "<Allocation trace of the previous chunk (tid: %" PRIu64 ")\n",
> + entry->allocation_trace.tid);
> + print_backtrace (&entry->allocation_trace, stream, report_printf);
> + if (entry->is_free)
> + {
> + report_printf (stream, "<Deallocation trace of the previous chunk (tid: %" PRIu64 ")>\n",
> + entry->deallocation_trace.tid);
> + print_backtrace (&entry->deallocation_trace, stream, report_printf);
> + }
> + if (entry_at_next == NULL)
> + goto report_end;
> +
> + if (!entry_at_next->is_free)
> + {
> + report_printf (stream, "=============================================\n");
> + report_printf (stream, "The next allocated chunk is located at 0x%" PRIxPTR " ",
> + entry_at_next->address);
> + report_printf (stream, "and occupies %" PRIu32 " bytes.\n",
> + entry_at_next->chunk_size);
> + report_printf (stream, "=============================================\n");
> + }
> + report_printf (stream, "<Allocation trace of the next chunk (tid: %" PRIu64 ")\n",
> + entry_at_next->allocation_trace.tid);
> + print_backtrace (&entry_at_next->allocation_trace, stream, report_printf);
> + if (entry_at_next->is_free)
> + {
> + report_printf (stream, "<Deallocation trace of the next chunk (tid: %" PRIu64 ")>\n",
> + entry_at_next->deallocation_trace.tid);
> + print_backtrace (&entry_at_next->deallocation_trace, stream, report_printf);
> + }
> + }
> + else
> + {
> + report_printf (stream, "<Allocation trace (tid: %" PRIu64 ")>\n",
> + entry->allocation_trace.tid);
> + print_backtrace (&entry->allocation_trace, stream, report_printf);
> + if (entry->is_free)
> + {
> + report_printf (stream, "<Deallocation trace (tid: %" PRIu64 ")>\n",
> + entry->deallocation_trace.tid);
> + print_backtrace (&entry->deallocation_trace, stream, report_printf);
> + }
> + }
> +
> +report_end:
> + report_printf (stream, "%s\n", report_foot);
> +
> + if (stream != STDERR_FILENO)
> + close (stream);
> +}
> +
> +bool
> +samasan_report_init (const char *output_path)
> +{
> + samasan_mutex_init (&report_lock);
> + if (!strcmp (output_path, "stderr"))
> + report_path = "stderr";
> + else
> + report_path = output_path;
> + return true;
> +}
> +
> +void
> +samasan_report_deinit (void)
> +{
> + report_path = NULL;
> +}
> diff --git a/sampling-asan/samasan_report.h b/sampling-asan/samasan_report.h
> new file mode 100644
> index 0000000000..c92a07a842
> --- /dev/null
> +++ b/sampling-asan/samasan_report.h
> @@ -0,0 +1,35 @@
> +/* Prototypes for sampling-asan reporting.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_REPORT_H
> +#define _SAMASAN_REPORT_H
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +
> +#include "samasan_error.h"
> +#include "samasan_allocate.h"
> +
> +extern void samasan_report_write (samasan_error_t error, uintptr_t address,
> + struct memory_pool_entry_info *entry,
> + struct memory_pool_entry_info *entry_at_next,
> + struct memory_pool_trace *fault_stack_trace);
> +extern bool samasan_report_init (const char *output_path);
> +extern void samasan_report_deinit (void);
> +
> +#endif /* samasan_report.h */
> diff --git a/sampling-asan/samasan_sampling.c b/sampling-asan/samasan_sampling.c
> new file mode 100644
> index 0000000000..b8e8e6b318
> --- /dev/null
> +++ b/sampling-asan/samasan_sampling.c
> @@ -0,0 +1,67 @@
> +/* Definitions for the sampling methods in sampling-asan.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +
> +#include "samasan_common.h"
> +
> +#define DEFAULT_SAMPLING_INITIAL_VALUE 0x19861216
> +#define DEFAULT_SAMPLING_MASK 100 * 1000
> +static uint32_t sampling_rate;
> +static uint32_t sampling_mask = DEFAULT_SAMPLING_MASK;
> +
> +static uint32_t *
> +get_sampling_variable (void)
> +{
> + static SAMASAN_TLS_SPECIFIER uint32_t sampling_variable =
> + DEFAULT_SAMPLING_INITIAL_VALUE;
> + return &sampling_variable;
> +}
> +
> +static inline uint32_t
> +get_random_value (uint32_t value)
> +{
> + value ^= value << 7;
> + value ^= value << 3;
> + value ^= value << 17;
> + value ^= value << 8;
> + return value;
> +}
> +
> +bool
> +decide_allocation_sampling (void)
> +{
> + bool ret = false;
> + uint32_t value = get_random_value (*get_sampling_variable ());
> + uint32_t sample = value % sampling_mask;
> +
> + if (SAMASAN_UNLIKELY (sample <= sampling_rate))
> + ret = true;
> + *get_sampling_variable () = value;
> + return ret;
> +}
> +
> +bool
> +samasan_sampling_init (uint32_t given_sampling_rate)
> +{
> + if (given_sampling_rate > DEFAULT_SAMPLING_MASK)
> + return false;
> + sampling_rate = given_sampling_rate;
> + return true;
> +}
> diff --git a/sampling-asan/samasan_sampling.h b/sampling-asan/samasan_sampling.h
> new file mode 100644
> index 0000000000..130da36dfc
> --- /dev/null
> +++ b/sampling-asan/samasan_sampling.h
> @@ -0,0 +1,28 @@
> +/* Prototypes for the sampling-asan sampling methods.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SAMASAN_SAMPLING_H
> +#define _SAMASAN_SAMPLING_H
> +
> +#include <stdint.h>
> +#include <stdbool.h> /* for bool type */
> +
> +extern bool decide_allocation_sampling (void);
> +extern bool samasan_sampling_init (uint32_t given_sampling_rate);
> +
> +#endif /* samasan_sampling.h */
> diff --git a/sampling-asan/samasan_variable_init.def b/sampling-asan/samasan_variable_init.def
> new file mode 100644
> index 0000000000..3a5441bc15
> --- /dev/null
> +++ b/sampling-asan/samasan_variable_init.def
> @@ -0,0 +1,37 @@
> +/* Default values of configurables in sampling-asan.
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#define DEFAULT_SAMASAN_ENABLED "false"
> +#define DEFAULT_SAMASAN_SAMPLING_RATE "0.005" /* 0.005 */
> +#define DEFAULT_SAMASAN_MAX_ALLOC_SIZE "4096"
> +#define DEFAULT_SAMASAN_MAX_ON_GOING_ALLOCATIONS "100"
> +#define DEFAULT_SAMASAN_OUTPUT_PATH "stderr"
> +#define DEFAULT_SAMASAN_PARTITION_SIZE "4096" /* 1 page */
> +#define DEFAULT_SAMASAN_PAUSE_ON_FORK "true"
> +#define DEFAULT_SAMASAN_CHUNK_PICK "CENTER"
> +
> +/* MAX and MIN define a range of the given configuration. */
> +#define MAX_SAMASAN_SAMPLING_RATE "100000"
> +#define MAX_SAMASAN_MAX_ALLOC_SIZE "40960"
> +#define MAX_SAMASAN_MAX_ON_GOING_ALLOCATIONS "10000"
> +#define MAX_SAMASAN_PARTITION_SIZE "40960" /* 10 pages */
> +
> +#define MIN_SAMASAN_SAMPLING_RATE "0"
> +#define MIN_SAMASAN_MAX_ALLOC_SIZE "4096"
> +#define MIN_SAMASAN_MAX_ON_GOING_ALLOCATIONS "1"
> +#define MIN_SAMASAN_PARTITION_SIZE "0" /* no partition page */
More information about the Libc-alpha
mailing list