Quelle proc-empty-vm.c
Sprache: C
#if defined __amd64__ || defined __i386__
/*
* Copyright ( c ) 2022 Alexey Dobriyan < adobriyan @ gmail . com >
*
* Permission to use , copy , modify , and distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
/*
* Create a process without mappings by unmapping everything at once and
* holding it with ptrace ( 2 ) . See what happens to
*
* / proc / $ { pid } / maps
* / proc / $ { pid } / numa_maps
* / proc / $ { pid } / smaps
* / proc / $ { pid } / smaps_rollup
*/
#undef _GNU_SOURCE
#define _GNU_SOURCE
#undef NDEBUG
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#ifdef __amd64__
#define TEST_VSYSCALL
#endif
#if defined __amd64__
#ifndef SYS_pkey_alloc
#define SYS_pkey_alloc 330
#endif
#ifndef SYS_pkey_free
#define SYS_pkey_free 331
#endif
#elif defined __i386__
#ifndef SYS_pkey_alloc
#define SYS_pkey_alloc 381
#endif
#ifndef SYS_pkey_free
#define SYS_pkey_free 382
#endif
#else
#error "SYS_pkey_alloc"
#endif
static int g_protection_key_support;
static int protection_key_support(void )
{
long rv = syscall(SYS_pkey_alloc, 0 , 0 );
if (rv > 0 ) {
syscall(SYS_pkey_free, (int )rv);
return 1 ;
} else if (rv == -1 && errno == ENOSYS) {
return 0 ;
} else if (rv == -1 && errno == EINVAL) {
// ospke=n
return 0 ;
} else {
fprintf(stderr, "%s: error: rv %ld, errno %d\n" , __func__, rv, errno);
exit (EXIT_FAILURE);
}
}
/*
* 0 : vsyscall VMA doesn ' t exist vsyscall = none
* 1 : vsyscall VMA is - - xp vsyscall = xonly
* 2 : vsyscall VMA is r - xp vsyscall = emulate
*/
static volatile int g_vsyscall;
static const char *g_proc_pid_maps_vsyscall;
static const char *g_proc_pid_smaps_vsyscall;
static const char proc_pid_maps_vsyscall_0[] = "" ;
static const char proc_pid_maps_vsyscall_1[] =
"ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n" ;
static const char proc_pid_maps_vsyscall_2[] =
"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n" ;
static const char proc_pid_smaps_vsyscall_0[] = "" ;
static const char proc_pid_smaps_vsyscall_1[] =
"ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n"
"Size: 4 kB\n"
"KernelPageSize: 4 kB\n"
"MMUPageSize: 4 kB\n"
"Rss: 0 kB\n"
"Pss: 0 kB\n"
"Pss_Dirty: 0 kB\n"
"Shared_Clean: 0 kB\n"
"Shared_Dirty: 0 kB\n"
"Private_Clean: 0 kB\n"
"Private_Dirty: 0 kB\n"
"Referenced: 0 kB\n"
"Anonymous: 0 kB\n"
"KSM: 0 kB\n"
"LazyFree: 0 kB\n"
"AnonHugePages: 0 kB\n"
"ShmemPmdMapped: 0 kB\n"
"FilePmdMapped: 0 kB\n"
"Shared_Hugetlb: 0 kB\n"
"Private_Hugetlb: 0 kB\n"
"Swap: 0 kB\n"
"SwapPss: 0 kB\n"
"Locked: 0 kB\n"
"THPeligible: 0\n"
;
static const char proc_pid_smaps_vsyscall_2[] =
"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"
"Size: 4 kB\n"
"KernelPageSize: 4 kB\n"
"MMUPageSize: 4 kB\n"
"Rss: 0 kB\n"
"Pss: 0 kB\n"
"Pss_Dirty: 0 kB\n"
"Shared_Clean: 0 kB\n"
"Shared_Dirty: 0 kB\n"
"Private_Clean: 0 kB\n"
"Private_Dirty: 0 kB\n"
"Referenced: 0 kB\n"
"Anonymous: 0 kB\n"
"KSM: 0 kB\n"
"LazyFree: 0 kB\n"
"AnonHugePages: 0 kB\n"
"ShmemPmdMapped: 0 kB\n"
"FilePmdMapped: 0 kB\n"
"Shared_Hugetlb: 0 kB\n"
"Private_Hugetlb: 0 kB\n"
"Swap: 0 kB\n"
"SwapPss: 0 kB\n"
"Locked: 0 kB\n"
"THPeligible: 0\n"
;
static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
{
_exit (EXIT_FAILURE);
}
#ifdef TEST_VSYSCALL
static void sigaction_SIGSEGV_vsyscall(int _, siginfo_t *__, void *___)
{
_exit (g_vsyscall);
}
/*
* vsyscall page can ' t be unmapped , probe it directly .
*/
static void vsyscall(void )
{
pid_t pid;
int wstatus;
pid = fork();
if (pid < 0 ) {
fprintf(stderr, "fork, errno %d\n" , errno);
exit (1 );
}
if (pid == 0 ) {
setrlimit(RLIMIT_CORE, &(struct rlimit){});
/* Hide "segfault at ffffffffff600000" messages. */
struct sigaction act = {};
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = sigaction_SIGSEGV_vsyscall;
sigaction(SIGSEGV, &act, NULL);
g_vsyscall = 0 ;
/* gettimeofday(NULL, NULL); */
uint64_t rax = 0 xffffffffff600000;
asm volatile (
"call *%[rax]"
: [rax] "+a" (rax)
: "D" (NULL), "S" (NULL)
: "rcx" , "r11"
);
g_vsyscall = 1 ;
*(volatile int *)0 xffffffffff600000UL;
g_vsyscall = 2 ;
exit (g_vsyscall);
}
waitpid(pid, &wstatus, 0 );
if (WIFEXITED(wstatus)) {
g_vsyscall = WEXITSTATUS(wstatus);
} else {
fprintf(stderr, "error: vsyscall wstatus %08x\n" , wstatus);
exit (1 );
}
}
#endif
static int test_proc_pid_maps(pid_t pid)
{
char buf[4096 ];
snprintf(buf, sizeof (buf), "/proc/%u/maps" , pid);
int fd = open(buf, O_RDONLY);
if (fd == -1 ) {
perror("open /proc/${pid}/maps" );
return EXIT_FAILURE;
} else {
ssize_t rv = read(fd, buf, sizeof (buf));
close(fd);
if (g_vsyscall == 0 ) {
assert(rv == 0 );
} else {
size_t len = strlen(g_proc_pid_maps_vsyscall);
assert(rv == len);
assert(memcmp(buf, g_proc_pid_maps_vsyscall, len) == 0 );
}
return EXIT_SUCCESS;
}
}
static int test_proc_pid_numa_maps(pid_t pid)
{
char buf[4096 ];
snprintf(buf, sizeof (buf), "/proc/%u/numa_maps" , pid);
int fd = open(buf, O_RDONLY);
if (fd == -1 ) {
if (errno == ENOENT) {
/*
* / proc / $ { pid } / numa_maps is under CONFIG_NUMA ,
* it doesn ' t necessarily exist .
*/
return EXIT_SUCCESS;
}
perror("open /proc/${pid}/numa_maps" );
return EXIT_FAILURE;
} else {
ssize_t rv = read(fd, buf, sizeof (buf));
close(fd);
assert(rv == 0 );
return EXIT_SUCCESS;
}
}
static int test_proc_pid_smaps(pid_t pid)
{
char buf[4096 ];
snprintf(buf, sizeof (buf), "/proc/%u/smaps" , pid);
int fd = open(buf, O_RDONLY);
if (fd == -1 ) {
if (errno == ENOENT) {
/*
* / proc / $ { pid } / smaps is under CONFIG_PROC_PAGE_MONITOR ,
* it doesn ' t necessarily exist .
*/
return EXIT_SUCCESS;
}
perror("open /proc/${pid}/smaps" );
return EXIT_FAILURE;
}
ssize_t rv = read(fd, buf, sizeof (buf));
close(fd);
assert(0 <= rv);
assert(rv <= sizeof (buf));
if (g_vsyscall == 0 ) {
assert(rv == 0 );
} else {
size_t len = strlen(g_proc_pid_smaps_vsyscall);
assert(rv > len);
assert(memcmp(buf, g_proc_pid_smaps_vsyscall, len) == 0 );
if (g_protection_key_support) {
#define PROTECTION_KEY "ProtectionKey: 0\n"
assert(memmem(buf, rv, PROTECTION_KEY, strlen(PROTECTION_KEY)));
}
}
return EXIT_SUCCESS;
}
static const char g_smaps_rollup[] =
"00000000-00000000 ---p 00000000 00:00 0 [rollup]\n"
"Rss: 0 kB\n"
"Pss: 0 kB\n"
"Pss_Dirty: 0 kB\n"
"Pss_Anon: 0 kB\n"
"Pss_File: 0 kB\n"
"Pss_Shmem: 0 kB\n"
"Shared_Clean: 0 kB\n"
"Shared_Dirty: 0 kB\n"
"Private_Clean: 0 kB\n"
"Private_Dirty: 0 kB\n"
"Referenced: 0 kB\n"
"Anonymous: 0 kB\n"
"KSM: 0 kB\n"
"LazyFree: 0 kB\n"
"AnonHugePages: 0 kB\n"
"ShmemPmdMapped: 0 kB\n"
"FilePmdMapped: 0 kB\n"
"Shared_Hugetlb: 0 kB\n"
"Private_Hugetlb: 0 kB\n"
"Swap: 0 kB\n"
"SwapPss: 0 kB\n"
"Locked: 0 kB\n"
;
static int test_proc_pid_smaps_rollup(pid_t pid)
{
char buf[4096 ];
snprintf(buf, sizeof (buf), "/proc/%u/smaps_rollup" , pid);
int fd = open(buf, O_RDONLY);
if (fd == -1 ) {
if (errno == ENOENT) {
/*
* / proc / $ { pid } / smaps_rollup is under CONFIG_PROC_PAGE_MONITOR ,
* it doesn ' t necessarily exist .
*/
return EXIT_SUCCESS;
}
perror("open /proc/${pid}/smaps_rollup" );
return EXIT_FAILURE;
} else {
ssize_t rv = read(fd, buf, sizeof (buf));
close(fd);
assert(rv == sizeof (g_smaps_rollup) - 1 );
assert(memcmp(buf, g_smaps_rollup, sizeof (g_smaps_rollup) - 1 ) == 0 );
return EXIT_SUCCESS;
}
}
static const char *parse_u64(const char *p, const char *const end, uint64_t *rv)
{
*rv = 0 ;
for (; p != end; p += 1 ) {
if ('0' <= *p && *p <= '9' ) {
assert(!__builtin_mul_overflow(*rv, 10 , rv));
assert(!__builtin_add_overflow(*rv, *p - '0' , rv));
} else {
break ;
}
}
assert(p != end);
return p;
}
/*
* There seems to be 2 types of valid output :
* " 0 A A B 0 0 0 \ n " for dynamic exeuctables ,
* " 0 0 0 B 0 0 0 \ n " for static executables .
*/
static int test_proc_pid_statm(pid_t pid)
{
char buf[4096 ];
snprintf(buf, sizeof (buf), "/proc/%u/statm" , pid);
int fd = open(buf, O_RDONLY);
if (fd == -1 ) {
perror("open /proc/${pid}/statm" );
return EXIT_FAILURE;
}
ssize_t rv = read(fd, buf, sizeof (buf));
close(fd);
assert(rv >= 0 );
assert(rv <= sizeof (buf));
const char *p = buf;
const char *const end = p + rv;
/* size */
assert(p != end && *p++ == '0' );
assert(p != end && *p++ == ' ' );
uint64_t resident;
p = parse_u64(p, end, &resident);
assert(p != end && *p++ == ' ' );
uint64_t shared;
p = parse_u64(p, end, &shared);
assert(p != end && *p++ == ' ' );
uint64_t text;
p = parse_u64(p, end, &text);
assert(p != end && *p++ == ' ' );
assert(p != end && *p++ == '0' );
assert(p != end && *p++ == ' ' );
/* data */
assert(p != end && *p++ == '0' );
assert(p != end && *p++ == ' ' );
assert(p != end && *p++ == '0' );
assert(p != end && *p++ == '\n' );
assert(p == end);
/*
* " text " is " mm - > end_code - mm - > start_code " at execve ( 2 ) time .
* munmap ( ) doesn ' t change it . It can be anything ( just link
* statically ) . It can ' t be 0 because executing to this point
* implies at least 1 page of code .
*/
assert(text > 0 );
/*
* These two are always equal . Always 0 for statically linked
* executables and sometimes 0 for dynamically linked executables .
* There is no way to tell one from another without parsing ELF
* which is too much for this test .
*/
assert(resident == shared);
return EXIT_SUCCESS;
}
int main(void )
{
int rv = EXIT_SUCCESS;
#ifdef TEST_VSYSCALL
vsyscall();
#endif
switch (g_vsyscall) {
case 0 :
g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_0;
g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_0;
break ;
case 1 :
g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_1;
g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_1;
break ;
case 2 :
g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_2;
g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_2;
break ;
default :
abort();
}
g_protection_key_support = protection_key_support();
pid_t pid = fork();
if (pid == -1 ) {
perror("fork" );
return EXIT_FAILURE;
} else if (pid == 0 ) {
rv = ptrace(PTRACE_TRACEME, 0 , NULL, NULL);
if (rv != 0 ) {
if (errno == EPERM) {
fprintf(stderr,
"Did you know? ptrace(PTRACE_TRACEME) doesn't work under strace.\n"
);
kill(getppid(), SIGTERM);
return EXIT_FAILURE;
}
perror("ptrace PTRACE_TRACEME" );
return EXIT_FAILURE;
}
/*
* Hide " segfault at . . . " messages . Signal handler won ' t run .
*/
struct sigaction act = {};
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = sigaction_SIGSEGV;
sigaction(SIGSEGV, &act, NULL);
#ifdef __amd64__
munmap(NULL, ((size_t)1 << 47 ) - 4096 );
#elif defined __i386__
{
size_t len;
for (len = -4096 ;; len -= 4096 ) {
munmap(NULL, len);
}
}
#else
#error "implement 'unmap everything'"
#endif
return EXIT_FAILURE;
} else {
/*
* TODO find reliable way to signal parent that munmap ( 2 ) completed .
* Child can ' t do it directly because it effectively doesn ' t exist
* anymore . Looking at child ' s VM files isn ' t 100 % reliable either :
* due to a bug they may not become empty or empty - like .
*/
sleep(1 );
if (rv == EXIT_SUCCESS) {
rv = test_proc_pid_maps(pid);
}
if (rv == EXIT_SUCCESS) {
rv = test_proc_pid_numa_maps(pid);
}
if (rv == EXIT_SUCCESS) {
rv = test_proc_pid_smaps(pid);
}
if (rv == EXIT_SUCCESS) {
rv = test_proc_pid_smaps_rollup(pid);
}
if (rv == EXIT_SUCCESS) {
rv = test_proc_pid_statm(pid);
}
/* Cut the rope. */
int wstatus;
waitpid(pid, &wstatus, 0 );
assert(WIFSTOPPED(wstatus));
assert(WSTOPSIG(wstatus) == SIGSEGV);
}
return rv;
}
#else
int main(void )
{
return 4 ;
}
#endif
Messung V0.5 in Prozent C=85 H=99 G=92
¤ Dauer der Verarbeitung: 0.13 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