From patchwork Fri Sep 19 01:30:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sung-hun Kim X-Patchwork-Id: 120495 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A36623858406 for ; Fri, 19 Sep 2025 01:34:02 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A36623858406 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256 header.s=mail20170921 header.b=CrMDIqM+ X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) by sourceware.org (Postfix) with ESMTPS id BD6173858D20 for ; Fri, 19 Sep 2025 01:31:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BD6173858D20 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=samsung.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BD6173858D20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245499; cv=none; b=aGw30GPfjbJCLI6RnO93fLSJ4HH2jgDeQQ9qgk0JP/YBfyIWVmZ0Ru7FlCPOYbbeWJR8IebV7yXGCTwFLIefSKJpPI/SwcARriwOLjG71ofAe/mGLtsVmmUiazczsFak9m+eJIPxIfeYQUM7hZbRuaGv0JJhblx6AgBMosC4rwI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245499; c=relaxed/simple; bh=PyvFj1Do1x+DZq/dBRWwS2sEDNOnuUWLVs7K/O5C7fk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=NgGjp3AvDc6trFG+fQ+oPpWyQLhQf8b5YjDO91m8KCZTqOYMmtDxwfpp3dTPQ0d7FE8yj+PJXe/AZTv05Imn7cbTASLeQ8YhrFeTVtREezl72eof98NKUewnV2+NzcDEoxRHbtAlZ1Ci7FI9Xax9i7PA6UBxcLTqMFbRodIOWvM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BD6173858D20 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250919013135epoutp0294e93a5248b51625866dc71f7865120f~mivZ2-VHG1436414364epoutp02P for ; Fri, 19 Sep 2025 01:31:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250919013135epoutp0294e93a5248b51625866dc71f7865120f~mivZ2-VHG1436414364epoutp02P DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1758245495; bh=8F8hrHX4HiJ+kMowRlY8dw8xWCo7cJaEM2NgHZM9ijw=; h=From:To:Cc:Subject:Date:References:From; b=CrMDIqM+zzFYJMmwLvkHeaNMaH/lUAr9Ja/f/UGfkyEicc2Nc1bmCayxb3Tx+4nta a0sMTliAqZqi69JLvZ0z0JU/mQgUzi0IxCUkb+0S9BWgrM7emQHvvwen76ySbiZqXX PNljK20AJTJvM1hKMaapXjNvNIoE5Z5UyqpIN4m4= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPS id 20250919013135epcas1p39d3c08522e87e0177518242ef87024d5~mivZa7sVe3233032330epcas1p34; Fri, 19 Sep 2025 01:31:35 +0000 (GMT) Received: from epcas1p2.samsung.com (unknown [182.195.38.106]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cSZjL1Jkkz6B9mP; Fri, 19 Sep 2025 01:31:34 +0000 (GMT) Received: from epsmtip2.samsung.com (unknown [182.195.34.31]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250919013133epcas1p2e4d42f4e91ac96fd7dff4906d875f572~mivX9GHTE1173311733epcas1p2A; Fri, 19 Sep 2025 01:31:33 +0000 (GMT) Received: from localhost.localdomain (unknown [10.113.111.45]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250919013133epsmtip2c9ee4f88f89cd1448e4bfbb85beb3989~mivX3Y55q0446004460epsmtip2g; Fri, 19 Sep 2025 01:31:33 +0000 (GMT) From: Sung-hun Kim To: libc-alpha@sourceware.org Cc: carlos@systemhalted.org, sfoon.kim@samsung.com, sebuns@gmail.com, dongkyun.s@samsung.com, sungguk.na@samsung.com Subject: [PATCH 1/7] sampling-asan: A sampling-based address sanitization for on-line memory bug detection Date: Fri, 19 Sep 2025 10:30:42 +0900 Message-Id: <20250919013048.1525832-1-sfoon.kim@samsung.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-CMS-MailID: 20250919013133epcas1p2e4d42f4e91ac96fd7dff4906d875f572 X-Msg-Generator: CA CMS-TYPE: 101P cpgsPolicy: CPGSC10-361,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250919013133epcas1p2e4d42f4e91ac96fd7dff4906d875f572 References: X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org 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 --- 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 | 339 +++++++++++++ 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 | 40 ++ 21 files changed, 2144 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 + . */ +#ifndef _SAMASAN_H + +/* _SAMASAN_H will be defined in the below header. */ +#include + +#endif /* samasan.h */ 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 +# . + +# 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 + +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; + } +} 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 + . */ + +#ifndef _SAMASAN_H +#define _SAMASAN_H + +#include /* for size_t */ +#include /* 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 + . */ + +/* 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. */ + +#include +#include +#include +#include /* for mmap/munmap */ +#include /* for __dso_handle */ +#include /* 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; + +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; + +#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) +{ + 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. + + |----|-----------------------|----|-- + |----|----|----|----|-- + + 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 + . */ + +#ifndef _SAMASAN_ALLOCATE_H +#define _SAMASAN_ALLOCATE_H + +#include + +/* 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 + . */ + +#include +#include +#include +#include +#include + +#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); +} 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 + . */ + +#ifndef _SAMASAN_BACKTRACE_H +#define _SAMASAN_BACKTRACE_H + +#include + +#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 + . */ + +#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..0705e2b4a3 --- /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 + . */ + +#ifndef _SAMASAN_COMMON_H +#define _SAMASAN_COMMON_H + +#include /* for fprintf */ +#include /* for size_t */ +#include /* for bool type */ +#include /* for sysconf */ +#include /* for mutex */ +#include /* 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) + +#define samasan_atomic_read(var) atomic_forced_read(var) +#define samasan_atomic_write(var, val) atomic_store_relaxed(var, val) +#define samasan_atomic_inc(var) catomic_increment(var) +#define samasan_atomic_dec(var) catomic_decrement(var) + +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 + . */ + +#include +#include /* 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 + . */ + +#ifndef _SAMASAN_ERROR_H +#define _SAMASAN_ERROR_H + +#include /* for uintptr_t type */ +#include /* 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 + . */ + +#include + +#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 (); +} + +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 + . */ + +#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..3a53068357 --- /dev/null +++ b/sampling-asan/samasan_init.c @@ -0,0 +1,339 @@ +/* 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 + . */ + +#include +#include +#include +#include +#include +#include + +#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; +} + +/* TODO: _Generic is supported since C11 standard (ISO/IEC 9899:2011) + Thus, the version of C should be checked and an alternative should be + provided for the unsupported versions. */ +/* A boolean type variable is treated as a integer type variable in C, + so I assign samasan_set_variable_bool to the integer type. */ +#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, \ + int: 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 + +#include "samasan_variable_init.def" + +static void +samasan_variable_init (void) +{ + if (SAMASAN_UNLIKELY (!samasan_configurables)) + return; + + samasan_set_variable (SAMASAN_ENABLE, + DEFAULT_SAMASAN_ENABLED); + samasan_set_variable (SAMASAN_SAMPLING_RATE, + DEFAULT_SAMASAN_SAMPLING_RATE); + samasan_set_variable_range (SAMASAN_SAMPLING_RATE, + MAX_SAMASAN_SAMPLING_RATE, + MIN_SAMASAN_SAMPLING_RATE); + samasan_set_variable (SAMASAN_MAX_ALLOC_SIZE, + DEFAULT_SAMASAN_MAX_ALLOC_SIZE); + samasan_set_variable_range (SAMASAN_MAX_ALLOC_SIZE, + MAX_SAMASAN_MAX_ALLOC_SIZE, + MIN_SAMASAN_MAX_ALLOC_SIZE); + samasan_set_variable (SAMASAN_MAX_ON_GOING_ALLOCATIONS, + DEFAULT_SAMASAN_MAX_ON_GOING_ALLOCATIONS); + samasan_set_variable_range (SAMASAN_MAX_ON_GOING_ALLOCATIONS, + MAX_SAMASAN_MAX_ON_GOING_ALLOCATIONS, + MIN_SAMASAN_MAX_ON_GOING_ALLOCATIONS); + samasan_set_variable (SAMASAN_OUTPUT_PATH, + DEFAULT_SAMASAN_OUTPUT_PATH); + samasan_set_variable (SAMASAN_PARTITION_SIZE, + DEFAULT_SAMASAN_PARTITION_SIZE); + samasan_set_variable_range (SAMASAN_PARTITION_SIZE, + MAX_SAMASAN_PARTITION_SIZE, + MIN_SAMASAN_PARTITION_SIZE); + samasan_set_variable (SAMASAN_PAUSE_ON_FORK, + DEFAULT_SAMASAN_PAUSE_ON_FORK); + samasan_set_variable (SAMASAN_CHUNK_PICK, + DEFAULT_SAMASAN_CHUNK_PICK); +} + +static bool +import_samasan_variable (int type, const char *val) +{ + switch (type) + { + case SAMASAN_ENABLE: + { + if (!strcmp (val, "on") || !strcmp (val, "enable") + || !strcmp (val, "yes") || !strcmp (val, "true")) + samasan_set_variable (SAMASAN_ENABLE, true); + 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: + { + int32_t converted = (int32_t) atoi (val); + if (converted < 0) + return false; + if (!samasan_check_variable_range (SAMASAN_PARTITION_SIZE, + (uint32_t) converted)) + return false; + samasan_set_variable (SAMASAN_PARTITION_SIZE, (uint32_t) converted); + break; + } + case SAMASAN_PAUSE_ON_FORK: + { + if (!strcmp (val, "on") || !strcmp (val, "enable") + || !strcmp (val, "yes") || !strcmp (val, "true")) + samasan_set_variable (SAMASAN_PAUSE_ON_FORK, true); + else + samasan_set_variable (SAMASAN_PAUSE_ON_FORK, false); + break; + } + case SAMASAN_CHUNK_PICK: + { + if (!strcmp (val, "left") || !strcmp (val, "LEFT")) + samasan_set_variable (SAMASAN_CHUNK_PICK, (uint32_t) PICK_FROM_LEFT); + else if (!strcmp (val, "right") || !strcmp (val, "RIGHT")) + samasan_set_variable (SAMASAN_CHUNK_PICK, (uint32_t) PICK_FROM_RIGHT); + else if (!strcmp (val, "center") || !strcmp (val, "CENTER")) + samasan_set_variable (SAMASAN_CHUNK_PICK, (uint32_t) PICK_CENTER); + else + return false; + break; + } + default: + break; + } + return true; +} + +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 + . */ + +#ifndef _SAMASAN_INIT_H +#define _SAMASAN_INIT_H + +#include + +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 + . */ + +#include +#include +#include +#include +#include +#include + +#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 = ""; +static const char *report_foot = ""; + +#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, "\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.tid); + print_backtrace (&entry->allocation_trace, stream, report_printf); + if (entry->is_free) + { + report_printf (stream, "\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.tid); + print_backtrace (&entry_at_next->allocation_trace, stream, report_printf); + if (entry_at_next->is_free) + { + report_printf (stream, "\n", + entry_at_next->deallocation_trace.tid); + print_backtrace (&entry_at_next->deallocation_trace, stream, report_printf); + } + } + else + { + report_printf (stream, "\n", + entry->allocation_trace.tid); + print_backtrace (&entry->allocation_trace, stream, report_printf); + if (entry->is_free) + { + report_printf (stream, "\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 + . */ + +#ifndef _SAMASAN_REPORT_H +#define _SAMASAN_REPORT_H + +#include +#include + +#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 + . */ + +#include +#include + +#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 + . */ + +#ifndef _SAMASAN_SAMPLING_H +#define _SAMASAN_SAMPLING_H + +#include +#include /* 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..954ce20eeb --- /dev/null +++ b/sampling-asan/samasan_variable_init.def @@ -0,0 +1,40 @@ +/* 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 + . */ + +/* All integer values defined in this file should have type of + unsigned integer. */ + +#define DEFAULT_SAMASAN_ENABLED false +#define DEFAULT_SAMASAN_SAMPLING_RATE 500u /* 0.005 */ +#define DEFAULT_SAMASAN_MAX_ALLOC_SIZE 4096u +#define DEFAULT_SAMASAN_MAX_ON_GOING_ALLOCATIONS 100u +#define DEFAULT_SAMASAN_OUTPUT_PATH "stderr" +#define DEFAULT_SAMASAN_PARTITION_SIZE 4096u /* 1 page */ +#define DEFAULT_SAMASAN_PAUSE_ON_FORK true +#define DEFAULT_SAMASAN_CHUNK_PICK PICK_CENTER /* defined in samasan_allocate.h */ + +/* MAX and MIN define a range of the given configuration. */ +#define MAX_SAMASAN_SAMPLING_RATE 100000u +#define MAX_SAMASAN_MAX_ALLOC_SIZE 40960u +#define MAX_SAMASAN_MAX_ON_GOING_ALLOCATIONS 10000u +#define MAX_SAMASAN_PARTITION_SIZE 40960u /* 10 pages */ + +#define MIN_SAMASAN_SAMPLING_RATE 0u +#define MIN_SAMASAN_MAX_ALLOC_SIZE 4096u +#define MIN_SAMASAN_MAX_ON_GOING_ALLOCATIONS 1u +#define MIN_SAMASAN_PARTITION_SIZE 0u /* no partition page */ From patchwork Fri Sep 19 01:30:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sung-hun Kim X-Patchwork-Id: 120501 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C99D63858D21 for ; Fri, 19 Sep 2025 01:39:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C99D63858D21 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256 header.s=mail20170921 header.b=PV6OLoi4 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) by sourceware.org (Postfix) with ESMTPS id B56D93858406 for ; Fri, 19 Sep 2025 01:32:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B56D93858406 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=samsung.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B56D93858406 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; cv=none; b=MJB2HneiiQvZWjcmSWuc/yziZyepPYsk0vwEX6T5tAjK966zFQMj8T0lN8anki2S6g43VSW/r9KcqH+zFo1XdWvwFGgHDDCS5c3V1vDsjPCsASaIlQc+6GqwUhBh6J6duA9cTq2+a0PjsxOvzp2NAhBiC+px7oJuyGj8iSmUFL0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; c=relaxed/simple; bh=m9e/4WZfJqpCjY/UB2oLWVUO4kW51hExk5gNzWK0vfI=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=eqTGFEwmTJJ5ideWGTCPA7leBECvLZ19XB0Ti+2SbF4R5a7Kv2cJLFooM6PNTf140nIdNRc+iu2kYhnhyA2NuyOjpk4Rwx7OFiGCV5mRZSQHLUEzhqLMpAVG+MBCUx5ReFGlzmDiGasj7zrAo3gW4tKS4Aua7LHz224PID/V/c4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B56D93858406 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250919013209epoutp01b7744196be398b33a6a37ead971dd1f2~miv5Y3pHA2500025000epoutp01A for ; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250919013209epoutp01b7744196be398b33a6a37ead971dd1f2~miv5Y3pHA2500025000epoutp01A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1758245529; bh=YrcVkPE+k1DX/GoC2hqgx0ADmGd2nUSAJczppYyTrYk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PV6OLoi4Q6xnPf/T4mXZvHUODIMNv9CkvcXmwsFf7/9I4LCmuO3opOFsZw3CsK2L9 Pq8Ms/qWkrOCoMCm3lN5b2InoAGwSdcdy9qG/NYCs/JfwE3+HIT8cXxZFe4/p5g7Ul rrmhwifYRRFMAc4V9EE+A6GRHnm8TSoo8uRwsuHc= Received: from epsnrtp03.localdomain (unknown [182.195.42.155]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPS id 20250919013209epcas1p4125bea9513698ae124ede6aaeb8cc194~miv44YC0A0371203712epcas1p4t; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) Received: from epcas1p4.samsung.com (unknown [182.195.38.103]) by epsnrtp03.localdomain (Postfix) with ESMTP id 4cSZk02q0Kz3hhT4; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from epsmtip2.samsung.com (unknown [182.195.34.31]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250919013207epcas1p2152846515bccb2b23fe271e874e123b8~miv30Z7er3257732577epcas1p2J; Fri, 19 Sep 2025 01:32:07 +0000 (GMT) Received: from localhost.localdomain (unknown [10.113.111.45]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250919013207epsmtip21b355d856fde98315def5dc3a6487a8c~miv3v0AXl0450404504epsmtip2g; Fri, 19 Sep 2025 01:32:07 +0000 (GMT) From: Sung-hun Kim To: libc-alpha@sourceware.org Cc: carlos@systemhalted.org, sfoon.kim@samsung.com, sebuns@gmail.com, dongkyun.s@samsung.com, sungguk.na@samsung.com Subject: [PATCH 2/7] sampling-asan: Add test cases for sampling-asan Date: Fri, 19 Sep 2025 10:30:43 +0900 Message-Id: <20250919013048.1525832-2-sfoon.kim@samsung.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250919013048.1525832-1-sfoon.kim@samsung.com> MIME-Version: 1.0 X-CMS-MailID: 20250919013207epcas1p2152846515bccb2b23fe271e874e123b8 X-Msg-Generator: CA CMS-TYPE: 101P cpgsPolicy: CPGSC10-361,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250919013207epcas1p2152846515bccb2b23fe271e874e123b8 References: <20250919013048.1525832-1-sfoon.kim@samsung.com> X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Test cases are categorized into two groups: 1. Testing basic functionalities of sampling-asan 2. Testing creation of proper memory bug reports Memory bug types are defined in samasan_error.h. If you wonder about it, please refer the header file. Signed-off-by: Sung-hun Kim --- sampling-asan/Makefile | 10 +- sampling-asan/tst-allocate.c | 209 ++++++++++++++++++++++++ sampling-asan/tst-block-sizes.c | 68 ++++++++ sampling-asan/tst-chunk-pick.c | 103 ++++++++++++ sampling-asan/tst-common.c | 88 ++++++++++ sampling-asan/tst-double-free.c | 63 +++++++ sampling-asan/tst-init.c | 120 ++++++++++++++ sampling-asan/tst-invalid-access.c | 71 ++++++++ sampling-asan/tst-invalid-access2.c | 66 ++++++++ sampling-asan/tst-invalid-free.c | 65 ++++++++ sampling-asan/tst-invalid-free2.c | 65 ++++++++ sampling-asan/tst-invalid-write.c | 69 ++++++++ sampling-asan/tst-out-of-memory-pool.c | 61 +++++++ sampling-asan/tst-partition-sizes.c | 88 ++++++++++ sampling-asan/tst-pause-on-fork.c | 139 ++++++++++++++++ sampling-asan/tst-sampling.c | 82 ++++++++++ sampling-asan/tst-threaded-allocation.c | 90 ++++++++++ sampling-asan/tst-use-after-free.c | 66 ++++++++ 18 files changed, 1522 insertions(+), 1 deletion(-) create mode 100644 sampling-asan/tst-allocate.c create mode 100644 sampling-asan/tst-block-sizes.c create mode 100644 sampling-asan/tst-chunk-pick.c create mode 100644 sampling-asan/tst-common.c create mode 100644 sampling-asan/tst-double-free.c create mode 100644 sampling-asan/tst-init.c create mode 100644 sampling-asan/tst-invalid-access.c create mode 100644 sampling-asan/tst-invalid-access2.c create mode 100644 sampling-asan/tst-invalid-free.c create mode 100644 sampling-asan/tst-invalid-free2.c create mode 100644 sampling-asan/tst-invalid-write.c create mode 100644 sampling-asan/tst-out-of-memory-pool.c create mode 100644 sampling-asan/tst-partition-sizes.c create mode 100644 sampling-asan/tst-pause-on-fork.c create mode 100644 sampling-asan/tst-sampling.c create mode 100644 sampling-asan/tst-threaded-allocation.c create mode 100644 sampling-asan/tst-use-after-free.c diff --git a/sampling-asan/Makefile b/sampling-asan/Makefile index 70f639dab5..b0d408af30 100644 --- a/sampling-asan/Makefile +++ b/sampling-asan/Makefile @@ -25,5 +25,13 @@ 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 +tests := tst-allocate tst-sampling tst-threaded-allocation tst-init \ + tst-double-free tst-use-after-free tst-invalid-write \ + tst-block-sizes tst-partition-sizes tst-invalid-access \ + tst-invalid-access2 tst-out-of-memory-pool \ + tst-invalid-free tst-invalid-free2 tst-pause-on-fork \ + tst-chunk-pick -include ../Rules +$(objpfx)tst-threaded-allocation: $(shared-thread-library) + +include ../Rules \ No newline at end of file diff --git a/sampling-asan/tst-allocate.c b/sampling-asan/tst-allocate.c new file mode 100644 index 0000000000..b0598a5fc5 --- /dev/null +++ b/sampling-asan/tst-allocate.c @@ -0,0 +1,209 @@ +/* Test and verify allocations 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 + . */ + +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +static void +test_allocations (void) +{ + const size_t zero_size = 0; + const size_t allocation_size = 4096; + void *ptr; + + ptr = samasan_allocate (zero_size); + TEST_VERIFY(ptr == NULL); + ptr = samasan_allocate (allocation_size); + TEST_VERIFY (ptr != NULL); + samasan_free (ptr); +} + +static void +test_too_large_allocation (void) +{ + const size_t allocation_size = 4096 + 4096; + void *ptr; + + ptr = samasan_allocate (allocation_size); + TEST_VERIFY (ptr == NULL); +} + +static void +test_multiple_allocations (void) +{ +#define MAX_ALLOCATIONS 101 + const size_t allocation_size = 512; + void *ptr[MAX_ALLOCATIONS] = { NULL, }; + + for (int i = 0; i < MAX_ALLOCATIONS; i++) + ptr[i] = samasan_allocate (allocation_size); + TEST_VERIFY (ptr[MAX_ALLOCATIONS - 1] == NULL); + for (int i = 0; i < MAX_ALLOCATIONS - 1; i++) + samasan_free (ptr[i]); +#undef MAX_ALLOCATIONS +} + +static void +test_allocation_and_free (void) +{ +#define MAX_ALLOCATIONS 100 + const size_t allocation_size = 512; + void *ptr[MAX_ALLOCATIONS] = { NULL, }; + + for (int i = 0; i < MAX_ALLOCATIONS; i++) + { + ptr[i] = samasan_allocate (allocation_size); + TEST_VERIFY (ptr[i] != NULL); + } + for (int i = 0; i < MAX_ALLOCATIONS; i++) + samasan_free (ptr[i]); + for (int i = 0; i < MAX_ALLOCATIONS; i++) + { + ptr[i] = samasan_allocate (allocation_size); + TEST_VERIFY (ptr[i] != NULL); + } + for (int i = 0; i < MAX_ALLOCATIONS; i++) + samasan_free (ptr[i]); +#undef MAX_ALLOCATIONS +} + +static void +test_allocation_use_free_repeat (void) +{ +#define NR_REPEAT 100 + const size_t allocation_size = 512; + void *ptr; + + for (int i = 0; i < NR_REPEAT; i++) + { + char *cptr; + ptr = samasan_allocate (allocation_size); + TEST_VERIFY (ptr != NULL); + cptr = (char *) ptr; + for (int j = 0; j < allocation_size; j++) + cptr[j] = 'a'; + samasan_free( ptr); + } +#undef NR_REPEAT +} + +static void +test_out_of_bound_pointer (void) +{ +#define OUT_OF_BOUND_OFFSET (4096 * 100) + const size_t allocation_size = 512; + void *ptr, *oob_ptr; + + ptr = samasan_allocate (allocation_size); + oob_ptr = ptr + OUT_OF_BOUND_OFFSET; + TEST_VERIFY (samasan_is_pointer_in_sampling_pool (oob_ptr) == false); + samasan_free (ptr); +#undef OUT_OF_BOUND_OFFSET +} + +static void +test_allocated_size (void) +{ + const size_t allocation_size1 = 512; + const size_t allocation_size2 = 1023; + void *ptr1, *ptr2; + + ptr1 = samasan_allocate (allocation_size1); + ptr2 = samasan_allocate (allocation_size2); + + TEST_VERIFY (samasan_get_size (ptr1) == allocation_size1); + TEST_VERIFY (samasan_get_size (ptr2) == allocation_size2); + + samasan_free (ptr1); + samasan_free (ptr2); +} + +static void +test_wrong_get_size (void) +{ +#define OUT_OF_BOUND_OFFSET (4096 * 100) + const size_t allocation_size = 512; + void *ptr, *oob_ptr; + + ptr = samasan_allocate (allocation_size); + oob_ptr = ptr + OUT_OF_BOUND_OFFSET; + + TEST_VERIFY (samasan_get_size (ptr) == allocation_size); + /* the size of out-of-bound memory chunk should be zero */ + TEST_VERIFY (samasan_get_size (oob_ptr) == 0); + + samasan_free (ptr); + + /* the size of freed chunk should be zero */ + TEST_VERIFY (samasan_get_size (ptr) == 0); +#undef OUT_OF_BOUND_OFFSET +} + +static inline bool +is_aligned (uintptr_t address) +{ + return (address % sizeof (size_t)) == 0; +} + +static void +test_aligned_allocation (void) +{ + const size_t allocation_size = 512; + void *ptr; + + for (size_t i = 0; i < 100; i++) + { + ptr = samasan_allocate (allocation_size); + TEST_VERIFY (is_aligned ((uintptr_t) ptr)); + samasan_free(ptr); + } +} + +static int +do_test (void) +{ + setenv ("SAMASAN_ENABLE", "true", 1/* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1/* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "100", 1/* replace */); + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + /* allocation tests */ + test_allocations(); + test_too_large_allocation (); + test_multiple_allocations (); + test_allocation_and_free (); + test_allocation_use_free_repeat (); + + /* allocation sanity checking tests */ + test_out_of_bound_pointer (); + test_allocated_size (); + test_wrong_get_size (); + test_aligned_allocation (); + + return 0; +} + +#include diff --git a/sampling-asan/tst-block-sizes.c b/sampling-asan/tst-block-sizes.c new file mode 100644 index 0000000000..5bf35ab7bc --- /dev/null +++ b/sampling-asan/tst-block-sizes.c @@ -0,0 +1,68 @@ +/* Test and verify various chunk sizes 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 + . */ + +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +static void +test_multiple_chunk_sizes (size_t size) +{ + void *ptr; + char *cptr; + char chunk_size[10]; + const size_t increase = 64; + size_t allocation_size = increase; + + sprintf(chunk_size, "%ld", size); + setenv ("SAMASAN_MAX_ALLOC_SIZE", chunk_size, 1/* replace */); + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == true); + for (; allocation_size <= size; allocation_size += increase) + { + ptr = samasan_allocate (allocation_size); + TEST_VERIFY (ptr != NULL); + cptr = (char *) ptr; + *cptr = 'A'; + samasan_free (ptr); + } + samasan_deinit (); +} + +static int +do_test (void) +{ + const size_t page_size = 4096; + + setenv ("SAMASAN_ENABLE", "true", 1/* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1/* replace */); + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + + for (size_t i = 1; i < 10; i++) + test_multiple_chunk_sizes (page_size * i); + + return 0; +} + +#include diff --git a/sampling-asan/tst-chunk-pick.c b/sampling-asan/tst-chunk-pick.c new file mode 100644 index 0000000000..94793e69d9 --- /dev/null +++ b/sampling-asan/tst-chunk-pick.c @@ -0,0 +1,103 @@ +/* Test and verify memory chunk pick styles defined 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 + . */ + +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ +#include "samasan_allocate.h" /* for memory_chunk_pick_style_t */ + +static const size_t page_size = 4096; + +static bool +check_allocation_align (memory_chunk_pick_style_t style, size_t size, void *ptr) +{ + uintptr_t address = (uintptr_t) ptr; + uintptr_t page_address = address - (address % page_size); + + if (style == PICK_FROM_LEFT) + { + /* If style == PICK_FROM_LEFT, the address should be same to + * the address of the memory block. */ + if (address == page_address) + return true; + return false; + } + else if (style == PICK_FROM_RIGHT) + { + /* If style == PICK_FROM_RIGHT, the end of the memory chunk should be + * same to the end of the memory block. */ + if (address == (page_address + page_size - size)) + return true; + return false; + } + else /* style == PICK_CENTER */ + { + /* If style == PICK_CENTER, the address should be located at the + center of the memory block. */ + uintptr_t expected = page_address + ((page_size - size) >> 1); + if (address == expected) + return true; + return false; + } + return false; +} + +static void +test_memory_chunk_pick_style (memory_chunk_pick_style_t style) +{ + const size_t alloc_size = 512; + void *ptr; + + if (style == PICK_FROM_LEFT) + setenv ("SAMASAN_CHUNK_PICK", "LEFT", 1); + else if (style == PICK_FROM_RIGHT) + setenv ("SAMASAN_CHUNK_PICK", "RIGHT", 1); + else + setenv ("SAMASAN_CHUNK_PICK", "CENTER", 1); + + samasan_init(); + + ptr = samasan_allocate (alloc_size); + TEST_VERIFY (ptr != NULL); + TEST_VERIFY (check_allocation_align (style, alloc_size, ptr)); + samasan_free (ptr); + + samasan_deinit (); +} + +static int +do_test (void) +{ + setenv ("SAMASAN_ENABLE", "true", 1/* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1/* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1/* replace */); + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + + for (memory_chunk_pick_style_t style = PICK_FROM_LEFT; style < PICK_END; style++) + test_memory_chunk_pick_style (style); + + return 0; +} + +#include diff --git a/sampling-asan/tst-common.c b/sampling-asan/tst-common.c new file mode 100644 index 0000000000..333ab9d8ab --- /dev/null +++ b/sampling-asan/tst-common.c @@ -0,0 +1,88 @@ +/* Definition of common functions for testing. + 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 + . */ + +#include +#include +#include +#include +#include +#include + +static bool +check_report (const char *keyword, const char *report_path) +{ + if (access (report_path, F_OK) == 0) + { + bool check = false; + FILE *report = fopen (report_path, "r"); + if (report != NULL) + { + char buf[255], *substr; + while (fscanf (report, "%s", buf) != EOF) + { + substr = strstr (buf, keyword); + if (substr != NULL && !strncmp (substr, keyword, strlen (keyword))) + { + check = true; + break; + } + memset (buf, 0, sizeof (buf)); + } + fclose (report); + } + return (check == true); + } + return false; +} + +static bool +wait_error (pid_t child_pid) +{ + int status; + waitpid (child_pid, &status, 0); + return WIFSIGNALED (status) && (WTERMSIG (status) == SIGSEGV); +} + +static void +clean_up_report (const char *report_path) +{ + if (access (report_path, F_OK) == 0) + unlink (report_path); +} + +static void +test_bed (void (*error_func) (void), const char *keyword, + const char *report_path) +{ + pid_t child_pid; + + clean_up_report (report_path); + + child_pid = _Fork(); + TEST_VERIFY_EXIT (child_pid != -1); + + if (child_pid == 0) + error_func (); + else + { + TEST_VERIFY_EXIT (wait_error (child_pid) == true); + TEST_VERIFY (check_report (keyword, report_path) == true); + } + + clean_up_report (report_path); +} diff --git a/sampling-asan/tst-double-free.c b/sampling-asan/tst-double-free.c new file mode 100644 index 0000000000..4ef5f5a62c --- /dev/null +++ b/sampling-asan/tst-double-free.c @@ -0,0 +1,63 @@ +/* Test and verify a double-free case. + 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 + . */ + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_double_free (void) +{ + const size_t allocation_size = 512; + void *ptr; + + ptr = samasan_allocate (allocation_size); + samasan_free (ptr); + samasan_free (ptr); /* raise segmentation fault */ +} + +static void test_double_free (const char *report_path) +{ + test_bed (do_double_free, "DOUBLE_FREE", report_path); +} + +static int do_test (void) +{ + const char *report_path = "/tmp/double-free-error-report"; + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1 /* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_double_free (report_path); + + return 0; +} + +#include diff --git a/sampling-asan/tst-init.c b/sampling-asan/tst-init.c new file mode 100644 index 0000000000..45cafd3d69 --- /dev/null +++ b/sampling-asan/tst-init.c @@ -0,0 +1,120 @@ +/* Test and verify initialization 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 + . */ + +#include +#include + +#include + +#include "samasan.h" +#include "samasan_init.h" + +static void +test_repeated_init_and_deinit (void) +{ +#define REPEATS 100 + setenv("SAMASAN_ENABLE", "true", 1/* replace */); + + for (int i = 0; i < REPEATS; i++) + { + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == true); + samasan_deinit (); + TEST_VERIFY (samasan_is_enabled () == false); + } +#undef REPEATS +} + +static void +test_misconfigurations (void) +{ + setenv ("SAMASAN_ENABLE", + "truth" /* should be "yes" or "true" or "enable" or "on" */, + 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096" /* exceed the upper limit */, + 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "100", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == false); + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "409600" /* exceed the upper limit */, + 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "100", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == false); + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", + "100000" /* exceed the upper limit */, 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == false); + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "100", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.2" /* exceed 100% */, 1 /* replace */); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == false); +} + +static void +test_multiple_inits (void) +{ + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096" /* exceed the upper limit */, + 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "100", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == true); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == true); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled () == true); + + samasan_deinit (); + TEST_VERIFY (samasan_is_enabled () == false); +} + +static int +do_test (void) +{ + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + + test_repeated_init_and_deinit (); + test_misconfigurations (); + test_multiple_inits (); + + return 0; +} + +#include diff --git a/sampling-asan/tst-invalid-access.c b/sampling-asan/tst-invalid-access.c new file mode 100644 index 0000000000..2105b068d2 --- /dev/null +++ b/sampling-asan/tst-invalid-access.c @@ -0,0 +1,71 @@ +/* Test and verify an invalid-access case. + 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 + . */ + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_invalid_access (void) +{ + const size_t allocation_size = 512; + void *ptr; + char *cptr; + + ptr = samasan_allocate (allocation_size); + /* The sampling memory pool tends to allocate a memory chunk from + the tail of the memory pool for the first allocation. Because of + that, an access to the increased address from the pointer causes + an undefined behavior since it reaches beyond the memory pool. + So, just use the decreased address for here. */ + cptr = (char *) ptr - 4096 * 2; // One page for partition + *cptr = 'A'; /* an invalid access occurred! */ +} + +static void +test_invalid_access (const char *report_path) +{ + test_bed (do_invalid_access, "INVALID_ACCESS", report_path); +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/invalid-access-error-report"; + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1 /* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_invalid_access (report_path); + + return 0; +} + +#include diff --git a/sampling-asan/tst-invalid-access2.c b/sampling-asan/tst-invalid-access2.c new file mode 100644 index 0000000000..64cb34c643 --- /dev/null +++ b/sampling-asan/tst-invalid-access2.c @@ -0,0 +1,66 @@ +/* Test and verify an unaligned-access case. + 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 + . */ + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_invalid_access (void) +{ + const size_t allocation_size = 512; + void *ptr; + char *cptr; + + ptr = samasan_allocate (allocation_size); + cptr = (char *) ptr + 4096; + *cptr = 'A'; /* an invalid access occurred! */ +} + +static void +test_invalid_access (const char *report_path) +{ + test_bed (do_invalid_access, "INVALID_ACCESS", report_path); +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/invalid-access-error-report2"; + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "12288", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1 /* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_invalid_access (report_path); + + return 0; +} + +#include diff --git a/sampling-asan/tst-invalid-free.c b/sampling-asan/tst-invalid-free.c new file mode 100644 index 0000000000..e4a5af4209 --- /dev/null +++ b/sampling-asan/tst-invalid-free.c @@ -0,0 +1,65 @@ +/* Test and verify an invalid-free case. + 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 + . */ + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_invalid_free (void) +{ + void *ptr; + const size_t allocation_size = 512; + + ptr = samasan_allocate (allocation_size); + ptr += 512; + samasan_free (ptr); /* invalid free error occurred */ +} + +static void +test_invalid_free (const char *report_path) +{ + test_bed (do_invalid_free, "INVALID_FREE", report_path); +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/invalid-free-error-report"; + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1 /* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_invalid_free (report_path); + + return 0; +} + +#include diff --git a/sampling-asan/tst-invalid-free2.c b/sampling-asan/tst-invalid-free2.c new file mode 100644 index 0000000000..7ad911ec24 --- /dev/null +++ b/sampling-asan/tst-invalid-free2.c @@ -0,0 +1,65 @@ +/* Test and verify an invalid-free case. + 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 + . */ + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_invalid_free (void) +{ + void *ptr; + const size_t allocation_size = 512; + + ptr = samasan_allocate (allocation_size); + ptr -= 512; + samasan_free (ptr); /* invalid free error occurred */ +} + +static void +test_invalid_free (const char *report_path) +{ + test_bed (do_invalid_free, "INVALID_FREE", report_path); +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/invalid-free-error-report2"; + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1 /* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_invalid_free (report_path); + + return 0; +} + +#include diff --git a/sampling-asan/tst-invalid-write.c b/sampling-asan/tst-invalid-write.c new file mode 100644 index 0000000000..0e3632020f --- /dev/null +++ b/sampling-asan/tst-invalid-write.c @@ -0,0 +1,69 @@ +/* Test and verify allocations 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 + . */ + +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_invalid_write (void) +{ + size_t allocation_size = 512; + void *ptr; + char *cptr; + + ptr = samasan_allocate (allocation_size); + TEST_VERIFY (ptr != NULL); + cptr = (char *) ptr; + *(cptr + allocation_size) = 'a'; + samasan_free (ptr); /* error occurred! */ +} + +static void +test_invalid_write (const char *report_path) +{ + test_bed (do_invalid_write, "INVALID_WRITE", report_path); +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/invalid-write-error-report"; + setenv ("SAMASAN_ENABLE", "true", 1/* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "8192", 1/* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1/* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1/* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + /* invalid-write tests */ + test_invalid_write (report_path); + + return 0; +} + +#include diff --git a/sampling-asan/tst-out-of-memory-pool.c b/sampling-asan/tst-out-of-memory-pool.c new file mode 100644 index 0000000000..cdf89533f7 --- /dev/null +++ b/sampling-asan/tst-out-of-memory-pool.c @@ -0,0 +1,61 @@ +/* Test and verify an out-of-memory-pool case. + 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 + . */ + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_out_of_memory_pool (void) +{ + char *cptr = (char *) 0; + *cptr = 'A'; /* an out-of-memory-pool fault occurred! */ +} + +static void +test_out_of_memory_pool (const char *report_path) +{ + test_bed (do_out_of_memory_pool, "OUT_OF_MEMORY_POOL", report_path); +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/out-of-memory-pool-error-report"; + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "1", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1 /* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_out_of_memory_pool (report_path); + + return 0; +} + +#include diff --git a/sampling-asan/tst-partition-sizes.c b/sampling-asan/tst-partition-sizes.c new file mode 100644 index 0000000000..78cdaadaea --- /dev/null +++ b/sampling-asan/tst-partition-sizes.c @@ -0,0 +1,88 @@ +/* Test and verify configurable partition size 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 + . */ + +#include +#include + +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" + +static void +test_partition_size (size_t partition_size) +{ + char buf[100]; + const size_t allocation_size = 512; + void *ptr[10]; + char *cptr; + + sprintf (buf, "%ld", partition_size); + setenv("SAMASAN_PARTITION_SIZE", buf, 1/* replace */); + + samasan_init (); + TEST_VERIFY (samasan_is_enabled()); + for (size_t i = 0; i < 10; i++) + { + ptr[i] = samasan_allocate (allocation_size); + TEST_VERIFY (ptr[i] != NULL); + cptr = (char *) ptr[i]; + memset (cptr, 'A' + i, allocation_size); + } + for (size_t i = 0; i < 10; i++) + samasan_free (ptr[i]); + samasan_deinit(); +} + +static void +test_wrong_partition_size (size_t partition_size) +{ + char buf[100]; + + sprintf (buf, "%ld", partition_size); + setenv("SAMASAN_PARTITION_SIZE", buf, 1/* replace */); + + samasan_init (); + TEST_VERIFY (!samasan_is_enabled()); +} + +static int +do_test (void) +{ + const size_t page_size = 4096; + + setenv("SAMASAN_ENABLE", "true", 1/* replace */); + setenv("SAMASAN_MAX_ALLOC_SIZE", "4096", 1/* replace */); + setenv("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1/* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit(); + + for (size_t i = 0; i <= 10; i++) + test_partition_size (page_size * i); + + test_wrong_partition_size (-1); + test_wrong_partition_size (page_size * 11); + + return 0; +} + +#include diff --git a/sampling-asan/tst-pause-on-fork.c b/sampling-asan/tst-pause-on-fork.c new file mode 100644 index 0000000000..995f0914e5 --- /dev/null +++ b/sampling-asan/tst-pause-on-fork.c @@ -0,0 +1,139 @@ +/* Test and verify the pause-on-fork feature. + This is implemented by pthread_atfork (internally, __register_atfork). + 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 + . */ + +#include +#include +#include +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +static bool thread_ready = false; +static pthread_mutex_t mutex; +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + +static inline void +thread_get_ready (void) +{ + xpthread_mutex_lock (&mutex); + thread_ready = true; + xpthread_cond_signal (&cond); + xpthread_mutex_unlock (&mutex); +} + +static void* +memory_pool_stay_pause (void* dummy) +{ + thread_get_ready (); + return NULL; +} + +static void* +memory_pool_enable (void* dummy) +{ + thread_get_ready (); + sleep (1); + samasan_memory_pool_resume (); + return NULL; +} + +static void +test_pthread_fork (bool memory_pool_resume) +{ + pid_t pid; + pthread_t thread_id; + + if (memory_pool_resume) + thread_id = xpthread_create (NULL, &memory_pool_enable, NULL); + else + thread_id = xpthread_create (NULL, &memory_pool_stay_pause, NULL); + + xpthread_mutex_lock (&mutex); + while (!thread_ready) + xpthread_cond_wait (&cond, &mutex); + xpthread_mutex_unlock (&mutex); + + if (!memory_pool_resume) + alarm (1); + samasan_memory_pool_pause (); + pid = fork (); + + if (pid == 0) + _exit (0); + + /* If memory_pool_enable == false, never reached here. + The process will be terminated by the alarm call. */ + waitpid (pid, NULL, 0); + xpthread_join (thread_id); +} + +static void +test_bed (bool memory_pool_resume) +{ + pid_t pid; + int ret; + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + xpthread_mutex_init (&mutex, NULL); + thread_ready = false; + + pid = fork (); + if (pid == 0) + { + test_pthread_fork (memory_pool_resume); + _exit (0); + } + + waitpid (pid, &ret, 0); + if (memory_pool_resume) + { + TEST_VERIFY (WIFEXITED (ret)); + TEST_COMPARE (WTERMSIG (ret), 0); + } + else + { + TEST_VERIFY (WIFSIGNALED (ret)); + TEST_COMPARE (WTERMSIG (ret), SIGALRM); + } +} + +static int +do_test (void) +{ + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_PAUSE_ON_FORK", "true", 1 /* replace */); + + test_bed (false /* memory_pool_resume */); + test_bed (true /* memory_pool_resume */); + + return 0; +} + +#include diff --git a/sampling-asan/tst-sampling.c b/sampling-asan/tst-sampling.c new file mode 100644 index 0000000000..981903469d --- /dev/null +++ b/sampling-asan/tst-sampling.c @@ -0,0 +1,82 @@ +/* Test and verify 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 + . */ + +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +static void +test_sampling_on_uninited (void) +{ + const size_t allocation_size = 512; + + TEST_VERIFY (samasan_sampling_ok (allocation_size) == false); +} + +static void +test_sampling (void) +{ + const size_t allocation_size = 512; + const size_t large_allocation_size = 4097; + + TEST_VERIFY (samasan_sampling_ok (allocation_size) == true); + TEST_VERIFY (samasan_sampling_ok (large_allocation_size) == false); +} + +static void +test_sampling_exhausted (void) +{ +#define MAX_ALLOCATIONS 10 + const size_t allocation_size = 512; + void *ptr[MAX_ALLOCATIONS] = {NULL,}; + + for (int i = 0; i < MAX_ALLOCATIONS; i++) + ptr[i] = samasan_allocate (allocation_size); + TEST_VERIFY (samasan_sampling_ok (allocation_size) == false); + + for (int i = 0; i < MAX_ALLOCATIONS; i++) + samasan_free (ptr[i]); + TEST_VERIFY (samasan_sampling_ok (allocation_size) == true); +#undef MAX_ALLOCATIONS +} + +static int +do_test (void) +{ + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + test_sampling_on_uninited (); + + samasan_init (); + + test_sampling (); + test_sampling_exhausted (); + + return 0; +} + +#include diff --git a/sampling-asan/tst-threaded-allocation.c b/sampling-asan/tst-threaded-allocation.c new file mode 100644 index 0000000000..c75581ab5b --- /dev/null +++ b/sampling-asan/tst-threaded-allocation.c @@ -0,0 +1,90 @@ +/* Test and verify threaded-allocations 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 + . */ + +#include +#include +#include + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#define MAX_ON_GOING_ALLOCATIONS 100 +#define MAX_THREADS 10 +static int allocations_per_thread = MAX_ON_GOING_ALLOCATIONS / MAX_THREADS; +static int thread_id; +static void *global_ptr[MAX_ON_GOING_ALLOCATIONS] = { NULL, }; + +static void * +allocation_thread_func (void *data) +{ + int id = __atomic_fetch_add (&thread_id, 1, __ATOMIC_SEQ_CST); + + for (int i = 0; i < allocations_per_thread; i++) + { + global_ptr[id * MAX_THREADS + i] = samasan_allocate (sizeof (int)); + TEST_VERIFY (global_ptr[id * MAX_THREADS + i] != NULL); + } + + return NULL; +} + +static void +test_multi_threaded_allocation (void) +{ + pthread_t *threads = xcalloc (sizeof (pthread_t), MAX_THREADS); + void *ptr; + + thread_id = 0; + + for (int i = 0; i < MAX_THREADS; i++) + threads[i] = xpthread_create (NULL, allocation_thread_func, NULL); + + for (int i = 0; i < MAX_THREADS; i++) + xpthread_join (threads[i]); + ptr = samasan_allocate (sizeof (int)); + TEST_VERIFY (ptr == NULL); + + for (int i = 0; i < MAX_ON_GOING_ALLOCATIONS; i++) + { + samasan_free (global_ptr[i]); + global_ptr[i] = NULL; + } + free (threads); +} + +static int +do_test (void) +{ + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "100", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_multi_threaded_allocation (); + + return 0; +} + +#include diff --git a/sampling-asan/tst-use-after-free.c b/sampling-asan/tst-use-after-free.c new file mode 100644 index 0000000000..96e5b0c377 --- /dev/null +++ b/sampling-asan/tst-use-after-free.c @@ -0,0 +1,66 @@ +/* Test and verify a double-free case. + 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 + . */ + +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +#include "tst-common.c" + +static void +do_use_after_free (void) +{ + size_t allocation_size = 512; + int *ptr; + + ptr = (int *) samasan_allocate (allocation_size); + *(ptr) = 1; + samasan_free (ptr); + *(ptr) = 0; /* raise use-after-free */ +} + +static void +test_use_after_free (const char *report_path) +{ + test_bed (do_use_after_free, "USE_AFTER_FREE", report_path); +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/use-after-free-error-report"; + + setenv ("SAMASAN_ENABLE", "true", 1/* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1/* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1/* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1/* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1/* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_use_after_free (report_path); + + return 0; +} + +#include From patchwork Fri Sep 19 01:30:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sung-hun Kim X-Patchwork-Id: 120497 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 548BD3858416 for ; Fri, 19 Sep 2025 01:35:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 548BD3858416 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256 header.s=mail20170921 header.b=plw3sMo8 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) by sourceware.org (Postfix) with ESMTPS id 92E9F3858C66 for ; Fri, 19 Sep 2025 01:32:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 92E9F3858C66 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=samsung.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 92E9F3858C66 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=203.254.224.33 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; cv=none; b=Ac0Cz73EPKgBdfheX+Z+ZmT/YZNdZ6NYQLQdKgbvXTVtml6/TNlff8Tv+UZCorN33qUUKC4y2j5TVvoRDPBO6I+QR+BlpDyNq/Lsk5/gBM4NJs8yaQ+JoIzjmU35KSCPtTLaQBeJqZrWHeNmr+Z4LGT2JH4y/WY85hKpAZOhdGk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; c=relaxed/simple; bh=HFT0DugdBG1hFT6dFH4GZ18iW5DdbrY6aUfPBkq/17I=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=cFXK82GkvPP/x/kQvgrLiHgppaA0EOsrPrWj8bOwkTzbcaC6PgK6dRsuyLNdHXY9a2nT57RyfPe1YlkWvXrssoSow1xNwNehZdP4IkRJwNYPTT3Vf45RH+9PGLc9PxeWY9Fq+ER2SkkfcSk2dbs4JrvgPsmbUF92hDrkiiIMlMI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 92E9F3858C66 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20250919013209epoutp037a40839fc3aeb9c3329c4ba6ee56c959~miv5X8VJd1869618696epoutp03X for ; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20250919013209epoutp037a40839fc3aeb9c3329c4ba6ee56c959~miv5X8VJd1869618696epoutp03X DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1758245529; bh=AjP8RioHZZwvWDz0IpZhGjbD7W+jFatQ+yxggIMFdhQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=plw3sMo8eJ1Mg1C1EJ+zAYv/UZShN0PqcVscdzC/sZbhZZ7Vf08HDU503Oy6pCmLV mzLTDFljKb8QOcrITy2A/f3ymsxFWJpSjBIm46UVm2+1YzLebXKVOmktj5td38MZYk 2lM1xmkEIfhFr4/qWEdYx4TYsk6YUHnnEyXpNr88= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPS id 20250919013209epcas1p41bbb9b4604c7eac09c01a40104918b5f~miv46Kr9-1434414344epcas1p4l; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) Received: from epcas1p4.samsung.com (unknown [182.195.38.107]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cSZk03fmbz6B9mD; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from epsmtip2.samsung.com (unknown [182.195.34.31]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20250919013207epcas1p4fefc1ca947c4f3968793044ef61b22b8~miv36ppWc0371203712epcas1p4l; Fri, 19 Sep 2025 01:32:07 +0000 (GMT) Received: from localhost.localdomain (unknown [10.113.111.45]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250919013207epsmtip2eb5a5580a74265e6ce9a04d4290433f5~miv30xCEe0434004340epsmtip2i; Fri, 19 Sep 2025 01:32:07 +0000 (GMT) From: Sung-hun Kim To: libc-alpha@sourceware.org Cc: carlos@systemhalted.org, sfoon.kim@samsung.com, sebuns@gmail.com, dongkyun.s@samsung.com, sungguk.na@samsung.com Subject: [PATCH 3/7] sampling-asan: Add README.md Date: Fri, 19 Sep 2025 10:30:44 +0900 Message-Id: <20250919013048.1525832-3-sfoon.kim@samsung.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250919013048.1525832-1-sfoon.kim@samsung.com> MIME-Version: 1.0 X-CMS-MailID: 20250919013207epcas1p4fefc1ca947c4f3968793044ef61b22b8 X-Msg-Generator: CA CMS-TYPE: 101P cpgsPolicy: CPGSC10-361,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250919013207epcas1p4fefc1ca947c4f3968793044ef61b22b8 References: <20250919013048.1525832-1-sfoon.kim@samsung.com> X-Spam-Status: No, score=-7.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, MEDICAL_SUBJECT, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Signed-off-by: Sung-hun Kim --- sampling-asan/README.md | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 sampling-asan/README.md diff --git a/sampling-asan/README.md b/sampling-asan/README.md new file mode 100644 index 0000000000..7f0c08bef1 --- /dev/null +++ b/sampling-asan/README.md @@ -0,0 +1,88 @@ +# Sampling-based address sanitization (sampling-asan) + +Author: Sung-hun Kim (sfoon.kim@samsung.com, sebuns@gmail.com) + +Date: 2025.02.24 + +## Overview + +A sampling-asan module provides sampling-based address sanitization. +Sampling-asan is highly inspired by [LLVM's gwp-asan](https://llvm.org/docs/GwpAsan.html). +A memory allocation is sampled based on the sampling rate predefined +by the user (or default). Each sampled memory allocation is tracked, +and if a memory bug occurs in the tracked memory area, sampling-asan +generates a bug report at the user defined path (by default, stderr). +In this document, and all sampling-asan source codes, a prefix "samasan" +is used to represent sampling-asan. + +## Preallocated memory pool + +Sampling-asan uses a preallocated memory pool to track memory +allocation. The sizes of the memory pool and a memory block in the +memory pool can be configured by environmental variables (see the +"Configurable" section). Two memory blocks in the memory pool are +separated by an area, namely a partition. Access to the partition is +prohibited. + +An area which is practically allocated to the user is called a memory +chunk. Normally, the user is allowed to access only a memory chunk. +However, due to the constraint of memory management in the kernel, +memory bugs may not be detected even if it accesses areas outside the +memory chunk. But, if the area is modified, it can be detected on the +free call. + +## Memory bugs + +Memory bugs which can be detected sampling-asan are listed below. + +- **INVALID_FREE**: The program tries to free an area that was not allocated before. +- **DOUBLE_FREE**: The program tries to free a memory chunk that has already been freed. +- **USE_AFTER_FREE**: The program tries to access a freed chunk. +- **INVALID_ACCESS**: The program tries to access a prohibited area (e.g., partition). +- **INVALID_WRITE**: The area outside the memory chunk is modified. +- **OUT_OF_MEMORY_POOL**: An error occurrs outside the memory pool. + +## Configurables + +Sampling-asan configures sanitization configuration using environmental +variables. Below is a list of configurables and their default values. + +- **SAMASAN_ENABLE=false**: It indicates whether the program uses sampling-asan or not. +- **SAMASAN_SAMPLING_RATE=0.005**: It represents the sampling-rate used by sampling-asan. +It can have a value between 0.0 and 1.0. +- **SAMASAN_MAX_ON_GOING_ALLOCATIONS=100**: It means the maximum number of concurrent allocations. +- **SAMASAN_MAX_ALLOC_SIZE=4096**: It means the maximum size of each allocation. +An allocation can occupy up to 40,960 bytes. +- **SAMASAN_OUTPUT_PATH=stderr**: It indicates the path of a bug report created when a memory bug occurs. +- **SAMASAN_PARTITION_SIZE=4096**: It represents the size of a boundary area between two memory blocks. +Sampling-asan cannot use this area for allocations. +- **SAMASAN_PAUSE_ON_FORK=true**: Sampling-asan synchronizes the memory pool when a child process is forked +if this variable is true. +- **SAMASAN_CHUNK_PICK=CENTER**: It indicates the picking tendency when allocating a memory chunk from a +memory block. Sampling-asan supports LEFT, RIGHT, and CENTER picking +tendencies. + +## How to use sampling-asan + +Run the program with sampling-asan using default configuration: + + $ export LD_PRELOAD=/usr/lib/libc_malloc_debug.so.0 + $ export MALLOC_SANITIZE_=sampling-asan + $ export SAMASAN_ENABLE=true + $ ./run_my_code + +Or, + + $ export LD_PRELOAD=/usr/lib/libc_malloc_debug.so.0 + $ export MALLOC_SANITIZE_=samasan + $ export SAMASAN_ENABLE=true + $ ./run_my_code + +In order to set the custom sampling-rate (e.g., using 0.01): + + $ SAMASAN_SAMPLING_RATE=0.01 ./run_my_code + +In order to set the report path to /path/to/error-report.txt + + $ SAMASAN_OUTPUT_PATH=/path/to/error-report.txt ./run_my_code + From patchwork Fri Sep 19 01:30:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sung-hun Kim X-Patchwork-Id: 120496 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E1A7E3858C66 for ; Fri, 19 Sep 2025 01:34:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E1A7E3858C66 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256 header.s=mail20170921 header.b=tOyvoeB8 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) by sourceware.org (Postfix) with ESMTPS id B2B013858405 for ; Fri, 19 Sep 2025 01:32:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B2B013858405 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=samsung.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B2B013858405 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; cv=none; b=pYd+smdDS65WWcOqSeK2pSgesMC+5MZcxmkftQrta/65rw9xbezO789CluzyqTab1Oh1hgEn+FsQ/lgsgIn+pO4x5FBNVFH0cxJaR1W2s1PWkY20WyYV29zcGuegx9f11CQ02Nt4wOohXU2OV9j9m87Z84r2S7ShC/l2P9duoKc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; c=relaxed/simple; bh=AeVciYUTk4eDWPfAcLtVwHECdJberNbb4GasWvtdYPw=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=IIN5h27y+vI1Hu2GnR9Ej6nDzUHw2st/fu02Q4gh2EfAgUbxJHljfBubFJi3F2ghD+gOfqyYEvg1jGETqKY3IlBBDni2RAVqA/VuKXljdca353V695+fkVF93UJFvH/9r7ofnfujzwCAUwUUOMGuOpIAfY7hwZm7DttgE7HLcQ4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B2B013858405 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250919013209epoutp01daa16826c1aad446a68e055449d2e307~miv5il8fT2278222782epoutp01O for ; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250919013209epoutp01daa16826c1aad446a68e055449d2e307~miv5il8fT2278222782epoutp01O DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1758245529; bh=QT18TpZlXFsG5RhIamy+xuZxr44sNB5pDj87jk8pB8c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tOyvoeB81Dy2I6ErmaOu07nTM+H+E3LFR2tRvBF30ssYthRz8X33qgwDvdShFygUU gc/GN0RaHURTQTYFuEOYkomCV+5PAToVcqkB4d7JLDySfY07aL4MHa8g11AVlgAnWm QAh91H5XYjoi3ZFKGuqa/2rzYoCwgzgQkLDs90pY= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPS id 20250919013209epcas1p4d20048ac2def801a75b05b6f7deed0d6~miv4_YdXZ1168211682epcas1p45; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) Received: from epcas1p4.samsung.com (unknown [182.195.38.98]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cSZk048y7z6B9mJ; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from epsmtip2.samsung.com (unknown [182.195.34.31]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20250919013208epcas1p460ef29c9f25c576453b47ad0b5d73771~miv3_5cQ-1168211682epcas1p4y; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from localhost.localdomain (unknown [10.113.111.45]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250919013207epsmtip272e16687656ee62d101110ace57db153~miv37RGyK0446004460epsmtip2V; Fri, 19 Sep 2025 01:32:07 +0000 (GMT) From: Sung-hun Kim To: libc-alpha@sourceware.org Cc: carlos@systemhalted.org, sfoon.kim@samsung.com, sebuns@gmail.com, dongkyun.s@samsung.com, sungguk.na@samsung.com Subject: [PATCH 4/7] sampling-asan: Support old version of C (before C11 standard) Date: Fri, 19 Sep 2025 10:30:45 +0900 Message-Id: <20250919013048.1525832-4-sfoon.kim@samsung.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250919013048.1525832-1-sfoon.kim@samsung.com> MIME-Version: 1.0 X-CMS-MailID: 20250919013208epcas1p460ef29c9f25c576453b47ad0b5d73771 X-Msg-Generator: CA CMS-TYPE: 101P cpgsPolicy: CPGSC10-361,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250919013208epcas1p460ef29c9f25c576453b47ad0b5d73771 References: <20250919013048.1525832-1-sfoon.kim@samsung.com> X-Spam-Status: No, score=-10.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Prior to the C11 standard, the C language did not include support for the _Generic macro, which is commonly used for mimicking polymorphism. To address this limitation, I have implemented an alternative approach using a void pointer and a variable option type to differentiate function calls. This change ensures compatiblity with the older C standards, allowing sampling-asan to codes implemented on the old C standards. Signed-off-by: Sung-hun Kim --- sampling-asan/samasan_init.c | 171 ++++++++++++++++-------- sampling-asan/samasan_variable_init.def | 33 +++-- 2 files changed, 127 insertions(+), 77 deletions(-) diff --git a/sampling-asan/samasan_init.c b/sampling-asan/samasan_init.c index 3a53068357..0136fe9a0d 100644 --- a/sampling-asan/samasan_init.c +++ b/sampling-asan/samasan_init.c @@ -105,9 +105,14 @@ samasan_set_variable_range (samasan_option_t option, uint32_t max, samasan_configurables[option].range.min = min; } -/* TODO: _Generic is supported since C11 standard (ISO/IEC 9899:2011) - Thus, the version of C should be checked and an alternative should be - provided for the unsupported versions. */ +/* _Generic is supported since C11 standard (ISO/IEC 9899:2011). */ +#if __STDC_VERSION__ >= 201112L +#define __C_GENERIC_SUPPORTED__ true +#else +#define __C_GENERIC_SUPPORTED__ false +#endif + +#if __C_GENERIC_SUPPORTED__ /* A boolean type variable is treated as a integer type variable in C, so I assign samasan_set_variable_bool to the integer type. */ #define samasan_set_variable(index, value) \ @@ -115,13 +120,42 @@ samasan_set_variable_range (samasan_option_t option, uint32_t max, uint32_t: samasan_set_variable_uint, \ const char *: samasan_set_variable_char, \ char *: samasan_set_variable_char, \ - int: samasan_set_variable_bool) (index, value) + bool: samasan_set_variable_bool) (index, value) +#else /* __C_GENERIC_SUPPORTED__ */ +#define OPTION_IS_UINT(option) (option == SAMASAN_MAX_ALLOC_SIZE || \ + option == SAMASAN_MAX_ON_GOING_ALLOCATIONS || \ + option == SAMASAN_PARTITION_SIZE || \ + option == SAMASAN_SAMPLING_RATE || \ + option == SAMASAN_CHUNK_PICK) +#define OPTION_IS_CHAR(option) (option == SAMASAN_OUTPUT_PATH) +#define OPTION_IS_BOOL(option) (option == SAMASAN_ENABLE || \ + option == SAMASAN_PAUSE_ON_FORK) + +static void __samasan_set_variable +(samasan_option_t option, void *value) +{ + if (OPTION_IS_UINT (option)) { + uint32_t *val = (uint32_t *) value; + samasan_set_variable_uint (option, *val); + } else if (OPTION_IS_CHAR (option)) { + char **val = (char **) value; + samasan_set_variable_char (option, *val); + } else if (OPTION_IS_BOOL (option)) { + bool *val = (bool *) value; + samasan_set_variable_bool (option, *val); + } else + samasan_exit_with_message ("Unknown variable type. Abort.\n"); +} + +#define samasan_set_variable(option, value) \ + __samasan_set_variable (option, (void *) &value) +#endif /* __C_GENERIC_SUPPORTED__ */ #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) + value <= samasan_configurables[index].range.max) #define samasan_get_enabled() samasan_get_variable (SAMASAN_ENABLE).b_value #define samasan_get_output_path() \ @@ -139,44 +173,6 @@ samasan_set_variable_range (samasan_option_t option, uint32_t max, #define samasan_get_chunk_pick_style() \ samasan_get_variable (SAMASAN_CHUNK_PICK).i_value -#include "samasan_variable_init.def" - -static void -samasan_variable_init (void) -{ - if (SAMASAN_UNLIKELY (!samasan_configurables)) - return; - - samasan_set_variable (SAMASAN_ENABLE, - DEFAULT_SAMASAN_ENABLED); - samasan_set_variable (SAMASAN_SAMPLING_RATE, - DEFAULT_SAMASAN_SAMPLING_RATE); - samasan_set_variable_range (SAMASAN_SAMPLING_RATE, - MAX_SAMASAN_SAMPLING_RATE, - MIN_SAMASAN_SAMPLING_RATE); - samasan_set_variable (SAMASAN_MAX_ALLOC_SIZE, - DEFAULT_SAMASAN_MAX_ALLOC_SIZE); - samasan_set_variable_range (SAMASAN_MAX_ALLOC_SIZE, - MAX_SAMASAN_MAX_ALLOC_SIZE, - MIN_SAMASAN_MAX_ALLOC_SIZE); - samasan_set_variable (SAMASAN_MAX_ON_GOING_ALLOCATIONS, - DEFAULT_SAMASAN_MAX_ON_GOING_ALLOCATIONS); - samasan_set_variable_range (SAMASAN_MAX_ON_GOING_ALLOCATIONS, - MAX_SAMASAN_MAX_ON_GOING_ALLOCATIONS, - MIN_SAMASAN_MAX_ON_GOING_ALLOCATIONS); - samasan_set_variable (SAMASAN_OUTPUT_PATH, - DEFAULT_SAMASAN_OUTPUT_PATH); - samasan_set_variable (SAMASAN_PARTITION_SIZE, - DEFAULT_SAMASAN_PARTITION_SIZE); - samasan_set_variable_range (SAMASAN_PARTITION_SIZE, - MAX_SAMASAN_PARTITION_SIZE, - MIN_SAMASAN_PARTITION_SIZE); - samasan_set_variable (SAMASAN_PAUSE_ON_FORK, - DEFAULT_SAMASAN_PAUSE_ON_FORK); - samasan_set_variable (SAMASAN_CHUNK_PICK, - DEFAULT_SAMASAN_CHUNK_PICK); -} - static bool import_samasan_variable (int type, const char *val) { @@ -184,10 +180,12 @@ import_samasan_variable (int type, const char *val) { case SAMASAN_ENABLE: { - if (!strcmp (val, "on") || !strcmp (val, "enable") - || !strcmp (val, "yes") || !strcmp (val, "true")) - samasan_set_variable (SAMASAN_ENABLE, true); - break; + bool var = false; + if (!strcmp (val, "on") || !strcmp (val, "enable") + || !strcmp (val, "yes") || !strcmp (val, "true")) + var = true; + samasan_set_variable (SAMASAN_ENABLE, var); + break; } case SAMASAN_MAX_ALLOC_SIZE: { @@ -207,7 +205,7 @@ import_samasan_variable (int type, const char *val) if (!converted) return false; if (!samasan_check_variable_range (SAMASAN_MAX_ON_GOING_ALLOCATIONS, - converted)) + converted)) return false; samasan_set_variable (SAMASAN_MAX_ON_GOING_ALLOCATIONS, converted); break; @@ -229,34 +227,34 @@ import_samasan_variable (int type, const char *val) } case SAMASAN_PARTITION_SIZE: { - int32_t converted = (int32_t) atoi (val); - if (converted < 0) - return false; + uint32_t converted = (uint32_t) atoi (val); if (!samasan_check_variable_range (SAMASAN_PARTITION_SIZE, - (uint32_t) converted)) + converted)) return false; - samasan_set_variable (SAMASAN_PARTITION_SIZE, (uint32_t) converted); + 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")) - samasan_set_variable (SAMASAN_PAUSE_ON_FORK, true); - else - samasan_set_variable (SAMASAN_PAUSE_ON_FORK, false); + 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")) - samasan_set_variable (SAMASAN_CHUNK_PICK, (uint32_t) PICK_FROM_LEFT); + pick = (uint32_t) PICK_FROM_LEFT; else if (!strcmp (val, "right") || !strcmp (val, "RIGHT")) - samasan_set_variable (SAMASAN_CHUNK_PICK, (uint32_t) PICK_FROM_RIGHT); + pick = (uint32_t) PICK_FROM_RIGHT; else if (!strcmp (val, "center") || !strcmp (val, "CENTER")) - samasan_set_variable (SAMASAN_CHUNK_PICK, (uint32_t) PICK_CENTER); + pick = (uint32_t) PICK_CENTER; else return false; + samasan_set_variable (SAMASAN_CHUNK_PICK, pick); break; } default: @@ -265,6 +263,61 @@ import_samasan_variable (int type, const char *val) 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) { diff --git a/sampling-asan/samasan_variable_init.def b/sampling-asan/samasan_variable_init.def index 954ce20eeb..3a5441bc15 100644 --- a/sampling-asan/samasan_variable_init.def +++ b/sampling-asan/samasan_variable_init.def @@ -16,25 +16,22 @@ License along with the GNU C Library; if not, see . */ -/* All integer values defined in this file should have type of - unsigned integer. */ - -#define DEFAULT_SAMASAN_ENABLED false -#define DEFAULT_SAMASAN_SAMPLING_RATE 500u /* 0.005 */ -#define DEFAULT_SAMASAN_MAX_ALLOC_SIZE 4096u -#define DEFAULT_SAMASAN_MAX_ON_GOING_ALLOCATIONS 100u +#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 4096u /* 1 page */ -#define DEFAULT_SAMASAN_PAUSE_ON_FORK true -#define DEFAULT_SAMASAN_CHUNK_PICK PICK_CENTER /* defined in samasan_allocate.h */ +#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 100000u -#define MAX_SAMASAN_MAX_ALLOC_SIZE 40960u -#define MAX_SAMASAN_MAX_ON_GOING_ALLOCATIONS 10000u -#define MAX_SAMASAN_PARTITION_SIZE 40960u /* 10 pages */ +#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 0u -#define MIN_SAMASAN_MAX_ALLOC_SIZE 4096u -#define MIN_SAMASAN_MAX_ON_GOING_ALLOCATIONS 1u -#define MIN_SAMASAN_PARTITION_SIZE 0u /* no partition page */ +#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 */ From patchwork Fri Sep 19 01:30:46 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sung-hun Kim X-Patchwork-Id: 120500 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B240C3858D21 for ; Fri, 19 Sep 2025 01:39:01 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B240C3858D21 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256 header.s=mail20170921 header.b=cy7Gs850 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) by sourceware.org (Postfix) with ESMTPS id 93BEB3858401 for ; Fri, 19 Sep 2025 01:32:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 93BEB3858401 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=samsung.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 93BEB3858401 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=203.254.224.33 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245531; cv=none; b=xQChD6PFhpv4neDk7S2jkB7H6ClP1xlA6o+ASQhNBpg+/ZTQXJVCaoRKSooGL2xfVo4Leac81UUePyoXVisuer+TUZGmHfFqfkKOy1vN2JNsRQghSK/PU0UzcJRINY3+T1FQKUu5xQ58Cnipdm39iSjpUpCwgIZ88YdCuq+/peU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245531; c=relaxed/simple; bh=4Gff6X9VqRJf7DaerHrJghOWrit3VB2olCkemtIi2/4=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=WEVA/vi9FGTm/i96lquBGh/LTnhBwX3ZbOhWvM6zfOw54uGttuxDMcgpScwhgRvZh+bVu0/fQuy1Dxhzxa6Zzlg7D+Htqd6Nnj4xBTLc9am7jfPGgKcBmrQ0IL63xEVTKTVJb4hGGrk/4diYOF82S9U96tQhiO3E5wtbXSuPWAg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 93BEB3858401 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20250919013209epoutp039a7b62cc8aabfbccd5a680d1b72541d6~miv5QJb1l1871718717epoutp03e for ; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20250919013209epoutp039a7b62cc8aabfbccd5a680d1b72541d6~miv5QJb1l1871718717epoutp03e DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1758245529; bh=Fya9u4YS4dc6hYHcScssLOGookgsRrXpHT1re8wizI0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cy7Gs850CtWERrMSrctBaOEkLAY0nrJ35+ZnUVCQM1YT8qS/zF1ZiiCaoKILvf41E 6tFtW+zqhVytxWIlzlYLQ2kCcC31BgoXTzwetBCVKq5s+aIlZNwa7cte6KBgazua4L pH21ybBh+wnash+PCIQgXDtm4p8ngkEvV3o33yz8= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPS id 20250919013209epcas1p3fc242c1331e049a189795342169294ff~miv4_1Qsd1395413954epcas1p36; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) Received: from epcas1p1.samsung.com (unknown [182.195.38.103]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cSZk053FYz6B9mG; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from epsmtip2.samsung.com (unknown [182.195.34.31]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20250919013208epcas1p36347e2390768983cb3e01dc6e94fd9fb~miv4FkN171395413954epcas1p3y; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from localhost.localdomain (unknown [10.113.111.45]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250919013208epsmtip2f01f2840ad29ab161b76998cc87ec59c~miv3-adPb0434004340epsmtip2j; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) From: Sung-hun Kim To: libc-alpha@sourceware.org Cc: carlos@systemhalted.org, sfoon.kim@samsung.com, sebuns@gmail.com, dongkyun.s@samsung.com, sungguk.na@samsung.com Subject: [PATCH 5/7] Makeconfig: Add a sampling-asan directory to the build script Date: Fri, 19 Sep 2025 10:30:46 +0900 Message-Id: <20250919013048.1525832-5-sfoon.kim@samsung.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250919013048.1525832-1-sfoon.kim@samsung.com> MIME-Version: 1.0 X-CMS-MailID: 20250919013208epcas1p36347e2390768983cb3e01dc6e94fd9fb X-Msg-Generator: CA CMS-TYPE: 101P cpgsPolicy: CPGSC10-361,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250919013208epcas1p36347e2390768983cb3e01dc6e94fd9fb References: <20250919013048.1525832-1-sfoon.kim@samsung.com> X-Spam-Status: No, score=-10.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Signed-off-by: Sung-hun Kim --- Makeconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makeconfig b/Makeconfig index 7102d922b2..e01e268f5b 100644 --- a/Makeconfig +++ b/Makeconfig @@ -1409,7 +1409,7 @@ all-subdirs = csu assert ctype locale intl catgets math setjmp signal \ posix io termios resource misc socket sysvipc gmon \ gnulib iconv iconvdata wctype manual po argp \ localedata timezone rt conform debug mathvec support \ - dlfcn elf + dlfcn elf sampling-asan ifndef avoid-generated # sysd-sorted itself will contain rules making the sysd-sorted target From patchwork Fri Sep 19 01:30:47 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sung-hun Kim X-Patchwork-Id: 120498 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7B5D6385840B for ; Fri, 19 Sep 2025 01:35:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7B5D6385840B Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256 header.s=mail20170921 header.b=KcpBi+J1 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) by sourceware.org (Postfix) with ESMTPS id BAD743858408 for ; Fri, 19 Sep 2025 01:32:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BAD743858408 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=samsung.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BAD743858408 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; cv=none; b=YnZ+MYXmQWXct2/J17zbJkvmmddQRa5KaeMlJVi0pV0Ic3l4cK39mySwlsL2P7N28GNOUgcXn1BKwV/uVpCBVTctGjE0F2LGeE7Uch2MSaysGAMcssVvKu9TjrOcZIdUjYQFIcv/iiBSPoaHWZlPTwZQ55nDExLGb0lkJpzWbds= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; c=relaxed/simple; bh=wUBQelZ+ftOFXFatb9Cnd/4Qqxn2FIOg3X8E54X2IjM=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=fRsbj+ERnTq1T+F/Dx6FxPqLi0ixR4Z9IDuohwqlONSrQa909BvzR+dfYiH6WK6XVfPtD9/NU3pYg1I+B2dXITtarMInpsnscomPW82t1FHKea5zGzaaldj6UXdXlvzvdcxJRmgcNJfbHj9tJmADEpfIFPnAq7TZIWOiO4whALc= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BAD743858408 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250919013209epoutp01439c93caa5b04bf4698553ab4cff65ef~miv5fZNk12500025000epoutp01B for ; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250919013209epoutp01439c93caa5b04bf4698553ab4cff65ef~miv5fZNk12500025000epoutp01B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1758245529; bh=OA/IOftQwstC0gctr16jKpuJINRAoX3VvRCpZH5uud0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KcpBi+J14M14xS/x7VtTERF2ML1p9dySprrNfT+GoLr02peLgaG3R0UmibuMe5fQy Xvlt5DKOnd9ZZPQiS5OokpHEGYcBnTladrYMikleIhZhlLdeC4Lczzu65+A+Tfnfk0 rJh4+G1vN1IMTUSxw64d7ZR2Ur2cDtYRCshQpHQc= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPS id 20250919013209epcas1p140dd03863f3e51d0b2c1ca24593e9600~miv5NSTna0717407174epcas1p1G; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) Received: from epcas1p4.samsung.com (unknown [182.195.38.110]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cSZk05nJcz6B9mK; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from epsmtip2.samsung.com (unknown [182.195.34.31]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250919013208epcas1p1b61223c713181b35cde88afb6e143eaf~miv4KU0ac0717407174epcas1p1A; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from localhost.localdomain (unknown [10.113.111.45]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250919013208epsmtip2b0f541c53d70fe638e76b67f91254f09~miv4GT91w0450404504epsmtip2h; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) From: Sung-hun Kim To: libc-alpha@sourceware.org Cc: carlos@systemhalted.org, sfoon.kim@samsung.com, sebuns@gmail.com, dongkyun.s@samsung.com, sungguk.na@samsung.com Subject: [PATCH 6/7] malloc: Add malloc_sanitize to libc_malloc_debug.so Date: Fri, 19 Sep 2025 10:30:47 +0900 Message-Id: <20250919013048.1525832-6-sfoon.kim@samsung.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250919013048.1525832-1-sfoon.kim@samsung.com> MIME-Version: 1.0 X-CMS-MailID: 20250919013208epcas1p1b61223c713181b35cde88afb6e143eaf X-Msg-Generator: CA CMS-TYPE: 101P cpgsPolicy: CPGSC10-361,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250919013208epcas1p1b61223c713181b35cde88afb6e143eaf References: <20250919013048.1525832-1-sfoon.kim@samsung.com> X-Spam-Status: No, score=-11.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org A new hook, malloc_sanitize, has been introduced to provide address sanitization extension for glibc malloc. As a proof of concept, I have implemented sampling-asan as the back-end method of malloc_sanitize. This feature can be enabled by setting the MALLOC_SANITIZE_ tunable as follows: $ LD_PRELOAD=/path/to/libc_malloc_debug.so MALLOC_SANITIZE_=sampling-asan fore configuring runtime options of sampling-asan, please refer the README in the sampling-asan directory. Signed-off-by: Sung-hun Kim --- Rules | 20 +++++ elf/dl-tunables.list | 6 ++ malloc/Makefile | 8 ++ malloc/malloc-debug.c | 17 ++++ malloc/malloc-sanitize.c | 147 +++++++++++++++++++++++++++++++++++ malloc/tst-malloc-sanitize.c | 79 +++++++++++++++++++ 6 files changed, 277 insertions(+) create mode 100644 malloc/malloc-sanitize.c create mode 100644 malloc/tst-malloc-sanitize.c diff --git a/Rules b/Rules index 44c041c491..995773616a 100644 --- a/Rules +++ b/Rules @@ -167,6 +167,7 @@ tests: $(tests:%=$(objpfx)%.out) $(tests-internal:%=$(objpfx)%.out) \ $(tests-malloc-hugetlb1:%=$(objpfx)%-malloc-hugetlb1.out) \ $(tests-malloc-hugetlb2:%=$(objpfx)%-malloc-hugetlb2.out) \ $(tests-malloc-largetcache:%=$(objpfx)%-malloc-largetcache.out) \ + $(tests-malloc-sanitize:%=$(objpfx)%-malloc-sanitize.out) \ $(tests-special) $(tests-printers-out) xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special) endif # $(run-built-tests) != no @@ -182,6 +183,7 @@ tests-expected = $(tests) $(tests-internal) $(tests-printers) \ $(tests-malloc-hugetlb1:%=%-malloc-hugetlb1) \ $(tests-malloc-hugetlb2:%=%-malloc-hugetlb2) \ $(tests-malloc-largetcache:%=%-malloc-largetcache) \ + $(tests-malloc-sanitize:%=%-malloc-sanitize) \ $(tests-mcheck:%=%-mcheck) xtests-expected = $(xtests) endif # $(run-built-tests) != no @@ -215,6 +217,7 @@ binaries-malloc-check-tests = $(tests-malloc-check:%=%-malloc-check) binaries-malloc-hugetlb1-tests = $(tests-malloc-hugetlb1:%=%-malloc-hugetlb1) binaries-malloc-hugetlb2-tests = $(tests-malloc-hugetlb2:%=%-malloc-hugetlb2) binaries-malloc-largetcache-tests = $(tests-malloc-largetcache:%=%-malloc-largetcache) +binaries-malloc-sanitize-tests = $(tests-malloc-sanitize:%=%-malloc-sanitize) else binaries-all-notests = binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs) @@ -229,6 +232,7 @@ binaries-malloc-check-tests = binaries-malloc-hugetlb1-tests = binaries-malloc-hugetlb2-tests = binaries-malloc-largetcache-tests = +binaries-malloc-sanitize-tests = endif binaries-pie = $(binaries-pie-tests) $(binaries-pie-notests) @@ -303,6 +307,14 @@ $(addprefix $(objpfx),$(binaries-malloc-largetcache-tests)): %-malloc-largetcach $(+link-tests) endif +ifneq "$(strip $(binaries-malloc-sanitize-tests))" "" +$(addprefix $(objpfx),$(binaries-malloc-sanitize-tests)): %-malloc-sanitize: %.o \ + $(link-extra-libs-tests) \ + $(sort $(filter $(common-objpfx)lib%,$(link-libc))) \ + $(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit) + $(+link-tests) +endif + ifneq "$(strip $(binaries-pie-tests))" "" $(addprefix $(objpfx),$(binaries-pie-tests)): %: %.o \ $(link-extra-libs-tests) \ @@ -358,6 +370,14 @@ $(1)-malloc-largetcache-ENV += GLIBC_TUNABLES=glibc.malloc.tcache_max=1048576 endef $(foreach t,$(tests-malloc-largetcache),$(eval $(call malloc-largetcache-ENVS,$(t)))) +# All malloc-sanitize tests will be run with MALLOC_SANITIZE_=sampling-asan +define malloc-sanitize-ENVS +$(1)-malloc-sanitize-ENV = MALLOC_SANITIZE_=sampling-asan SAMASAN_ENABLE=true \ + SAMASAN_SAMPLING_RATE=1.0 SAMASAN_MAX_ALLOC_SIZE=4096 \ + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so +endef +$(foreach t,$(tests-malloc-sanitize),$(eval $(call malloc-sanitize-ENVS,$(t)))) + # mcheck tests need the debug DSO to support -lmcheck. define mcheck-ENVS $(1)-mcheck-ENV = LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list index c03c9967f0..dd4804b77b 100644 --- a/elf/dl-tunables.list +++ b/elf/dl-tunables.list @@ -82,6 +82,12 @@ glibc { type: SIZE_T minval: 0 } + malloc_sanitize { + type: STRING + minval: 7 + maxval: 13 + env_alias: MALLOC_SANITIZE_ + } } elision { diff --git a/malloc/Makefile b/malloc/Makefile index 83f6c873e8..7d7a883734 100644 --- a/malloc/Makefile +++ b/malloc/Makefile @@ -41,6 +41,7 @@ tests := \ tst-malloc-check \ tst-malloc-fork-deadlock \ tst-malloc-random \ + tst-malloc-sanitize \ tst-malloc-stats-cancellation \ tst-malloc-tcache-leak \ tst-malloc-thread-exit \ @@ -106,6 +107,7 @@ tests-exclude-malloc-check = \ tst-compathooks-off \ tst-compathooks-on \ tst-malloc-check \ + tst-malloc-sanitize \ tst-malloc-tcache-leak \ tst-malloc-usable \ tst-mallocfork2 \ @@ -130,6 +132,7 @@ tests-exclude-hugetlb1 = \ tst-interpose-static-nothread \ tst-interpose-static-thread \ tst-interpose-thread \ + tst-malloc-sanitize \ tst-malloc-tcache-leak \ tst-malloc-usable \ tst-malloc-usable-tunables \ @@ -155,6 +158,7 @@ tests-exclude-largetcache = \ tst-interpose-static-thread \ tst-interpose-thread \ tst-malloc-backtrace \ + tst-malloc-sanitize \ tst-malloc-usable \ tst-malloc-usable-tunables \ tst-mallocstate \ @@ -175,6 +179,7 @@ tests-exclude-mcheck = \ tst-compathooks-on \ tst-malloc-backtrace \ tst-malloc-fork-deadlock \ + tst-malloc-sanitize \ tst-malloc-stats-cancellation \ tst-malloc-tcache-leak \ tst-malloc-thread-exit \ @@ -382,6 +387,9 @@ tst-malloc-usable-ENV = MALLOC_CHECK_=3 \ LD_PRELOAD=$(objpfx)/libc_malloc_debug.so tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 \ LD_PRELOAD=$(objpfx)/libc_malloc_debug.so +tst-malloc-sanitize-ENV = MALLOC_SANITIZE_=sampling-asan SAMASAN_ENABLE=true \ + SAMASAN_SAMPLING_RATE=1.0 SAMASAN_MAX_ALLOC_SIZE=4096 \ + LD_PRELOAD=$(objpfx)/libc_malloc_debug.so tst-mxfast-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=0:glibc.malloc.mxfast=0 diff --git a/malloc/malloc-debug.c b/malloc/malloc-debug.c index 8bcb5652e0..8cf036f022 100644 --- a/malloc/malloc-debug.c +++ b/malloc/malloc-debug.c @@ -52,6 +52,7 @@ enum malloc_debug_hooks MALLOC_MCHECK_HOOK = 1 << 0, /* mcheck() */ MALLOC_MTRACE_HOOK = 1 << 1, /* mtrace() */ MALLOC_CHECK_HOOK = 1 << 2, /* MALLOC_CHECK_ or glibc.malloc.check. */ + MALLOC_SANITIZE_HOOK = 1 << 3, /* MALLOC_SANITIZE_ or glibc.malloc.sanitize. */ }; static unsigned __malloc_debugging_hooks; @@ -76,6 +77,7 @@ __malloc_debug_disable (enum malloc_debug_hooks flag) #include "mcheck.c" #include "mtrace.c" #include "malloc-check.c" +#include "malloc-sanitize.c" #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_24) extern void (*__malloc_initialize_hook) (void); @@ -118,6 +120,8 @@ generic_hook_ini (void) will not try to optimize it away. */ __libc_free (__libc_malloc (0)); + initialize_malloc_sanitize (); + #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_24) void (*hook) (void) = __malloc_initialize_hook; if (hook != NULL) @@ -171,6 +175,8 @@ __debug_malloc (size_t bytes) void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); if (__glibc_unlikely (hook != NULL)) return (*hook)(bytes, RETURN_ADDRESS (0)); + if (__is_malloc_debug_enabled (MALLOC_SANITIZE_HOOK)) + return malloc_sanitize (bytes); void *victim = NULL; size_t orig_bytes = bytes; @@ -198,6 +204,11 @@ __debug_free (void *mem) (*hook)(mem, RETURN_ADDRESS (0)); return; } + if (__is_malloc_debug_enabled (MALLOC_SANITIZE_HOOK)) + { + free_sanitize (mem); + return; + } if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)) mem = free_mcheck (mem); @@ -221,6 +232,9 @@ __debug_realloc (void *oldmem, size_t bytes) if (__glibc_unlikely (hook != NULL)) return (*hook)(oldmem, bytes, RETURN_ADDRESS (0)); + if (__is_malloc_debug_enabled (MALLOC_SANITIZE_HOOK)) + return realloc_sanitize (oldmem, bytes); + size_t orig_bytes = bytes, oldsize = 0; void *victim = NULL; @@ -364,6 +378,9 @@ __debug_calloc (size_t nmemb, size_t size) { size_t bytes; + if (__is_malloc_debug_enabled (MALLOC_SANITIZE_HOOK)) + return calloc_sanitize (nmemb, size); + if (__glibc_unlikely (__builtin_mul_overflow (nmemb, size, &bytes))) { errno = ENOMEM; diff --git a/malloc/malloc-sanitize.c b/malloc/malloc-sanitize.c new file mode 100644 index 0000000000..21d31fc555 --- /dev/null +++ b/malloc/malloc-sanitize.c @@ -0,0 +1,147 @@ +/* glibc.malloc.sanitize 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; see the file COPYING.LIB. If + not, see . */ + +#include +#include +#include // for realloc_sanitize () +#include // for realloc_sanitize () + +static void * +malloc_sanitize (size_t bytes) +{ + void *ptr = NULL; + + if (samasan_sampling_ok (bytes)) + ptr = samasan_allocate (bytes); + // Even if samasan_allocate is failed, fall back to the normal + // malloc path to guarantee memory allocation. + if (__glibc_unlikely (!ptr)) + ptr = __libc_malloc (bytes); + return ptr; +} + +static void +free_sanitize (void *mem) +{ + if (samasan_is_pointer_in_sampling_pool (mem)) + samasan_free (mem); + else + __libc_free (mem); +} + +static void * +realloc_sanitize (void *oldmem, size_t bytes) +{ + void *mem; + if (oldmem == 0) + return malloc_sanitize (bytes); + if (samasan_sampling_ok (bytes)) + { + mem = samasan_allocate (bytes); + if (mem != 0) + { + size_t oldsize; + memset (mem, 0, bytes); + if (samasan_is_pointer_in_sampling_pool (oldmem)) + oldsize = samasan_get_size (oldmem); + else + oldsize = memsize (mem2chunk (oldmem)); + if (oldsize > bytes) + memcpy (mem, oldmem, bytes); + else + memcpy (mem, oldmem, oldsize); + free_sanitize (oldmem); + return mem; + } + } + if (!samasan_is_pointer_in_sampling_pool (oldmem)) + return __libc_realloc (oldmem, bytes); + + /* Oldmem points a sampled memory block. Let's mimic __libc_realloc */ + mem = __libc_malloc (bytes); + if (mem != 0) + { + size_t oldsize = samasan_get_size (oldmem); + if (oldsize > bytes) + memcpy (mem, oldmem, bytes); + else + memcpy (mem, oldmem, oldsize); + samasan_free (oldmem); + } + return mem; +} + +static void * +calloc_sanitize (size_t num, size_t bytes) +{ + void *mem; + size_t size; + + if (__glibc_unlikely (__builtin_mul_overflow (num, bytes, &size))) + { + __set_errno (ENOMEM); + return NULL; + } + + if (samasan_sampling_ok (size)) + { + mem = samasan_allocate (size); + if (mem != 0) + { + memset (mem, 0, size); + return mem; + } + } + /* Fall through */ + return __libc_calloc (num, bytes); +} + +#define SAMPLING_ASAN_FULL_LEN 13 +#define SAMPLING_ASAN_FULL_NAME "sampling-asan" +#define SAMPLING_ASAN_SHORT_LEN 7 +#define SAMPLING_ASAN_SHORT_NAME "samasan" + +static inline bool is_tunable_sampling_asan (const char *str, size_t len) +{ + if (len == 0) + return false; + if (len == SAMPLING_ASAN_FULL_LEN && + !strncmp (str, SAMPLING_ASAN_FULL_NAME, len)) + return true; + if (len == SAMPLING_ASAN_SHORT_LEN && + !strncmp (str, SAMPLING_ASAN_SHORT_NAME, len)) + return true; + return false; +} + +static void +TUNABLE_CALLBACK (set_malloc_sanitize) (tunable_val_t *valp) +{ + const char *value = (char *) valp->strval.str; + size_t len = valp->strval.len; + if (is_tunable_sampling_asan (value, len)) + __malloc_debug_enable (MALLOC_SANITIZE_HOOK); +} + +static bool +initialize_malloc_sanitize (void) +{ + samasan_init (); + TUNABLE_GET (malloc_sanitize, const struct tunable_str_t *, TUNABLE_CALLBACK (set_malloc_sanitize)); + return __is_malloc_debug_enabled (MALLOC_SANITIZE_HOOK); +} diff --git a/malloc/tst-malloc-sanitize.c b/malloc/tst-malloc-sanitize.c new file mode 100644 index 0000000000..4d9424c770 --- /dev/null +++ b/malloc/tst-malloc-sanitize.c @@ -0,0 +1,79 @@ +/* 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sampling-asan/samasan_init.h" /* for samasan_deinit () */ + +static int +do_test (void) +{ + void *ptr; + const size_t alloc_size = 512; + char *c; + int i, ok; + + errno = 0; + + ptr = malloc (alloc_size); + TEST_VERIFY (samasan_is_pointer_in_sampling_pool (ptr)); + free (ptr); + + /* Since a memory_block size is 4096, an allocation request for + 512 * 10 bytes should be falled back to __libc_malloc. */ + ptr = malloc (alloc_size * 10); + TEST_VERIFY (!samasan_is_pointer_in_sampling_pool (ptr)); + free (ptr); + + ptr = realloc (NULL, alloc_size); + TEST_VERIFY (samasan_is_pointer_in_sampling_pool (ptr)); + ptr = realloc (ptr, alloc_size * 2); + TEST_VERIFY (samasan_is_pointer_in_sampling_pool (ptr)); + ptr = realloc (ptr, alloc_size * 10); + /* Allocation size crosses the limitation of sampling-asan. + It should be allocated by the normal malloc path. */ + TEST_VERIFY (!samasan_is_pointer_in_sampling_pool (ptr)); + free (ptr); + + ptr = calloc (1, alloc_size); + TEST_VERIFY (samasan_is_pointer_in_sampling_pool (ptr)); + c = (char *) ptr; + ok = 0; + for (i = 0; i < alloc_size; i++) + if (c[i] == 0) + ok++; + TEST_VERIFY (ok == alloc_size); + free (ptr); + + /* Since a memory_block size is 4096, an allocation request for + 512 * 10 bytes should be falled back to __libc_calloc. */ + ptr = calloc (10, alloc_size); + TEST_VERIFY (!samasan_is_pointer_in_sampling_pool (ptr)); + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" From patchwork Fri Sep 19 01:30:48 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sung-hun Kim X-Patchwork-Id: 120499 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 34D02385840B for ; Fri, 19 Sep 2025 01:35:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 34D02385840B Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=samsung.com header.i=@samsung.com header.a=rsa-sha256 header.s=mail20170921 header.b=dYapeAqF X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) by sourceware.org (Postfix) with ESMTPS id EFD623858CD9 for ; Fri, 19 Sep 2025 01:32:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EFD623858CD9 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=samsung.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org EFD623858CD9 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; cv=none; b=oMVrzffe6B9HYmOeCVzGdFNTuIIn271T3aMkXaQjlpuqhgm4dkc4u4SRL/dU2oRdeTpvuqIp7End3UOmVwCVyFx84c+FcmlH+/167RvqwYXBQILfFo3+qiseNSZ6Smfxh5yZIVfBbHL1mvRpQS+CggyRlk/135gtOyNgU4CX8iw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758245532; c=relaxed/simple; bh=IV/G/SswPtHvgWQ1S6HE7n6Oe87qAANHznqEe8RAa4U=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=GgBkDDNi2k9J8UP/hGd4qWaVbqo5u+E3ypiqnd6JNChV3xvPs9PYbbNw29GSc0OF/mnhNW9jFXsg5NMxTnujW0oT9TqKDZFSZcfeBzRL007s8o3GJKHhOdCNeJHN+EPeX0248Al8vilTeH4EwU+qxYfnjhgx/FqiKn0bC9xnlM0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EFD623858CD9 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250919013209epoutp0405e282bb1f30662ddc65f34be8570a9a~miv5qQi7v0598005980epoutp04G for ; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250919013209epoutp0405e282bb1f30662ddc65f34be8570a9a~miv5qQi7v0598005980epoutp04G DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1758245529; bh=/5cT40Fo2EjsLe+fDSDR88XwdfAvFejz3JoDqCwIjds=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dYapeAqFQNHBNs9xD5+2SxqXeMDmYW+79bhF7MRODoc9ScFk2zphSHOIHcrWa93lK v50J4IY84IM0jXaorJWv8oNOSkEYo1l8I44Emqx2MLVBcwL8/oY7dwt5mdJwSUPC+e yBz+Lq3LzagsUWpu0OABmBm/v+5/jHojRXEi+N3g= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPS id 20250919013209epcas1p4610bc5371225f57bfe522ba0e33258d4~miv5UTQu70371203712epcas1p4v; Fri, 19 Sep 2025 01:32:09 +0000 (GMT) Received: from epcas1p2.samsung.com (unknown [182.195.38.98]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cSZk06bRbz6B9mL; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from epsmtip2.samsung.com (unknown [182.195.34.31]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20250919013208epcas1p37b3f6ef6e9463e4077db1954b6a0ae0f~miv4Qlml21395413954epcas1p30; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) Received: from localhost.localdomain (unknown [10.113.111.45]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250919013208epsmtip2ea2d2f0644601d63d749b67f9e2a1442~miv4Ld5eI0490304903epsmtip2q; Fri, 19 Sep 2025 01:32:08 +0000 (GMT) From: Sung-hun Kim To: libc-alpha@sourceware.org Cc: carlos@systemhalted.org, sfoon.kim@samsung.com, sebuns@gmail.com, dongkyun.s@samsung.com, sungguk.na@samsung.com Subject: [PATCH 7/7] sampling-asan: Add a continue-on-crash option Date: Fri, 19 Sep 2025 10:30:48 +0900 Message-Id: <20250919013048.1525832-7-sfoon.kim@samsung.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20250919013048.1525832-1-sfoon.kim@samsung.com> MIME-Version: 1.0 X-CMS-MailID: 20250919013208epcas1p37b3f6ef6e9463e4077db1954b6a0ae0f X-Msg-Generator: CA CMS-TYPE: 101P cpgsPolicy: CPGSC10-361,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250919013208epcas1p37b3f6ef6e9463e4077db1954b6a0ae0f References: <20250919013048.1525832-1-sfoon.kim@samsung.com> X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, PROLO_LEO1, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org To enhance usability in production environments, I have introduced a continue-on-crash option for sampling-asan. This feature allows the application to restore the running context after encountering bypassable memory bugs, such as use-after-free and invalid- write bugs. By enabling this option, developers can ensure that applications keep their running contexts even they meet memory bugs while developers collect bug reports. This addition makes sampling-asan more practical for production environments. Signed-off-by: Sung-hun Kim --- sampling-asan/Makefile | 2 +- sampling-asan/README.md | 6 ++ sampling-asan/samasan_allocate.c | 41 +++++++++ sampling-asan/samasan_allocate.h | 5 +- sampling-asan/samasan_error.h | 5 ++ sampling-asan/samasan_fault_handler.c | 21 +++-- sampling-asan/samasan_init.c | 22 ++++- sampling-asan/samasan_init.h | 1 + sampling-asan/samasan_variable_init.def | 1 + sampling-asan/tst-bypass-block.c | 68 ++++++++++++++ sampling-asan/tst-continue-on-crash.c | 113 ++++++++++++++++++++++++ 11 files changed, 273 insertions(+), 12 deletions(-) create mode 100644 sampling-asan/tst-bypass-block.c create mode 100644 sampling-asan/tst-continue-on-crash.c diff --git a/sampling-asan/Makefile b/sampling-asan/Makefile index b0d408af30..b040a5ede9 100644 --- a/sampling-asan/Makefile +++ b/sampling-asan/Makefile @@ -30,7 +30,7 @@ tests := tst-allocate tst-sampling tst-threaded-allocation tst-init \ tst-block-sizes tst-partition-sizes tst-invalid-access \ tst-invalid-access2 tst-out-of-memory-pool \ tst-invalid-free tst-invalid-free2 tst-pause-on-fork \ - tst-chunk-pick + tst-chunk-pick tst-continue-on-crash tst-bypass-block $(objpfx)tst-threaded-allocation: $(shared-thread-library) diff --git a/sampling-asan/README.md b/sampling-asan/README.md index 7f0c08bef1..6ec217c2ad 100644 --- a/sampling-asan/README.md +++ b/sampling-asan/README.md @@ -3,6 +3,7 @@ Author: Sung-hun Kim (sfoon.kim@samsung.com, sebuns@gmail.com) Date: 2025.02.24 +Updated: 2025.09.12 ## Overview @@ -61,6 +62,11 @@ if this variable is true. - **SAMASAN_CHUNK_PICK=CENTER**: It indicates the picking tendency when allocating a memory chunk from a memory block. Sampling-asan supports LEFT, RIGHT, and CENTER picking tendencies. +- **SAMASAN_CONTINUE_ON_CRASH=true**: It indicates to restore the running context from the memory bug catched by +sampling-asan. So, the application keeps run while it gets a bug report. + +When the SAMASAN_CONTINUE_ON_CRASH option is enabled, the application continues running even after a memory bug is detected. However, the preallocated memory pool can be exhausted if bugs are reported continuously. +In such cases, additional memory bugs won't be able to be reported anymore while the application continues running. ## How to use sampling-asan diff --git a/sampling-asan/samasan_allocate.c b/sampling-asan/samasan_allocate.c index c021523e56..75857f922d 100644 --- a/sampling-asan/samasan_allocate.c +++ b/sampling-asan/samasan_allocate.c @@ -150,6 +150,22 @@ get_next_memory_pool_entry_from_pointer (void *ptr) return get_relative_memory_pool_entry_from_pointer (ptr, 1); } +/* A bypassed memory_pool entry should be removed from the free list */ +static inline void +remove_memory_pool_entry_from_free_list (struct memory_pool_entry_info *entry, + struct memory_pool_entry_info *prev) +{ + if (entry == free_list_head && entry == free_list_tail) + free_list_head = free_list_tail = NULL; + else + { + prev->list = entry->list; + entry->list = NULL; + if (entry == free_list_tail) + free_list_tail = prev; + } +} + /* 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) @@ -219,6 +235,31 @@ unprotect_range_in_block (void *start, size_t size) PROT_READ | PROT_WRITE) == 0; } +bool +bypass_block (struct memory_pool_entry_info *entry) +{ + bool ret = unprotect_range_in_block ( + get_memory_pool_block_address_by_entry_info (entry), + memory_pool_block_size); + if (!ret) + return false; + entry->is_bypassed = true; + if (entry->is_free) + { + struct memory_pool_entry_info *prev; + /* remove from free list */ + samasan_mutex_lock (&free_list_mutex); + prev = free_list_head; + while (prev->list != entry && prev != free_list_tail) + prev = prev->list; + remove_memory_pool_entry_from_free_list (entry, prev); + samasan_mutex_unlock (&free_list_mutex); + /* decrease the number of useful free blocks */ + samasan_atomic_inc (&memory_pool_entry_in_use); + } + return true; +} + /* 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. diff --git a/sampling-asan/samasan_allocate.h b/sampling-asan/samasan_allocate.h index ce611cbeba..4f5195a58f 100644 --- a/sampling-asan/samasan_allocate.h +++ b/sampling-asan/samasan_allocate.h @@ -32,13 +32,13 @@ struct memory_pool_trace { 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; + uint32_t chunk_size:30; + bool is_bypassed:1; bool is_free:1; }; @@ -55,6 +55,7 @@ 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 bypass_block (struct memory_pool_entry_info *entry); extern bool is_pointer_in_partition (void *ptr); extern bool is_out_of_chunk_access (uintptr_t address, struct memory_pool_entry_info *entry); diff --git a/sampling-asan/samasan_error.h b/sampling-asan/samasan_error.h index 270b244f10..dcf5af728d 100644 --- a/sampling-asan/samasan_error.h +++ b/sampling-asan/samasan_error.h @@ -45,4 +45,9 @@ extern void raise_fault_with_address (samasan_error_t error, extern bool samasan_error_init (void); extern void samasan_error_deinit (void); +#define is_bypassable_error(error) ( \ + error == USE_AFTER_FREE || \ + error == INVALID_ACCESS || \ + error == INVALID_WRITE) + #endif /* samasan_error.h */ diff --git a/sampling-asan/samasan_fault_handler.c b/sampling-asan/samasan_fault_handler.c index 1e88e9b07f..4e30eae707 100644 --- a/sampling-asan/samasan_fault_handler.c +++ b/sampling-asan/samasan_fault_handler.c @@ -22,6 +22,7 @@ #include "samasan_error.h" #include "samasan_common.h" #include "samasan_report.h" +#include "samasan_init.h" #include "samasan_backtrace.h" static struct sigaction default_handler; @@ -54,6 +55,7 @@ segfault_handler (int sig, siginfo_t *info, void *context) 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, @@ -61,21 +63,23 @@ segfault_handler (int sig, siginfo_t *info, void *context) /* Crash forwarding */ if (default_handler.sa_handler == SIG_DFL) + { + /* If the error is not reported by sampling-asan, forward the crash + to the default handler. */ + if (samasan_continue_on_crash == false || + !entry || + !is_bypassable_error (error) || + !bypass_block (entry)) { 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); - } + signal (SIGSEGV, SIG_IGN); + raise (SIGSEGV); } else { @@ -96,6 +100,7 @@ install_signal_handler (void) if (SAMASAN_UNLIKELY (handler_installed)) return; + sigemptyset (&action.sa_mask); action.sa_sigaction = segfault_handler; action.sa_flags = SA_SIGINFO; sigaction (SIGSEGV, &action, &default_handler); diff --git a/sampling-asan/samasan_init.c b/sampling-asan/samasan_init.c index 0136fe9a0d..f561c7ac0c 100644 --- a/sampling-asan/samasan_init.c +++ b/sampling-asan/samasan_init.c @@ -39,6 +39,7 @@ typedef enum samasan_option { SAMASAN_PARTITION_SIZE, SAMASAN_PAUSE_ON_FORK, SAMASAN_CHUNK_PICK, + SAMASAN_CONTINUE_ON_CRASH, SAMASAN_OPTIONS, } samasan_option_t; @@ -51,6 +52,7 @@ static const char *options_string[SAMASAN_OPTIONS + 1] = { "SAMASAN_PARTITION_SIZE", "SAMASAN_PAUSE_ON_FORK", "SAMASAN_CHUNK_PICK", + "SAMASAN_CONTINUE_ON_CRASH", "SAMASAN_OPTIONS", }; @@ -72,6 +74,8 @@ struct samasan_configurable { static struct samasan_configurable *samasan_configurables; bool samasan_enabled; /* sampling-asan is on-going or not */ +bool samasan_continue_on_crash; /* the program keeps running even after + a crash reported by sampling-asan */ bool samasan_is_enabled (void) @@ -129,7 +133,8 @@ samasan_set_variable_range (samasan_option_t option, uint32_t max, option == SAMASAN_CHUNK_PICK) #define OPTION_IS_CHAR(option) (option == SAMASAN_OUTPUT_PATH) #define OPTION_IS_BOOL(option) (option == SAMASAN_ENABLE || \ - option == SAMASAN_PAUSE_ON_FORK) + option == SAMASAN_PAUSE_ON_FORK || \ + option == SAMASAN_CONTINUE_ON_CRASH) static void __samasan_set_variable (samasan_option_t option, void *value) @@ -172,6 +177,8 @@ static void __samasan_set_variable samasan_get_variable (SAMASAN_PAUSE_ON_FORK).b_value #define samasan_get_chunk_pick_style() \ samasan_get_variable (SAMASAN_CHUNK_PICK).i_value +#define samasan_get_continue_on_crash() \ + samasan_get_variable (SAMASAN_CONTINUE_ON_CRASH).b_value static bool import_samasan_variable (int type, const char *val) @@ -256,6 +263,15 @@ import_samasan_variable (int type, const char *val) return false; samasan_set_variable (SAMASAN_CHUNK_PICK, pick); break; + } + case SAMASAN_CONTINUE_ON_CRASH: + { + bool var = false; + if (!strcmp (val, "on") || !strcmp (val, "enable") + || !strcmp (val, "yes") || !strcmp (val, "true")) + var = true; + samasan_set_variable (SAMASAN_CONTINUE_ON_CRASH, var); + break; } default: break; @@ -316,6 +332,8 @@ samasan_variable_init (void) DEFAULT_SAMASAN_PAUSE_ON_FORK); import_samasan_variable (SAMASAN_CHUNK_PICK, DEFAULT_SAMASAN_CHUNK_PICK); + import_samasan_variable (SAMASAN_CONTINUE_ON_CRASH, + DEFAULT_SAMASAN_CONTINUE_ON_CRASH); } void @@ -363,6 +381,8 @@ samasan_init (void) goto err_out4; if (samasan_get_pause_on_fork ()) samasan_install_fork_handler (); + if (samasan_get_continue_on_crash ()) + samasan_continue_on_crash = true; if (samasan_get_enabled ()) samasan_enabled = true; samasan_configurables = NULL; diff --git a/sampling-asan/samasan_init.h b/sampling-asan/samasan_init.h index 607131cf25..971590eeca 100644 --- a/sampling-asan/samasan_init.h +++ b/sampling-asan/samasan_init.h @@ -22,6 +22,7 @@ #include extern bool samasan_enabled; +extern bool samasan_continue_on_crash; static __inline __attribute__ ((always_inline)) bool is_samasan_enabled (void) diff --git a/sampling-asan/samasan_variable_init.def b/sampling-asan/samasan_variable_init.def index 3a5441bc15..b92feb1701 100644 --- a/sampling-asan/samasan_variable_init.def +++ b/sampling-asan/samasan_variable_init.def @@ -24,6 +24,7 @@ #define DEFAULT_SAMASAN_PARTITION_SIZE "4096" /* 1 page */ #define DEFAULT_SAMASAN_PAUSE_ON_FORK "true" #define DEFAULT_SAMASAN_CHUNK_PICK "CENTER" +#define DEFAULT_SAMASAN_CONTINUE_ON_CRASH "false" /* MAX and MIN define a range of the given configuration. */ #define MAX_SAMASAN_SAMPLING_RATE "100000" diff --git a/sampling-asan/tst-bypass-block.c b/sampling-asan/tst-bypass-block.c new file mode 100644 index 0000000000..67d2e8ae82 --- /dev/null +++ b/sampling-asan/tst-bypass-block.c @@ -0,0 +1,68 @@ +/* Test and verify a double-free case. + 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 + . */ + +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +static void +do_use_after_free_iteration (size_t iterations) +{ + size_t allocation_size = 512; + int *ptr; + + for (size_t i = 0; i < iterations; i++) + { + ptr = (int *) samasan_allocate (allocation_size); + *(ptr) = 1; + samasan_free (ptr); + *(ptr) = 0; /* raise use-after-free */ + printf ("%ld\n", i); + } + + ptr = (int *) samasan_allocate (allocation_size); + TEST_VERIFY (ptr == NULL); /* memory pool exhausted */ +} + +static int +do_test (void) +{ + const char *report_path = "/dev/null"; + + setenv ("SAMASAN_ENABLE", "true", 1/* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1/* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1/* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1/* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1/* replace */); + setenv ("SAMASAN_CONTINUE_ON_CRASH", "true", 1/* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + do_use_after_free_iteration (10); + + return 0; +} + +#include diff --git a/sampling-asan/tst-continue-on-crash.c b/sampling-asan/tst-continue-on-crash.c new file mode 100644 index 0000000000..28926d2e83 --- /dev/null +++ b/sampling-asan/tst-continue-on-crash.c @@ -0,0 +1,113 @@ +/* Test and verify an invalid-access case. + 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "samasan.h" +#include "samasan_init.h" /* for samasan_deinit */ + +static bool continue_after_crash = false; + +static bool +check_report (const char *keyword, const char *report_path) +{ + printf ("%s\n", report_path); + if (access (report_path, F_OK) == 0) + { + printf ("access success\n"); + bool check = false; + FILE *report = fopen (report_path, "r"); + if (report != NULL) + { + char buf[255], *substr; + while (fscanf (report, "%s", buf) != EOF) + { + substr = strstr (buf, keyword); + if (substr != NULL && !strncmp (substr, keyword, strlen (keyword))) + { + check = true; + break; + } + memset (buf, 0, sizeof (buf)); + } + fclose (report); + } + return (check == true); + } + printf ("access failed\n"); + return false; +} + +static void +clean_up_report (const char *report_path) +{ + if (access (report_path, F_OK) == 0) + unlink (report_path); +} + +static void +test_use_after_free_continue (void) +{ + const size_t allocation_size = 512; + int *ptr; + + ptr = (int *) samasan_allocate (allocation_size); + *(ptr) = 1; + samasan_free (ptr); + *(ptr) = 0; /* raise use-after-free */ + + continue_after_crash = true; +} + +static int +do_test (void) +{ + const char *report_path = "/tmp/continue-on-crash-report"; + + setenv ("SAMASAN_ENABLE", "true", 1 /* replace */); + setenv ("SAMASAN_MAX_ALLOC_SIZE", "4096", 1 /* replace */); + setenv ("SAMASAN_MAX_ON_GOING_ALLOCATIONS", "10", 1 /* replace */); + setenv ("SAMASAN_SAMPLING_RATE", "1.0", 1 /* replace */); + setenv ("SAMASAN_OUTPUT_PATH", report_path, 1 /* replace */); + setenv ("SAMASAN_CONTINUE_ON_CRASH", "on", 1 /* replace */); + + /* glibc already initialized sampling-asan. + So, deinitialize sampling-asan before starting the test */ + samasan_deinit (); + samasan_init (); + + test_use_after_free_continue (); + + TEST_VERIFY (check_report ("USE_AFTER_FREE", report_path) == true); + clean_up_report (report_path); + + /* We set SAMASAN_CONTINUE_ON_CRASH to "on", so the thread should + run even after a crash occurred. */ + TEST_VERIFY (continue_after_crash == true); + + return 0; +} + +#include