[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