support: add check_mem_access function

Add check_mem_access(addr) function to check if memory at addr can
be written or read returning false if memory is not accessible.

This function changes signal handler for SIGSEGV and SIGBUS signals
when it is called first, and it is not thread-safe.

Co-authored-by: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
This commit is contained in:
Yury Khrustalev 2025-09-09 15:15:29 +01:00
parent 041151f439
commit 9be489d778
5 changed files with 102 additions and 48 deletions

View File

@ -12,7 +12,7 @@ extern void ____longjmp_chk (__jmp_buf __env, int __val)
__attribute__ ((__noreturn__)) attribute_hidden;
extern void __longjmp_chk (sigjmp_buf env, int val)
__attribute__ ((noreturn)) attribute_hidden;
__attribute__ ((noreturn));
/* The redirection in the installed header does not work with
libc_hidden_proto. */
#define longjmp __longjmp_chk

View File

@ -26,54 +26,22 @@
#include <support/xsignal.h>
#include <support/xthread.h>
#include <support/xunistd.h>
#include <support/check_mem_access.h>
#include <sys/mman.h>
#include <stdlib.h>
static long int pagesz;
/* To check if the guard region is inaccessible, the thread tries read/writes
on it and checks if a SIGSEGV is generated. */
static volatile sig_atomic_t signal_jump_set;
static sigjmp_buf signal_jmp_buf;
static void
sigsegv_handler (int sig)
{
if (signal_jump_set == 0)
return;
siglongjmp (signal_jmp_buf, sig);
}
static bool
try_access_buf (char *ptr, bool write)
{
signal_jump_set = true;
bool failed = sigsetjmp (signal_jmp_buf, 0) != 0;
if (!failed)
{
if (write)
*(volatile char *)(ptr) = 'x';
else
*(volatile char *)(ptr);
}
signal_jump_set = false;
return !failed;
}
static bool
try_read_buf (char *ptr)
{
return try_access_buf (ptr, false);
return check_mem_access (ptr, false);
}
static bool
try_write_buf (char *ptr)
{
return try_access_buf (ptr, true);
return check_mem_access (ptr, true);
}
static bool
@ -332,18 +300,6 @@ do_test (void)
{
pagesz = sysconf (_SC_PAGESIZE);
{
struct sigaction sa = {
.sa_handler = sigsegv_handler,
.sa_flags = SA_NODEFER,
};
sigemptyset (&sa.sa_mask);
xsigaction (SIGSEGV, &sa, NULL);
/* Some system generates SIGBUS accessing the guard area when it is
setup with madvise. */
xsigaction (SIGBUS, &sa, NULL);
}
static const struct {
const char *descr;
void (*test)(void);

View File

@ -66,6 +66,7 @@ libsupport-routines = \
support_format_netent \
support_fuse \
support_isolate_in_subprocess \
support_mem_access \
support_mutex_pi_monotonic \
support_need_proc \
support_open_and_compare_file_bytes \

View File

@ -0,0 +1,36 @@
/* Test verification functions for memory access checks.
Copyright (C) 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
<https://www.gnu.org/licenses/>. */
#ifndef SUPPORT_CHECK_MEM_ACCESS_H
#define SUPPORT_CHECK_MEM_ACCESS_H
#include <sys/cdefs.h>
__BEGIN_DECLS
/* To check if the a memory region is inaccessible, this function tries
read / write on the provided address ADDR and checks if a SIGSEGV is
generated. This function is not thread-safe and it changes signal
handlers for SIGSEGV and SIGBUS.
If WRITE is true, only the write operation is checked, otherwise only
the read operation is checked. */
bool check_mem_access (const void *addr, bool write);
__END_DECLS
#endif // SUPPORT_CHECK_MEM_ACCESS_H

View File

@ -0,0 +1,61 @@
/* Implementation of the test verification functions for memory access
checks.
Copyright (C) 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
<https://www.gnu.org/licenses/>. */
#include <setjmp.h>
#include <signal.h>
#include <support/xsignal.h>
static sigjmp_buf sigsegv_jmp_buf;
static void
__sigsegv_handler (int signum)
{
siglongjmp (sigsegv_jmp_buf, signum);
}
bool check_mem_access (const void *addr, bool write)
{
/* This is obviously not thread-safe. */
static bool handler_set_up;
if (!handler_set_up)
{
struct sigaction sa = {
.sa_handler = __sigsegv_handler,
.sa_flags = SA_NODEFER,
};
sigemptyset (&sa.sa_mask);
xsigaction (SIGSEGV, &sa, NULL);
/* Some system generates SIGBUS accessing the guard area when it is
setup with madvise. */
xsigaction (SIGBUS, &sa, NULL);
handler_set_up = true;
}
int r = sigsetjmp (sigsegv_jmp_buf, 0);
if (r == 0)
{
if (write)
*(volatile char *)addr = 'x';
else
*(volatile char *)addr;
return true;
}
if (r == SIGSEGV || r == SIGBUS)
return false;
return true;
}