#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <sys/prctl.h>

#ifndef PR_SET_MDWE
# define PR_SET_MDWE 65
#endif
#ifndef PR_MDWE_REFUSE_EXEC_GAIN
# define PR_MDWE_REFUSE_EXEC_GAIN 1
#endif

int main(void)
{
	int fd;
	char *addr;
	const char *data_x = "benign code";
	const char *data_X = "malicious code";
	size_t len_x = strlen(data_x);
	size_t len_X = strlen(data_X);

	// Step 0: Set MDWE to refuse EXEC gain.
	if (prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0, 0, 0) == -1) {
		perror("prctl(PR_SET_MDWE)");
		exit(ENOSYS);
	}

	// Step 1: Open file.
	fd = open("./mmap", O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
	if (fd == -1) {
		perror("open");
		exit(EXIT_FAILURE);
	}

	// Write initial content.
	if (write(fd, data_x, len_x) != len_x) {
		perror("write");
		exit(EXIT_FAILURE);
	}

	// Step 2: Memory-map the file.
	addr = mmap(NULL, len_x, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
	if (addr == MAP_FAILED) {
		perror("mmap");
		exit(EXIT_FAILURE);
	}

	// Write new content to the file.
	if (lseek(fd, 0, SEEK_SET) == -1) {
		perror("lseek");
		exit(EXIT_FAILURE);
	}

	if (write(fd, data_X, len_X) != len_X) {
		perror("write");
		exit(EXIT_FAILURE);
	}

	// Close file, this will sync the contents to the readable memory area.
	// This breaks W^X and MDWE should prevent this.
	close(fd);

	// Check the mapped memory.
	printf("[*] Mapped Content: %s\n", addr);
	if (!strncmp(addr, "malicious", strlen("malicious"))) {
		printf("[!] RX memory updated thru a backing file write under MDWE.\n");
	}

	unlink("./mmap");
	return EXIT_SUCCESS;
}

