Quelle tm-signal-pagefault.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2020 , Gustavo Luiz Duarte , IBM Corp .
*
* This test starts a transaction and triggers a signal , forcing a pagefault to
* happen when the kernel signal handling code touches the user signal stack .
*
* In order to avoid pre - faulting the signal stack memory and to force the
* pagefault to happen precisely in the kernel signal handling code , the
* pagefault handling is done in userspace using the userfaultfd facility .
*
* Further pagefaults are triggered by crafting the signal handler ' s ucontext
* to point to additional memory regions managed by the userfaultfd , so using
* the same mechanism used to avoid pre - faulting the signal stack memory .
*
* On failure ( bug is present ) kernel crashes or never returns control back to
* userspace . If bug is not present , tests completes almost immediately .
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include "tm.h"
#define UF_MEM_SIZE 655360 /* 10 x 64k pages */
/* Memory handled by userfaultfd */
static char *uf_mem;
static size_t uf_mem_offset = 0 ;
/*
* Data that will be copied into the faulting pages ( instead of zero - filled
* pages ) . This is used to make the test more reliable and avoid segfaulting
* when we return from the signal handler . Since we are making the signal
* handler ' s ucontext point to newly allocated memory , when that memory is
* paged - in it will contain the expected content .
*/
static char backing_mem[UF_MEM_SIZE];
static size_t pagesize;
/*
* Return a chunk of at least ' size ' bytes of memory that will be handled by
* userfaultfd . If ' backing_data ' is not NULL , its content will be save to
* ' backing_mem ' and then copied into the faulting pages when the page fault
* is handled .
*/
void *get_uf_mem(size_t size, void *backing_data)
{
void *ret;
if (uf_mem_offset + size > UF_MEM_SIZE) {
fprintf(stderr, "Requesting more uf_mem than expected!\n" );
exit (EXIT_FAILURE);
}
ret = &uf_mem[uf_mem_offset];
/* Save the data that will be copied into the faulting page */
if (backing_data != NULL)
memcpy(&backing_mem[uf_mem_offset], backing_data, size);
/* Reserve the requested amount of uf_mem */
uf_mem_offset += size;
/* Keep uf_mem_offset aligned to the page size (round up) */
uf_mem_offset = (uf_mem_offset + pagesize - 1 ) & ~(pagesize - 1 );
return ret;
}
void *fault_handler_thread(void *arg)
{
struct uffd_msg msg; /* Data read from userfaultfd */
long uffd; /* userfaultfd file descriptor */
struct uffdio_copy uffdio_copy;
struct pollfd pollfd;
ssize_t nread, offset;
uffd = (long ) arg;
for (;;) {
pollfd.fd = uffd;
pollfd.events = POLLIN;
if (poll(&pollfd, 1 , -1 ) == -1 ) {
perror("poll() failed" );
exit (EXIT_FAILURE);
}
nread = read(uffd, &msg, sizeof (msg));
if (nread == 0 ) {
fprintf(stderr, "read(): EOF on userfaultfd\n" );
exit (EXIT_FAILURE);
}
if (nread == -1 ) {
perror("read() failed" );
exit (EXIT_FAILURE);
}
/* We expect only one kind of event */
if (msg.event != UFFD_EVENT_PAGEFAULT) {
fprintf(stderr, "Unexpected event on userfaultfd\n" );
exit (EXIT_FAILURE);
}
/*
* We need to handle page faults in units of pages ( ! ) .
* So , round faulting address down to page boundary .
*/
uffdio_copy.dst = msg.arg.pagefault.address & ~(pagesize-1 );
offset = (char *) uffdio_copy.dst - uf_mem;
uffdio_copy.src = (unsigned long ) &backing_mem[offset];
uffdio_copy.len = pagesize;
uffdio_copy.mode = 0 ;
uffdio_copy.copy = 0 ;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1 ) {
perror("ioctl-UFFDIO_COPY failed" );
exit (EXIT_FAILURE);
}
}
}
void setup_uf_mem(void )
{
long uffd; /* userfaultfd file descriptor */
pthread_t thr;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int ret;
pagesize = sysconf(_SC_PAGE_SIZE);
/* Create and enable userfaultfd object */
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1 ) {
perror("userfaultfd() failed" );
exit (EXIT_FAILURE);
}
uffdio_api.api = UFFD_API;
uffdio_api.features = 0 ;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1 ) {
perror("ioctl-UFFDIO_API failed" );
exit (EXIT_FAILURE);
}
/*
* Create a private anonymous mapping . The memory will be demand - zero
* paged , that is , not yet allocated . When we actually touch the memory
* the related page will be allocated via the userfaultfd mechanism .
*/
uf_mem = mmap(NULL, UF_MEM_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 );
if (uf_mem == MAP_FAILED) {
perror("mmap() failed" );
exit (EXIT_FAILURE);
}
/*
* Register the memory range of the mapping we ' ve just mapped to be
* handled by the userfaultfd object . In ' mode ' we request to track
* missing pages ( i . e . pages that have not yet been faulted - in ) .
*/
uffdio_register.range.start = (unsigned long ) uf_mem;
uffdio_register.range.len = UF_MEM_SIZE;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1 ) {
perror("ioctl-UFFDIO_REGISTER" );
exit (EXIT_FAILURE);
}
/* Create a thread that will process the userfaultfd events */
ret = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd);
if (ret != 0 ) {
fprintf(stderr, "pthread_create(): Error. Returned %d\n" , ret);
exit (EXIT_FAILURE);
}
}
/*
* Assumption : the signal was delivered while userspace was in transactional or
* suspended state , i . e . uc - > uc_link ! = NULL .
*/
void signal_handler(int signo, siginfo_t *si, void *uc)
{
ucontext_t *ucp = uc;
/* Skip 'trap' after returning, otherwise we get a SIGTRAP again */
ucp->uc_link->uc_mcontext.regs->nip += 4 ;
ucp->uc_mcontext.v_regs =
get_uf_mem(sizeof (elf_vrreg_t), ucp->uc_mcontext.v_regs);
ucp->uc_link->uc_mcontext.v_regs =
get_uf_mem(sizeof (elf_vrreg_t), ucp->uc_link->uc_mcontext.v_regs);
ucp->uc_link = get_uf_mem(sizeof (ucontext_t), ucp->uc_link);
}
bool have_userfaultfd(void )
{
long rc;
errno = 0 ;
rc = syscall(__NR_userfaultfd, -1 );
return rc == 0 || errno != ENOSYS;
}
int tm_signal_pagefault(void )
{
struct sigaction sa;
stack_t ss;
SKIP_IF(!have_htm());
SKIP_IF(htm_is_synthetic());
SKIP_IF(!have_userfaultfd());
setup_uf_mem();
/*
* Set an alternative stack that will generate a page fault when the
* signal is raised . The page fault will be treated via userfaultfd ,
* i . e . via fault_handler_thread .
*/
ss.ss_sp = get_uf_mem(SIGSTKSZ, NULL);
ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0 ;
if (sigaltstack(&ss, NULL) == -1 ) {
perror("sigaltstack() failed" );
exit (EXIT_FAILURE);
}
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
sa.sa_sigaction = signal_handler;
if (sigaction(SIGTRAP, &sa, NULL) == -1 ) {
perror("sigaction() failed" );
exit (EXIT_FAILURE);
}
/* Trigger a SIGTRAP in transactional state */
asm __volatile__(
"tbegin.;"
"beq 1f;"
"trap;"
"1: ;"
: : : "memory" );
/* Trigger a SIGTRAP in suspended state */
asm __volatile__(
"tbegin.;"
"beq 1f;"
"tsuspend.;"
"trap;"
"tresume.;"
"1: ;"
: : : "memory" );
return EXIT_SUCCESS;
}
int main(int argc, char **argv)
{
/*
* Depending on kernel config , the TM Bad Thing might not result in a
* crash , instead the kernel never returns control back to userspace , so
* set a tight timeout . If the test passes it completes almost
* immediately .
*/
test_harness_set_timeout(2 );
return test_harness(tm_signal_pagefault, "tm_signal_pagefault" );
}
Messung V0.5 in Prozent C=92 H=93 G=92
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-10-12)
¤
*© Formatika GbR, Deutschland
2026-10-11
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1128594
#Domains=1947682