162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2019 Alexey Dobriyan <adobriyan@gmail.com>
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any
562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * Fork and exec tiny 1 page executable which precisely controls its VM.
1862306a36Sopenharmony_ci * Test /proc/$PID/maps
1962306a36Sopenharmony_ci * Test /proc/$PID/smaps
2062306a36Sopenharmony_ci * Test /proc/$PID/smaps_rollup
2162306a36Sopenharmony_ci * Test /proc/$PID/statm
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * FIXME require CONFIG_TMPFS which can be disabled
2462306a36Sopenharmony_ci * FIXME test other values from "smaps"
2562306a36Sopenharmony_ci * FIXME support other archs
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci#undef NDEBUG
2862306a36Sopenharmony_ci#include <assert.h>
2962306a36Sopenharmony_ci#include <errno.h>
3062306a36Sopenharmony_ci#include <sched.h>
3162306a36Sopenharmony_ci#include <signal.h>
3262306a36Sopenharmony_ci#include <stdbool.h>
3362306a36Sopenharmony_ci#include <stdint.h>
3462306a36Sopenharmony_ci#include <stdio.h>
3562306a36Sopenharmony_ci#include <string.h>
3662306a36Sopenharmony_ci#include <stdlib.h>
3762306a36Sopenharmony_ci#include <sys/mount.h>
3862306a36Sopenharmony_ci#include <sys/types.h>
3962306a36Sopenharmony_ci#include <sys/stat.h>
4062306a36Sopenharmony_ci#include <sys/wait.h>
4162306a36Sopenharmony_ci#include <fcntl.h>
4262306a36Sopenharmony_ci#include <unistd.h>
4362306a36Sopenharmony_ci#include <sys/syscall.h>
4462306a36Sopenharmony_ci#include <sys/uio.h>
4562306a36Sopenharmony_ci#include <linux/kdev_t.h>
4662306a36Sopenharmony_ci#include <sys/time.h>
4762306a36Sopenharmony_ci#include <sys/resource.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include "../kselftest.h"
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic inline long sys_execveat(int dirfd, const char *pathname, char **argv, char **envp, int flags)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return syscall(SYS_execveat, dirfd, pathname, argv, envp, flags);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void make_private_tmp(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	if (unshare(CLONE_NEWNS) == -1) {
5962306a36Sopenharmony_ci		if (errno == ENOSYS || errno == EPERM) {
6062306a36Sopenharmony_ci			exit(4);
6162306a36Sopenharmony_ci		}
6262306a36Sopenharmony_ci		exit(1);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) == -1) {
6562306a36Sopenharmony_ci		exit(1);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci	if (mount(NULL, "/tmp", "tmpfs", 0, NULL) == -1) {
6862306a36Sopenharmony_ci		exit(1);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic pid_t pid = -1;
7362306a36Sopenharmony_cistatic void ate(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if (pid > 0) {
7662306a36Sopenharmony_ci		kill(pid, SIGTERM);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct elf64_hdr {
8162306a36Sopenharmony_ci	uint8_t e_ident[16];
8262306a36Sopenharmony_ci	uint16_t e_type;
8362306a36Sopenharmony_ci	uint16_t e_machine;
8462306a36Sopenharmony_ci	uint32_t e_version;
8562306a36Sopenharmony_ci	uint64_t e_entry;
8662306a36Sopenharmony_ci	uint64_t e_phoff;
8762306a36Sopenharmony_ci	uint64_t e_shoff;
8862306a36Sopenharmony_ci	uint32_t e_flags;
8962306a36Sopenharmony_ci	uint16_t e_ehsize;
9062306a36Sopenharmony_ci	uint16_t e_phentsize;
9162306a36Sopenharmony_ci	uint16_t e_phnum;
9262306a36Sopenharmony_ci	uint16_t e_shentsize;
9362306a36Sopenharmony_ci	uint16_t e_shnum;
9462306a36Sopenharmony_ci	uint16_t e_shstrndx;
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct elf64_phdr {
9862306a36Sopenharmony_ci	uint32_t p_type;
9962306a36Sopenharmony_ci	uint32_t p_flags;
10062306a36Sopenharmony_ci	uint64_t p_offset;
10162306a36Sopenharmony_ci	uint64_t p_vaddr;
10262306a36Sopenharmony_ci	uint64_t p_paddr;
10362306a36Sopenharmony_ci	uint64_t p_filesz;
10462306a36Sopenharmony_ci	uint64_t p_memsz;
10562306a36Sopenharmony_ci	uint64_t p_align;
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#ifdef __x86_64__
10962306a36Sopenharmony_ci#define PAGE_SIZE 4096
11062306a36Sopenharmony_ci#define VADDR (1UL << 32)
11162306a36Sopenharmony_ci#define MAPS_OFFSET 73
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define syscall	0x0f, 0x05
11462306a36Sopenharmony_ci#define mov_rdi(x)	\
11562306a36Sopenharmony_ci	0x48, 0xbf,	\
11662306a36Sopenharmony_ci	(x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff,	\
11762306a36Sopenharmony_ci	((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, ((x)>>56)&0xff
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define mov_rsi(x)	\
12062306a36Sopenharmony_ci	0x48, 0xbe,	\
12162306a36Sopenharmony_ci	(x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff,	\
12262306a36Sopenharmony_ci	((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, ((x)>>56)&0xff
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define mov_eax(x)	\
12562306a36Sopenharmony_ci	0xb8, (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic const uint8_t payload[] = {
12862306a36Sopenharmony_ci	/* Casually unmap stack, vDSO and everything else. */
12962306a36Sopenharmony_ci	/* munmap */
13062306a36Sopenharmony_ci	mov_rdi(VADDR + 4096),
13162306a36Sopenharmony_ci	mov_rsi((1ULL << 47) - 4096 - VADDR - 4096),
13262306a36Sopenharmony_ci	mov_eax(11),
13362306a36Sopenharmony_ci	syscall,
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Ping parent. */
13662306a36Sopenharmony_ci	/* write(0, &c, 1); */
13762306a36Sopenharmony_ci	0x31, 0xff,					/* xor edi, edi */
13862306a36Sopenharmony_ci	0x48, 0x8d, 0x35, 0x00, 0x00, 0x00, 0x00,	/* lea rsi, [rip] */
13962306a36Sopenharmony_ci	0xba, 0x01, 0x00, 0x00, 0x00,			/* mov edx, 1 */
14062306a36Sopenharmony_ci	mov_eax(1),
14162306a36Sopenharmony_ci	syscall,
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* 1: pause(); */
14462306a36Sopenharmony_ci	mov_eax(34),
14562306a36Sopenharmony_ci	syscall,
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	0xeb, 0xf7,	/* jmp 1b */
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int make_exe(const uint8_t *payload, size_t len)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct elf64_hdr h;
15362306a36Sopenharmony_ci	struct elf64_phdr ph;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	struct iovec iov[3] = {
15662306a36Sopenharmony_ci		{&h, sizeof(struct elf64_hdr)},
15762306a36Sopenharmony_ci		{&ph, sizeof(struct elf64_phdr)},
15862306a36Sopenharmony_ci		{(void *)payload, len},
15962306a36Sopenharmony_ci	};
16062306a36Sopenharmony_ci	int fd, fd1;
16162306a36Sopenharmony_ci	char buf[64];
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	memset(&h, 0, sizeof(h));
16462306a36Sopenharmony_ci	h.e_ident[0] = 0x7f;
16562306a36Sopenharmony_ci	h.e_ident[1] = 'E';
16662306a36Sopenharmony_ci	h.e_ident[2] = 'L';
16762306a36Sopenharmony_ci	h.e_ident[3] = 'F';
16862306a36Sopenharmony_ci	h.e_ident[4] = 2;
16962306a36Sopenharmony_ci	h.e_ident[5] = 1;
17062306a36Sopenharmony_ci	h.e_ident[6] = 1;
17162306a36Sopenharmony_ci	h.e_ident[7] = 0;
17262306a36Sopenharmony_ci	h.e_type = 2;
17362306a36Sopenharmony_ci	h.e_machine = 0x3e;
17462306a36Sopenharmony_ci	h.e_version = 1;
17562306a36Sopenharmony_ci	h.e_entry = VADDR + sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr);
17662306a36Sopenharmony_ci	h.e_phoff = sizeof(struct elf64_hdr);
17762306a36Sopenharmony_ci	h.e_shoff = 0;
17862306a36Sopenharmony_ci	h.e_flags = 0;
17962306a36Sopenharmony_ci	h.e_ehsize = sizeof(struct elf64_hdr);
18062306a36Sopenharmony_ci	h.e_phentsize = sizeof(struct elf64_phdr);
18162306a36Sopenharmony_ci	h.e_phnum = 1;
18262306a36Sopenharmony_ci	h.e_shentsize = 0;
18362306a36Sopenharmony_ci	h.e_shnum = 0;
18462306a36Sopenharmony_ci	h.e_shstrndx = 0;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	memset(&ph, 0, sizeof(ph));
18762306a36Sopenharmony_ci	ph.p_type = 1;
18862306a36Sopenharmony_ci	ph.p_flags = (1<<2)|1;
18962306a36Sopenharmony_ci	ph.p_offset = 0;
19062306a36Sopenharmony_ci	ph.p_vaddr = VADDR;
19162306a36Sopenharmony_ci	ph.p_paddr = 0;
19262306a36Sopenharmony_ci	ph.p_filesz = sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len;
19362306a36Sopenharmony_ci	ph.p_memsz = sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len;
19462306a36Sopenharmony_ci	ph.p_align = 4096;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	fd = openat(AT_FDCWD, "/tmp", O_WRONLY|O_EXCL|O_TMPFILE, 0700);
19762306a36Sopenharmony_ci	if (fd == -1) {
19862306a36Sopenharmony_ci		exit(1);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (writev(fd, iov, 3) != sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len) {
20262306a36Sopenharmony_ci		exit(1);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Avoid ETXTBSY on exec. */
20662306a36Sopenharmony_ci	snprintf(buf, sizeof(buf), "/proc/self/fd/%u", fd);
20762306a36Sopenharmony_ci	fd1 = open(buf, O_RDONLY|O_CLOEXEC);
20862306a36Sopenharmony_ci	close(fd);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return fd1;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci#endif
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/*
21562306a36Sopenharmony_ci * 0: vsyscall VMA doesn't exist	vsyscall=none
21662306a36Sopenharmony_ci * 1: vsyscall VMA is --xp		vsyscall=xonly
21762306a36Sopenharmony_ci * 2: vsyscall VMA is r-xp		vsyscall=emulate
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic volatile int g_vsyscall;
22062306a36Sopenharmony_cistatic const char *str_vsyscall;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic const char str_vsyscall_0[] = "";
22362306a36Sopenharmony_cistatic const char str_vsyscall_1[] =
22462306a36Sopenharmony_ci"ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]\n";
22562306a36Sopenharmony_cistatic const char str_vsyscall_2[] =
22662306a36Sopenharmony_ci"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]\n";
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci#ifdef __x86_64__
22962306a36Sopenharmony_cistatic void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	_exit(g_vsyscall);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * vsyscall page can't be unmapped, probe it directly.
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_cistatic void vsyscall(void)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	pid_t pid;
24062306a36Sopenharmony_ci	int wstatus;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	pid = fork();
24362306a36Sopenharmony_ci	if (pid < 0) {
24462306a36Sopenharmony_ci		fprintf(stderr, "fork, errno %d\n", errno);
24562306a36Sopenharmony_ci		exit(1);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	if (pid == 0) {
24862306a36Sopenharmony_ci		struct rlimit rlim = {0, 0};
24962306a36Sopenharmony_ci		(void)setrlimit(RLIMIT_CORE, &rlim);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		/* Hide "segfault at ffffffffff600000" messages. */
25262306a36Sopenharmony_ci		struct sigaction act;
25362306a36Sopenharmony_ci		memset(&act, 0, sizeof(struct sigaction));
25462306a36Sopenharmony_ci		act.sa_flags = SA_SIGINFO;
25562306a36Sopenharmony_ci		act.sa_sigaction = sigaction_SIGSEGV;
25662306a36Sopenharmony_ci		(void)sigaction(SIGSEGV, &act, NULL);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		g_vsyscall = 0;
25962306a36Sopenharmony_ci		/* gettimeofday(NULL, NULL); */
26062306a36Sopenharmony_ci		uint64_t rax = 0xffffffffff600000;
26162306a36Sopenharmony_ci		asm volatile (
26262306a36Sopenharmony_ci			"call *%[rax]"
26362306a36Sopenharmony_ci			: [rax] "+a" (rax)
26462306a36Sopenharmony_ci			: "D" (NULL), "S" (NULL)
26562306a36Sopenharmony_ci			: "rcx", "r11"
26662306a36Sopenharmony_ci		);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		g_vsyscall = 1;
26962306a36Sopenharmony_ci		*(volatile int *)0xffffffffff600000UL;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		g_vsyscall = 2;
27262306a36Sopenharmony_ci		exit(g_vsyscall);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	waitpid(pid, &wstatus, 0);
27562306a36Sopenharmony_ci	if (WIFEXITED(wstatus)) {
27662306a36Sopenharmony_ci		g_vsyscall = WEXITSTATUS(wstatus);
27762306a36Sopenharmony_ci	} else {
27862306a36Sopenharmony_ci		fprintf(stderr, "error: wstatus %08x\n", wstatus);
27962306a36Sopenharmony_ci		exit(1);
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciint main(void)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	int pipefd[2];
28662306a36Sopenharmony_ci	int exec_fd;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	vsyscall();
28962306a36Sopenharmony_ci	switch (g_vsyscall) {
29062306a36Sopenharmony_ci	case 0:
29162306a36Sopenharmony_ci		str_vsyscall = str_vsyscall_0;
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	case 1:
29462306a36Sopenharmony_ci		str_vsyscall = str_vsyscall_1;
29562306a36Sopenharmony_ci		break;
29662306a36Sopenharmony_ci	case 2:
29762306a36Sopenharmony_ci		str_vsyscall = str_vsyscall_2;
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci	default:
30062306a36Sopenharmony_ci		abort();
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	atexit(ate);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	make_private_tmp();
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Reserve fd 0 for 1-byte pipe ping from child. */
30862306a36Sopenharmony_ci	close(0);
30962306a36Sopenharmony_ci	if (open("/", O_RDONLY|O_DIRECTORY|O_PATH) != 0) {
31062306a36Sopenharmony_ci		return 1;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	exec_fd = make_exe(payload, sizeof(payload));
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (pipe(pipefd) == -1) {
31662306a36Sopenharmony_ci		return 1;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci	if (dup2(pipefd[1], 0) != 0) {
31962306a36Sopenharmony_ci		return 1;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	pid = fork();
32362306a36Sopenharmony_ci	if (pid == -1) {
32462306a36Sopenharmony_ci		return 1;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci	if (pid == 0) {
32762306a36Sopenharmony_ci		sys_execveat(exec_fd, "", NULL, NULL, AT_EMPTY_PATH);
32862306a36Sopenharmony_ci		return 1;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	char _;
33262306a36Sopenharmony_ci	if (read(pipefd[0], &_, 1) != 1) {
33362306a36Sopenharmony_ci		return 1;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	struct stat st;
33762306a36Sopenharmony_ci	if (fstat(exec_fd, &st) == -1) {
33862306a36Sopenharmony_ci		return 1;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* Generate "head -n1 /proc/$PID/maps" */
34262306a36Sopenharmony_ci	char buf0[256];
34362306a36Sopenharmony_ci	memset(buf0, ' ', sizeof(buf0));
34462306a36Sopenharmony_ci	int len = snprintf(buf0, sizeof(buf0),
34562306a36Sopenharmony_ci			"%08lx-%08lx r-xp 00000000 %02lx:%02lx %llu",
34662306a36Sopenharmony_ci			VADDR, VADDR + PAGE_SIZE,
34762306a36Sopenharmony_ci			MAJOR(st.st_dev), MINOR(st.st_dev),
34862306a36Sopenharmony_ci			(unsigned long long)st.st_ino);
34962306a36Sopenharmony_ci	buf0[len] = ' ';
35062306a36Sopenharmony_ci	snprintf(buf0 + MAPS_OFFSET, sizeof(buf0) - MAPS_OFFSET,
35162306a36Sopenharmony_ci		 "/tmp/#%llu (deleted)\n", (unsigned long long)st.st_ino);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* Test /proc/$PID/maps */
35462306a36Sopenharmony_ci	{
35562306a36Sopenharmony_ci		const size_t len = strlen(buf0) + strlen(str_vsyscall);
35662306a36Sopenharmony_ci		char buf[256];
35762306a36Sopenharmony_ci		ssize_t rv;
35862306a36Sopenharmony_ci		int fd;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		snprintf(buf, sizeof(buf), "/proc/%u/maps", pid);
36162306a36Sopenharmony_ci		fd = open(buf, O_RDONLY);
36262306a36Sopenharmony_ci		if (fd == -1) {
36362306a36Sopenharmony_ci			return 1;
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci		rv = read(fd, buf, sizeof(buf));
36662306a36Sopenharmony_ci		assert(rv == len);
36762306a36Sopenharmony_ci		assert(memcmp(buf, buf0, strlen(buf0)) == 0);
36862306a36Sopenharmony_ci		if (g_vsyscall > 0) {
36962306a36Sopenharmony_ci			assert(memcmp(buf + strlen(buf0), str_vsyscall, strlen(str_vsyscall)) == 0);
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* Test /proc/$PID/smaps */
37462306a36Sopenharmony_ci	{
37562306a36Sopenharmony_ci		char buf[4096];
37662306a36Sopenharmony_ci		ssize_t rv;
37762306a36Sopenharmony_ci		int fd;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		snprintf(buf, sizeof(buf), "/proc/%u/smaps", pid);
38062306a36Sopenharmony_ci		fd = open(buf, O_RDONLY);
38162306a36Sopenharmony_ci		if (fd == -1) {
38262306a36Sopenharmony_ci			return 1;
38362306a36Sopenharmony_ci		}
38462306a36Sopenharmony_ci		rv = read(fd, buf, sizeof(buf));
38562306a36Sopenharmony_ci		assert(0 <= rv && rv <= sizeof(buf));
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		assert(rv >= strlen(buf0));
38862306a36Sopenharmony_ci		assert(memcmp(buf, buf0, strlen(buf0)) == 0);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci#define RSS1 "Rss:                   4 kB\n"
39162306a36Sopenharmony_ci#define RSS2 "Rss:                   0 kB\n"
39262306a36Sopenharmony_ci#define PSS1 "Pss:                   4 kB\n"
39362306a36Sopenharmony_ci#define PSS2 "Pss:                   0 kB\n"
39462306a36Sopenharmony_ci		assert(memmem(buf, rv, RSS1, strlen(RSS1)) ||
39562306a36Sopenharmony_ci		       memmem(buf, rv, RSS2, strlen(RSS2)));
39662306a36Sopenharmony_ci		assert(memmem(buf, rv, PSS1, strlen(PSS1)) ||
39762306a36Sopenharmony_ci		       memmem(buf, rv, PSS2, strlen(PSS2)));
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		static const char *S[] = {
40062306a36Sopenharmony_ci			"Size:                  4 kB\n",
40162306a36Sopenharmony_ci			"KernelPageSize:        4 kB\n",
40262306a36Sopenharmony_ci			"MMUPageSize:           4 kB\n",
40362306a36Sopenharmony_ci			"Anonymous:             0 kB\n",
40462306a36Sopenharmony_ci			"AnonHugePages:         0 kB\n",
40562306a36Sopenharmony_ci			"Shared_Hugetlb:        0 kB\n",
40662306a36Sopenharmony_ci			"Private_Hugetlb:       0 kB\n",
40762306a36Sopenharmony_ci			"Locked:                0 kB\n",
40862306a36Sopenharmony_ci		};
40962306a36Sopenharmony_ci		int i;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(S); i++) {
41262306a36Sopenharmony_ci			assert(memmem(buf, rv, S[i], strlen(S[i])));
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		if (g_vsyscall > 0) {
41662306a36Sopenharmony_ci			assert(memmem(buf, rv, str_vsyscall, strlen(str_vsyscall)));
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* Test /proc/$PID/smaps_rollup */
42162306a36Sopenharmony_ci	{
42262306a36Sopenharmony_ci		char bufr[256];
42362306a36Sopenharmony_ci		memset(bufr, ' ', sizeof(bufr));
42462306a36Sopenharmony_ci		len = snprintf(bufr, sizeof(bufr),
42562306a36Sopenharmony_ci				"%08lx-%08lx ---p 00000000 00:00 0",
42662306a36Sopenharmony_ci				VADDR, VADDR + PAGE_SIZE);
42762306a36Sopenharmony_ci		bufr[len] = ' ';
42862306a36Sopenharmony_ci		snprintf(bufr + MAPS_OFFSET, sizeof(bufr) - MAPS_OFFSET,
42962306a36Sopenharmony_ci			 "[rollup]\n");
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		char buf[1024];
43262306a36Sopenharmony_ci		ssize_t rv;
43362306a36Sopenharmony_ci		int fd;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		snprintf(buf, sizeof(buf), "/proc/%u/smaps_rollup", pid);
43662306a36Sopenharmony_ci		fd = open(buf, O_RDONLY);
43762306a36Sopenharmony_ci		if (fd == -1) {
43862306a36Sopenharmony_ci			return 1;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci		rv = read(fd, buf, sizeof(buf));
44162306a36Sopenharmony_ci		assert(0 <= rv && rv <= sizeof(buf));
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		assert(rv >= strlen(bufr));
44462306a36Sopenharmony_ci		assert(memcmp(buf, bufr, strlen(bufr)) == 0);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		assert(memmem(buf, rv, RSS1, strlen(RSS1)) ||
44762306a36Sopenharmony_ci		       memmem(buf, rv, RSS2, strlen(RSS2)));
44862306a36Sopenharmony_ci		assert(memmem(buf, rv, PSS1, strlen(PSS1)) ||
44962306a36Sopenharmony_ci		       memmem(buf, rv, PSS2, strlen(PSS2)));
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		static const char *S[] = {
45262306a36Sopenharmony_ci			"Anonymous:             0 kB\n",
45362306a36Sopenharmony_ci			"AnonHugePages:         0 kB\n",
45462306a36Sopenharmony_ci			"Shared_Hugetlb:        0 kB\n",
45562306a36Sopenharmony_ci			"Private_Hugetlb:       0 kB\n",
45662306a36Sopenharmony_ci			"Locked:                0 kB\n",
45762306a36Sopenharmony_ci		};
45862306a36Sopenharmony_ci		int i;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(S); i++) {
46162306a36Sopenharmony_ci			assert(memmem(buf, rv, S[i], strlen(S[i])));
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* Test /proc/$PID/statm */
46662306a36Sopenharmony_ci	{
46762306a36Sopenharmony_ci		char buf[64];
46862306a36Sopenharmony_ci		ssize_t rv;
46962306a36Sopenharmony_ci		int fd;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		snprintf(buf, sizeof(buf), "/proc/%u/statm", pid);
47262306a36Sopenharmony_ci		fd = open(buf, O_RDONLY);
47362306a36Sopenharmony_ci		if (fd == -1) {
47462306a36Sopenharmony_ci			return 1;
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci		rv = read(fd, buf, sizeof(buf));
47762306a36Sopenharmony_ci		assert(rv == 7 * 2);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		assert(buf[0] == '1');	/* ->total_vm */
48062306a36Sopenharmony_ci		assert(buf[1] == ' ');
48162306a36Sopenharmony_ci		assert(buf[2] == '0' || buf[2] == '1');	/* rss */
48262306a36Sopenharmony_ci		assert(buf[3] == ' ');
48362306a36Sopenharmony_ci		assert(buf[4] == '0' || buf[2] == '1');	/* file rss */
48462306a36Sopenharmony_ci		assert(buf[5] == ' ');
48562306a36Sopenharmony_ci		assert(buf[6] == '1');	/* ELF executable segments */
48662306a36Sopenharmony_ci		assert(buf[7] == ' ');
48762306a36Sopenharmony_ci		assert(buf[8] == '0');
48862306a36Sopenharmony_ci		assert(buf[9] == ' ');
48962306a36Sopenharmony_ci		assert(buf[10] == '0');	/* ->data_vm + ->stack_vm */
49062306a36Sopenharmony_ci		assert(buf[11] == ' ');
49162306a36Sopenharmony_ci		assert(buf[12] == '0');
49262306a36Sopenharmony_ci		assert(buf[13] == '\n');
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return 0;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci#else
49862306a36Sopenharmony_ciint main(void)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	return 4;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci#endif
503