Skip to content

Instantly share code, notes, and snippets.

@thejh
Created July 29, 2018 01:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thejh/92b66c44cd3ecc381deed9815b72310c to your computer and use it in GitHub Desktop.
Save thejh/92b66c44cd3ecc381deed9815b72310c to your computer and use it in GitHub Desktop.
unused stack memory experiment
// small stack memory usage experiment
// written by Jann Horn
#include <stdint.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <fcntl.h>
#include <err.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
int main(void) {
int pid_max;
{
char pid_max_str[30];
int fd = open("/proc/sys/kernel/pid_max", O_RDONLY);
if (fd == -1) err(1, "open pid_max");
int res = read(fd, pid_max_str, sizeof(pid_max_str)-1);
if (res == -1) err(1, "read pid_max");
close(fd);
pid_max_str[res] = 0;
pid_max = atoi(pid_max_str);
if (pid_max < 100)
errx(1, "read pid_max");
printf("got pid_max: %d\n", pid_max);
}
int procs_seen = 0;
long present_pages_sum = 0, swapped_pages_sum = 0;
for (int pid = 1; pid < pid_max; pid++) {
int r;
r = ptrace(PTRACE_ATTACH, pid, 0, 0);
if (r == -1) continue;
// wait for SIGSTOP, but reinject everything else
while (1) {
int status;
if (waitpid(pid, &status, 0) != pid)
err(1, "waitpid");
if (WIFEXITED(status) || WIFSIGNALED(status)) {
// oops, we raced, it's dead
goto next_pid;
}
assert(WIFSTOPPED(status));
int sig = WSTOPSIG(status);
if (sig == SIGSTOP) {
// wheee, it's stopped now!
break;
} else {
// bleh. reinject and loop back.
if (ptrace(PTRACE_CONT, pid, NULL, sig))
err(1, "reinject signal");
}
}
// we're attached, grab the register state
printf("pid %d\n", pid);
struct user_regs_struct regs = {0};
if (ptrace(PTRACE_GETREGS, pid, NULL, &regs))
err(1, "PTRACE_GETREGS");
printf(" rsp: 0x%016llx\n", regs.rsp);
printf(" rip: 0x%016llx\n", regs.rip);
unsigned long redzone_bottom = regs.rsp - 128;
unsigned long redzone_bottom_page = redzone_bottom & ~0xfffUL;
char maps_name[100];
sprintf(maps_name, "/proc/%d/maps", pid);
FILE *maps_file = fopen(maps_name, "r");
if (!maps_file) {
perror("open maps");
goto detach;
}
char line[1000];
unsigned long stack_start;
while (fgets(line, sizeof(line), maps_file) == line) {
unsigned long start, end;
if (sscanf(line, "%lx-%lx", &start, &end) != 2)
continue;
if (start <= regs.rsp && regs.rsp < end) {
printf(" found stack: 0x%lx-0x%lx\n", start, end);
printf(" stack usage: %lu bytes\n", end-redzone_bottom);
stack_start = start;
break;
}
}
fclose(maps_file);
char pagemap_name[100];
sprintf(pagemap_name, "/proc/%d/pagemap", pid);
int pagemap_fd = open(pagemap_name, O_RDONLY);
int present_pages = 0, swapped_pages = 0;
if (pagemap_fd == -1) {
perror("open pagemap");
goto detach;
}
for (unsigned long page = stack_start; page < redzone_bottom_page; page += 0x1000) {
unsigned long off = page / 0x1000 * sizeof(uint64_t);
uint64_t val;
if (pread(pagemap_fd, &val, sizeof(uint64_t), off) != sizeof(uint64_t)) {
perror("bad pagemap read");
goto detach;
}
if (val & (1UL<<63)) {
present_pages++;
} else if (val & (1UL<<62)) {
swapped_pages++;
}
}
close(pagemap_fd);
printf(" present: %d kB\n", present_pages*4);
printf(" swapped: %d kB\n", swapped_pages*4);
present_pages_sum += present_pages;
swapped_pages_sum += swapped_pages;
// get out of here!
procs_seen++;
detach:
if (ptrace(PTRACE_DETACH, pid, 0, 0))
err(1, "PTRACE_DETACH");
next_pid:;
}
printf("\nTOTAL:\n");
printf("tasks: %d\n", procs_seen);
printf("present: %ld kB\n", present_pages_sum*4);
printf("swapped: %ld kB\n", swapped_pages_sum*4);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment