• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Cyril Hrubis <chrubis@suse.cz>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * This is a regression test for write race that allows unprivileged programs
20  * to change readonly files on the system.
21  *
22  * It has been fixed long time ago:
23  *
24  *   commit 4ceb5db9757aaeadcf8fbbf97d76bd42aa4df0d6
25  *   Author: Linus Torvalds <torvalds@g5.osdl.org>
26  *   Date:   Mon Aug 1 11:14:49 2005 -0700
27  *
28  *   Fix get_user_pages() race for write access
29  *
30  * Then it reappeared and was fixed again in:
31  *
32  *   commit 19be0eaffa3ac7d8eb6784ad9bdbc7d67ed8e619
33  *   Author: Linus Torvalds <torvalds@linux-foundation.org>
34  *   Date:   Thu Oct 13 20:07:36 2016 GMT
35  *
36  *   mm: remove gup_flags FOLL_WRITE games from __get_user_pages()
37  */
38 
39 #include <sys/mman.h>
40 #include <fcntl.h>
41 #include <pthread.h>
42 #include <unistd.h>
43 #include <sys/stat.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <pwd.h>
47 
48 #include "tst_test.h"
49 
50 #define FNAME "test"
51 #define STR   "this is not a test\n"
52 
53 static uid_t nobody_uid;
54 static gid_t nobody_gid;
55 
setup(void)56 static void setup(void)
57 {
58 	struct passwd *pw;
59 
60 	pw = SAFE_GETPWNAM("nobody");
61 
62 	nobody_uid = pw->pw_uid;
63 	nobody_gid = pw->pw_gid;
64 }
65 
dirtyc0w_test(void)66 void dirtyc0w_test(void)
67 {
68 	int i, fd, pid, fail = 0;
69 	char c;
70 
71 	/* Create file */
72 	fd = SAFE_OPEN(FNAME, O_WRONLY|O_CREAT|O_EXCL, 0444);
73 	SAFE_WRITE(1, fd, STR, sizeof(STR)-1);
74 	SAFE_CLOSE(fd);
75 
76 	pid = SAFE_FORK();
77 
78 	if (!pid) {
79 		SAFE_SETGID(nobody_gid);
80 		SAFE_SETUID(nobody_uid);
81 		SAFE_EXECLP("dirtyc0w_child", "dirtyc0w_child", NULL);
82 	}
83 
84 	for (i = 0; i < 100; i++)  {
85 		usleep(10000);
86 
87 		SAFE_FILE_SCANF(FNAME, "%c", &c);
88 
89 		if (c != 't') {
90 			fail = 1;
91 			break;
92 		}
93 	}
94 
95 	SAFE_KILL(pid, SIGUSR1);
96 	tst_reap_children();
97 	SAFE_UNLINK(FNAME);
98 
99 	if (fail)
100 		tst_res(TFAIL, "Bug reproduced!");
101 	else
102 		tst_res(TPASS, "Bug not reproduced");
103 }
104 
105 static struct tst_test test = {
106 	.tid = "dirtyc0w",
107 	.needs_tmpdir = 1,
108 	.forks_child = 1,
109 	.needs_root = 1,
110 	.setup = setup,
111 	.test_all = dirtyc0w_test,
112 };
113