// SPDX-License-Identifier: GPL-2.0+
/*
* Ptrace test for Memory Protection Key registers
*
* Copyright ( C ) 2015 Anshuman Khandual , IBM Corporation .
* Copyright ( C ) 2018 IBM Corporation .
*/
#include <limits.h>
#include <linux/kernel.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <unistd.h>
#include "ptrace.h"
#include "child.h"
#include "pkeys.h"
#define CORE_FILE_LIMIT (5 * 1024 * 1024 ) /* 5 MB should be enough */
static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern" ;
static const char user_write[] = "[User Write (Running)]" ;
static const char core_read_running[] = "[Core Read (Running)]" ;
/* Information shared between the parent and the child. */
struct shared_info {
struct child_sync child_sync;
/* AMR value the parent expects to read in the core file. */
unsigned long amr;
/* IAMR value the parent expects to read in the core file. */
unsigned long iamr;
/* UAMOR value the parent expects to read in the core file. */
unsigned long uamor;
/* When the child crashed. */
time_t core_time;
};
static int increase_core_file_limit(void )
{
struct rlimit rlim;
int ret;
ret = getrlimit(RLIMIT_CORE, &rlim);
FAIL_IF(ret);
if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
rlim.rlim_cur = CORE_FILE_LIMIT;
if (rlim.rlim_max != RLIM_INFINITY &&
rlim.rlim_max < CORE_FILE_LIMIT)
rlim.rlim_max = CORE_FILE_LIMIT;
ret = setrlimit(RLIMIT_CORE, &rlim);
FAIL_IF(ret);
}
ret = getrlimit(RLIMIT_FSIZE, &rlim);
FAIL_IF(ret);
if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
rlim.rlim_cur = CORE_FILE_LIMIT;
if (rlim.rlim_max != RLIM_INFINITY &&
rlim.rlim_max < CORE_FILE_LIMIT)
rlim.rlim_max = CORE_FILE_LIMIT;
ret = setrlimit(RLIMIT_FSIZE, &rlim);
FAIL_IF(ret);
}
return TEST_PASS;
}
static int child(struct shared_info *info)
{
bool disable_execute = true ;
int pkey1, pkey2, pkey3;
int *ptr, ret;
/* Wait until parent fills out the initial register values. */
ret = wait_parent(&info->child_sync);
if (ret)
return ret;
ret = increase_core_file_limit();
FAIL_IF(ret);
/* Get some pkeys so that we can change their bits in the AMR. */
pkey1 = sys_pkey_alloc(0 , PKEY_DISABLE_EXECUTE);
if (pkey1 < 0 ) {
pkey1 = sys_pkey_alloc(0 , PKEY_UNRESTRICTED);
FAIL_IF(pkey1 < 0 );
disable_execute = false ;
}
pkey2 = sys_pkey_alloc(0 , PKEY_UNRESTRICTED);
FAIL_IF(pkey2 < 0 );
pkey3 = sys_pkey_alloc(0 , PKEY_UNRESTRICTED);
FAIL_IF(pkey3 < 0 );
info->amr |= 3 ul << pkeyshift(pkey1) | 2 ul << pkeyshift(pkey2);
if (disable_execute)
info->iamr |= 1 ul << pkeyshift(pkey1);
else
info->iamr &= ~(1 ul << pkeyshift(pkey1));
info->iamr &= ~(1 ul << pkeyshift(pkey2) | 1 ul << pkeyshift(pkey3));
info->uamor |= 3 ul << pkeyshift(pkey1) | 3 ul << pkeyshift(pkey2);
printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n" ,
user_write, info->amr, pkey1, pkey2, pkey3);
set_amr(info->amr);
/*
* We won ' t use pkey3 . This tests whether the kernel restores the UAMOR
* permissions after a key is freed .
*/
sys_pkey_free(pkey3);
info->core_time = time(NULL);
/* Crash. */
ptr = 0 ;
*ptr = 1 ;
/* Shouldn't get here. */
FAIL_IF(true );
return TEST_FAIL;
}
/* Return file size if filename exists and pass sanity check, or zero if not. */
static off_t try_core_file(const char *filename, struct shared_info *info,
pid_t pid)
{
struct stat buf;
int ret;
ret = stat(filename, &buf);
if (ret == -1 )
return TEST_FAIL;
/* Make sure we're not using a stale core file. */
return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
}
static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
{
return (void *) nhdr + sizeof (*nhdr) +
__ALIGN_KERNEL(nhdr->n_namesz, 4 ) +
__ALIGN_KERNEL(nhdr->n_descsz, 4 );
}
static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
off_t core_size)
{
unsigned long *regs;
Elf64_Phdr *phdr;
Elf64_Nhdr *nhdr;
size_t phdr_size;
void *p = ehdr, *note;
int ret;
ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
FAIL_IF(ret);
FAIL_IF(ehdr->e_type != ET_CORE);
FAIL_IF(ehdr->e_machine != EM_PPC64);
FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0 );
/*
* e_phnum is at most 65535 so calculating the size of the
* program header cannot overflow .
*/
phdr_size = sizeof (*phdr) * ehdr->e_phnum;
/* Sanity check the program header table location. */
FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
/* Find the PT_NOTE segment. */
for (phdr = p + ehdr->e_phoff;
(void *) phdr < p + ehdr->e_phoff + phdr_size;
phdr += ehdr->e_phentsize)
if (phdr->p_type == PT_NOTE)
break ;
FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
/* Find the NT_PPC_PKEY note. */
for (nhdr = p + phdr->p_offset;
(void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
nhdr = next_note(nhdr))
if (nhdr->n_type == NT_PPC_PKEY)
break ;
FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
FAIL_IF(nhdr->n_descsz == 0 );
p = nhdr;
note = p + sizeof (*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4 );
regs = (unsigned long *) note;
printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n" ,
core_read_running, regs[0 ], regs[1 ], regs[2 ]);
FAIL_IF(regs[0 ] != info->amr);
FAIL_IF(regs[1 ] != info->iamr);
FAIL_IF(regs[2 ] != info->uamor);
return TEST_PASS;
}
static int parent(struct shared_info *info, pid_t pid)
{
char *filenames, *filename[3 ];
int fd, i, ret, status;
unsigned long regs[3 ];
off_t core_size;
void *core;
/*
* Get the initial values for AMR , IAMR and UAMOR and communicate them
* to the child .
*/
ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3 );
PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported" );
PARENT_FAIL_IF(ret, &info->child_sync);
info->amr = regs[0 ];
info->iamr = regs[1 ];
info->uamor = regs[2 ];
/* Wake up child so that it can set itself up. */
ret = prod_child(&info->child_sync);
PARENT_FAIL_IF(ret, &info->child_sync);
ret = wait(&status);
if (ret != pid) {
printf("Child's exit status not captured\n" );
return TEST_FAIL;
} else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
printf("Child didn't dump core\n" );
return TEST_FAIL;
}
/* Construct array of core file names to try. */
filename[0 ] = filenames = malloc(PATH_MAX);
if (!filenames) {
perror("Error allocating memory" );
return TEST_FAIL;
}
ret = snprintf(filename[0 ], PATH_MAX, "core-pkey.%d" , pid);
if (ret < 0 || ret >= PATH_MAX) {
ret = TEST_FAIL;
goto out;
}
filename[1 ] = filename[0 ] + ret + 1 ;
ret = snprintf(filename[1 ], PATH_MAX - ret - 1 , "core.%d" , pid);
if (ret < 0 || ret >= PATH_MAX - ret - 1 ) {
ret = TEST_FAIL;
goto out;
}
filename[2 ] = "core" ;
for (i = 0 ; i < 3 ; i++) {
core_size = try_core_file(filename[i], info, pid);
if (core_size != TEST_FAIL)
break ;
}
if (i == 3 ) {
printf("Couldn't find core file\n" );
ret = TEST_FAIL;
goto out;
}
fd = open(filename[i], O_RDONLY);
if (fd == -1 ) {
perror("Error opening core file" );
ret = TEST_FAIL;
goto out;
}
core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0 );
if (core == (void *) -1 ) {
perror("Error mmapping core file" );
ret = TEST_FAIL;
goto out;
}
ret = check_core_file(info, core, core_size);
munmap(core, core_size);
close(fd);
unlink(filename[i]);
out:
free(filenames);
return ret;
}
static int write_core_pattern(const char *core_pattern)
{
int err;
err = write_file(core_pattern_file, core_pattern, strlen(core_pattern));
if (err) {
SKIP_IF_MSG(err == -EPERM, "Try with root privileges" );
perror("Error writing to core_pattern file" );
return TEST_FAIL;
}
return TEST_PASS;
}
static int setup_core_pattern(char **core_pattern_, bool *changed_)
{
char *core_pattern;
size_t len;
int ret;
core_pattern = malloc(PATH_MAX);
if (!core_pattern) {
perror("Error allocating memory" );
return TEST_FAIL;
}
ret = read_file(core_pattern_file, core_pattern, PATH_MAX - 1 , &len);
if (ret) {
perror("Error reading core_pattern file" );
ret = TEST_FAIL;
goto out;
}
core_pattern[len] = '\0' ;
/* Check whether we can predict the name of the core file. */
if (!strcmp(core_pattern, "core" ) || !strcmp(core_pattern, "core.%p" ))
*changed_ = false ;
else {
ret = write_core_pattern("core-pkey.%p" );
if (ret)
goto out;
*changed_ = true ;
}
*core_pattern_ = core_pattern;
ret = TEST_PASS;
out:
if (ret)
free(core_pattern);
return ret;
}
static int core_pkey(void )
{
char *core_pattern;
bool changed_core_pattern;
struct shared_info *info;
int shm_id;
int ret;
pid_t pid;
ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
if (ret)
return ret;
shm_id = shmget(IPC_PRIVATE, sizeof (*info), 0777 | IPC_CREAT);
info = shmat(shm_id, NULL, 0 );
ret = init_child_sync(&info->child_sync);
if (ret)
return ret;
pid = fork();
if (pid < 0 ) {
perror("fork() failed" );
ret = TEST_FAIL;
} else if (pid == 0 )
ret = child(info);
else
ret = parent(info, pid);
shmdt(info);
if (pid) {
destroy_child_sync(&info->child_sync);
shmctl(shm_id, IPC_RMID, NULL);
if (changed_core_pattern)
write_core_pattern(core_pattern);
}
free(core_pattern);
return ret;
}
int main(int argc, char *argv[])
{
return test_harness(core_pkey, "core_pkey" );
}
Messung V0.5 in Prozent C=95 H=94 G=94
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-10-12)
¤
*© Formatika GbR, Deutschland