#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <syscall.h>
#include <errno.h>
#include <stdbool.h>
#ifndef MLOCK_ONFAULT
#define MLOCK_ONFAULT 1
#endif
#ifndef MCL_ONFAULT
#define MCL_ONFAULT (MCL_FUTURE << 1)
#endif
static int mlock2_(void *start, size_t len, int flags)
{
#ifdef __NR_mlock2
return syscall(__NR_mlock2, start, len, flags);
#else
errno = ENOSYS;
return -1;
#endif
}
struct vm_boundaries {
unsigned long start;
unsigned long end;
};
static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
{
FILE *file;
int ret = 1;
char line[1024] = {0};
char *end_addr;
char *stop;
unsigned long start;
unsigned long end;
if (!area)
return ret;
file = fopen("/proc/self/maps", "r");
if (!file) {
perror("fopen");
return ret;
}
memset(area, 0, sizeof(struct vm_boundaries));
while(fgets(line, 1024, file)) {
end_addr = strchr(line, '-');
if (!end_addr) {
printf("cannot parse /proc/self/maps\n");
goto out;
}
*end_addr = '\0';
end_addr++;
stop = strchr(end_addr, ' ');
if (!stop) {
printf("cannot parse /proc/self/maps\n");
goto out;
}
stop = '\0';
sscanf(line, "%lx", &start);
sscanf(end_addr, "%lx", &end);
if (start <= addr && end > addr) {
area->start = start;
area->end = end;
ret = 0;
goto out;
}
}
out:
fclose(file);
return ret;
}
static uint64_t get_pageflags(unsigned long addr)
{
FILE *file;
uint64_t pfn;
unsigned long offset;
file = fopen("/proc/self/pagemap", "r");
if (!file) {
perror("fopen pagemap");
_exit(1);
}
offset = addr / getpagesize() * sizeof(pfn);
if (fseek(file, offset, SEEK_SET)) {
perror("fseek pagemap");
_exit(1);
}
if (fread(&pfn, sizeof(pfn), 1, file) != 1) {
perror("fread pagemap");
_exit(1);
}
fclose(file);
return pfn;
}
static uint64_t get_kpageflags(unsigned long pfn)
{
uint64_t flags;
FILE *file;
file = fopen("/proc/kpageflags", "r");
if (!file) {
perror("fopen kpageflags");
_exit(1);
}
if (fseek(file, pfn * sizeof(flags), SEEK_SET)) {
perror("fseek kpageflags");
_exit(1);
}
if (fread(&flags, sizeof(flags), 1, file) != 1) {
perror("fread kpageflags");
_exit(1);
}
fclose(file);
return flags;
}
static FILE *seek_to_smaps_entry(unsigned long addr)
{
FILE *file;
char *line = NULL;
size_t size = 0;
unsigned long start, end;
char perms[5];
unsigned long offset;
char dev[32];
unsigned long inode;
char path[BUFSIZ];
file = fopen("/proc/self/smaps", "r");
if (!file) {
perror("fopen smaps");
_exit(1);
}
while (getline(&line, &size, file) > 0) {
if (sscanf(line, "%lx-%lx %s %lx %s %lu %s\n",
&start, &end, perms, &offset, dev, &inode, path) < 6)
goto next;
if (start <= addr && addr < end)
goto out;
next:
free(line);
line = NULL;
size = 0;
}
fclose(file);
file = NULL;
out:
free(line);
return file;
}
#define VMFLAGS "VmFlags:"
static bool is_vmflag_set(unsigned long addr, const char *vmflag)
{
char *line = NULL;
char *flags;
size_t size = 0;
bool ret = false;
FILE *smaps;
smaps = seek_to_smaps_entry(addr);
if (!smaps) {
printf("Unable to parse /proc/self/smaps\n");
goto out;
}
while (getline(&line, &size, smaps) > 0) {
if (!strstr(line, VMFLAGS)) {
free(line);
line = NULL;
size = 0;
continue;
}
flags = line + strlen(VMFLAGS);
ret = (strstr(flags, vmflag) != NULL);
goto out;