Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


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 = 0xffffffffff600000;
  asm volatile (
   "call *%[rax]"
   : [rax] "+a" (rax)
   : "D" (NULL), "S" (NULL)
   : "rcx""r11"
  );

  g_vsyscall = 1;
  *(volatile int *)0xffffffffff600000UL;

  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
C=80 H=99 G=90

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge