| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (c) 2018 Dmitry Safonov, Arista Networks | |
| * | |
| * MAP_POPULATE | MAP_PRIVATE should COW VMA pages. | |
| */ | |
| #define _GNU_SOURCE | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <sys/mman.h> | |
| #include <sys/socket.h> | |
| #include <sys/types.h> | |
| #include <sys/wait.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #ifndef MMAP_SZ | |
| #define MMAP_SZ 4096 | |
| #endif | |
| #define BUG_ON(condition, description) \ | |
| do { \ | |
| if (condition) { \ | |
| fprintf(stderr, "[FAIL]\t%s:%d\t%s:%s\n", __func__, \ | |
| __LINE__, (description), strerror(errno)); \ | |
| exit(1); \ | |
| } \ | |
| } while (0) | |
| static int parent_f(int sock, unsigned long *smap, int child) | |
| { | |
| int status, ret; | |
| ret = read(sock, &status, sizeof(int)); | |
| BUG_ON(ret <= 0, "read(sock)"); | |
| *smap = 0x22222BAD; | |
| ret = msync(smap, MMAP_SZ, MS_SYNC); | |
| BUG_ON(ret, "msync()"); | |
| ret = write(sock, &status, sizeof(int)); | |
| BUG_ON(ret <= 0, "write(sock)"); | |
| waitpid(child, &status, 0); | |
| BUG_ON(!WIFEXITED(status), "child in unexpected state"); | |
| return WEXITSTATUS(status); | |
| } | |
| static int child_f(int sock, unsigned long *smap, int fd) | |
| { | |
| int ret, buf = 0; | |
| smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE, | |
| MAP_PRIVATE | MAP_POPULATE, fd, 0); | |
| BUG_ON(smap == MAP_FAILED, "mmap()"); | |
| BUG_ON(*smap != 0xdeadbabe, "MAP_PRIVATE | MAP_POPULATE changed file"); | |
| ret = write(sock, &buf, sizeof(int)); | |
| BUG_ON(ret <= 0, "write(sock)"); | |
| ret = read(sock, &buf, sizeof(int)); | |
| BUG_ON(ret <= 0, "read(sock)"); | |
| BUG_ON(*smap == 0x22222BAD, "MAP_POPULATE didn't COW private page"); | |
| BUG_ON(*smap != 0xdeadbabe, "mapping was corrupted"); | |
| return 0; | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| int sock[2], child, ret; | |
| FILE *ftmp; | |
| unsigned long *smap; | |
| ftmp = tmpfile(); | |
| BUG_ON(ftmp == 0, "tmpfile()"); | |
| ret = ftruncate(fileno(ftmp), MMAP_SZ); | |
| BUG_ON(ret, "ftruncate()"); | |
| smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE, | |
| MAP_SHARED, fileno(ftmp), 0); | |
| BUG_ON(smap == MAP_FAILED, "mmap()"); | |
| *smap = 0xdeadbabe; | |
| /* Probably unnecessary, but let it be. */ | |
| ret = msync(smap, MMAP_SZ, MS_SYNC); | |
| BUG_ON(ret, "msync()"); | |
| ret = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sock); | |
| BUG_ON(ret, "socketpair()"); | |
| child = fork(); | |
| BUG_ON(child == -1, "fork()"); | |
| if (child) { | |
| ret = close(sock[0]); | |
| BUG_ON(ret, "close()"); | |
| return parent_f(sock[1], smap, child); | |
| } | |
| ret = close(sock[1]); | |
| BUG_ON(ret, "close()"); | |
| return child_f(sock[0], smap, fileno(ftmp)); | |
| } |