Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tools/testing/selftests: add tests for !tgt, src mremap() merges

Test that mremap()'ing a VMA into a position such that the target VMA on
merge is unfaulted and the source faulted is correctly performed.

We cover 4 cases:

1. Previous VMA unfaulted:

copied -----|
v
|-----------|.............|
| unfaulted |(faulted VMA)|
|-----------|.............|
prev

target = prev, expand prev to cover.

2. Next VMA unfaulted:

copied -----|
v
|.............|-----------|
|(faulted VMA)| unfaulted |
|.............|-----------|
next

target = next, expand next to cover.

3. Both adjacent VMAs unfaulted:

copied -----|
v
|-----------|.............|-----------|
| unfaulted |(faulted VMA)| unfaulted |
|-----------|.............|-----------|
prev next

target = prev, expand prev to cover.

4. prev unfaulted, next faulted:

copied -----|
v
|-----------|.............|-----------|
| unfaulted |(faulted VMA)| faulted |
|-----------|.............|-----------|
prev next

target = prev, expand prev to cover. Essentially equivalent to 3, but
with additional requirement that next's anon_vma is the same as the
copied VMA's.

Each of these are performed with MREMAP_DONTUNMAP set, which will cause a
KASAN assert for UAF or an assert on zero refcount anon_vma if a bug
exists with correctly propagating anon_vma state in each scenario.

Link: https://lkml.kernel.org/r/f903af2930c7c2c6e0948c886b58d0f42d8e8ba3.1767638272.git.lorenzo.stoakes@oracle.com
Fixes: 879bca0a2c4f ("mm/vma: fix incorrectly disallowed anonymous VMA merges")
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: David Hildenbrand (Red Hat) <david@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Jeongjun Park <aha310510@gmail.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Rik van Riel <riel@surriel.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Yeoreum Yun <yeoreum.yun@arm.com>
Cc: Harry Yoo <harry.yoo@oracle.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Lorenzo Stoakes and committed by
Andrew Morton
0ace8f2d 61f67c23

+232
+232
tools/testing/selftests/mm/merge.c
··· 1171 1171 ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); 1172 1172 } 1173 1173 1174 + TEST_F(merge, mremap_faulted_to_unfaulted_prev) 1175 + { 1176 + struct procmap_fd *procmap = &self->procmap; 1177 + unsigned int page_size = self->page_size; 1178 + char *ptr_a, *ptr_b; 1179 + 1180 + /* 1181 + * mremap() such that A and B merge: 1182 + * 1183 + * |------------| 1184 + * | \ | 1185 + * |-----------| | / |---------| 1186 + * | unfaulted | v \ | faulted | 1187 + * |-----------| / |---------| 1188 + * B \ A 1189 + */ 1190 + 1191 + /* Map VMA A into place. */ 1192 + ptr_a = mmap(&self->carveout[page_size + 3 * page_size], 1193 + 3 * page_size, 1194 + PROT_READ | PROT_WRITE, 1195 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1196 + ASSERT_NE(ptr_a, MAP_FAILED); 1197 + /* Fault it in. */ 1198 + ptr_a[0] = 'x'; 1199 + 1200 + /* 1201 + * Now move it out of the way so we can place VMA B in position, 1202 + * unfaulted. 1203 + */ 1204 + ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size, 1205 + MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]); 1206 + ASSERT_NE(ptr_a, MAP_FAILED); 1207 + 1208 + /* Map VMA B into place. */ 1209 + ptr_b = mmap(&self->carveout[page_size], 3 * page_size, 1210 + PROT_READ | PROT_WRITE, 1211 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1212 + ASSERT_NE(ptr_b, MAP_FAILED); 1213 + 1214 + /* 1215 + * Now move VMA A into position with MREMAP_DONTUNMAP to catch incorrect 1216 + * anon_vma propagation. 1217 + */ 1218 + ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size, 1219 + MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 1220 + &self->carveout[page_size + 3 * page_size]); 1221 + ASSERT_NE(ptr_a, MAP_FAILED); 1222 + 1223 + /* The VMAs should have merged. */ 1224 + ASSERT_TRUE(find_vma_procmap(procmap, ptr_b)); 1225 + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b); 1226 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 6 * page_size); 1227 + } 1228 + 1229 + TEST_F(merge, mremap_faulted_to_unfaulted_next) 1230 + { 1231 + struct procmap_fd *procmap = &self->procmap; 1232 + unsigned int page_size = self->page_size; 1233 + char *ptr_a, *ptr_b; 1234 + 1235 + /* 1236 + * mremap() such that A and B merge: 1237 + * 1238 + * |---------------------------| 1239 + * | \ | 1240 + * | |-----------| / |---------| 1241 + * v | unfaulted | \ | faulted | 1242 + * |-----------| / |---------| 1243 + * B \ A 1244 + * 1245 + * Then unmap VMA A to trigger the bug. 1246 + */ 1247 + 1248 + /* Map VMA A into place. */ 1249 + ptr_a = mmap(&self->carveout[page_size], 3 * page_size, 1250 + PROT_READ | PROT_WRITE, 1251 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1252 + ASSERT_NE(ptr_a, MAP_FAILED); 1253 + /* Fault it in. */ 1254 + ptr_a[0] = 'x'; 1255 + 1256 + /* 1257 + * Now move it out of the way so we can place VMA B in position, 1258 + * unfaulted. 1259 + */ 1260 + ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size, 1261 + MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]); 1262 + ASSERT_NE(ptr_a, MAP_FAILED); 1263 + 1264 + /* Map VMA B into place. */ 1265 + ptr_b = mmap(&self->carveout[page_size + 3 * page_size], 3 * page_size, 1266 + PROT_READ | PROT_WRITE, 1267 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1268 + ASSERT_NE(ptr_b, MAP_FAILED); 1269 + 1270 + /* 1271 + * Now move VMA A into position with MREMAP_DONTUNMAP to catch incorrect 1272 + * anon_vma propagation. 1273 + */ 1274 + ptr_a = mremap(ptr_a, 3 * page_size, 3 * page_size, 1275 + MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 1276 + &self->carveout[page_size]); 1277 + ASSERT_NE(ptr_a, MAP_FAILED); 1278 + 1279 + /* The VMAs should have merged. */ 1280 + ASSERT_TRUE(find_vma_procmap(procmap, ptr_a)); 1281 + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a); 1282 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 6 * page_size); 1283 + } 1284 + 1285 + TEST_F(merge, mremap_faulted_to_unfaulted_prev_unfaulted_next) 1286 + { 1287 + struct procmap_fd *procmap = &self->procmap; 1288 + unsigned int page_size = self->page_size; 1289 + char *ptr_a, *ptr_b, *ptr_c; 1290 + 1291 + /* 1292 + * mremap() with MREMAP_DONTUNMAP such that A, B and C merge: 1293 + * 1294 + * |---------------------------| 1295 + * | \ | 1296 + * |-----------| | |-----------| / |---------| 1297 + * | unfaulted | v | unfaulted | \ | faulted | 1298 + * |-----------| |-----------| / |---------| 1299 + * A C \ B 1300 + */ 1301 + 1302 + /* Map VMA B into place. */ 1303 + ptr_b = mmap(&self->carveout[page_size + 3 * page_size], 3 * page_size, 1304 + PROT_READ | PROT_WRITE, 1305 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1306 + ASSERT_NE(ptr_b, MAP_FAILED); 1307 + /* Fault it in. */ 1308 + ptr_b[0] = 'x'; 1309 + 1310 + /* 1311 + * Now move it out of the way so we can place VMAs A, C in position, 1312 + * unfaulted. 1313 + */ 1314 + ptr_b = mremap(ptr_b, 3 * page_size, 3 * page_size, 1315 + MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]); 1316 + ASSERT_NE(ptr_b, MAP_FAILED); 1317 + 1318 + /* Map VMA A into place. */ 1319 + 1320 + ptr_a = mmap(&self->carveout[page_size], 3 * page_size, 1321 + PROT_READ | PROT_WRITE, 1322 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1323 + ASSERT_NE(ptr_a, MAP_FAILED); 1324 + 1325 + /* Map VMA C into place. */ 1326 + ptr_c = mmap(&self->carveout[page_size + 3 * page_size + 3 * page_size], 1327 + 3 * page_size, PROT_READ | PROT_WRITE, 1328 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1329 + ASSERT_NE(ptr_c, MAP_FAILED); 1330 + 1331 + /* 1332 + * Now move VMA B into position with MREMAP_DONTUNMAP to catch incorrect 1333 + * anon_vma propagation. 1334 + */ 1335 + ptr_b = mremap(ptr_b, 3 * page_size, 3 * page_size, 1336 + MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 1337 + &self->carveout[page_size + 3 * page_size]); 1338 + ASSERT_NE(ptr_b, MAP_FAILED); 1339 + 1340 + /* The VMAs should have merged. */ 1341 + ASSERT_TRUE(find_vma_procmap(procmap, ptr_a)); 1342 + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a); 1343 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size); 1344 + } 1345 + 1346 + TEST_F(merge, mremap_faulted_to_unfaulted_prev_faulted_next) 1347 + { 1348 + struct procmap_fd *procmap = &self->procmap; 1349 + unsigned int page_size = self->page_size; 1350 + char *ptr_a, *ptr_b, *ptr_bc; 1351 + 1352 + /* 1353 + * mremap() with MREMAP_DONTUNMAP such that A, B and C merge: 1354 + * 1355 + * |---------------------------| 1356 + * | \ | 1357 + * |-----------| | |-----------| / |---------| 1358 + * | unfaulted | v | faulted | \ | faulted | 1359 + * |-----------| |-----------| / |---------| 1360 + * A C \ B 1361 + */ 1362 + 1363 + /* 1364 + * Map VMA B and C into place. We have to map them together so their 1365 + * anon_vma is the same and the vma->vm_pgoff's are correctly aligned. 1366 + */ 1367 + ptr_bc = mmap(&self->carveout[page_size + 3 * page_size], 1368 + 3 * page_size + 3 * page_size, 1369 + PROT_READ | PROT_WRITE, 1370 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1371 + ASSERT_NE(ptr_bc, MAP_FAILED); 1372 + 1373 + /* Fault it in. */ 1374 + ptr_bc[0] = 'x'; 1375 + 1376 + /* 1377 + * Now move VMA B out the way (splitting VMA BC) so we can place VMA A 1378 + * in position, unfaulted, and leave the remainder of the VMA we just 1379 + * moved in place, faulted, as VMA C. 1380 + */ 1381 + ptr_b = mremap(ptr_bc, 3 * page_size, 3 * page_size, 1382 + MREMAP_FIXED | MREMAP_MAYMOVE, &self->carveout[20 * page_size]); 1383 + ASSERT_NE(ptr_b, MAP_FAILED); 1384 + 1385 + /* Map VMA A into place. */ 1386 + ptr_a = mmap(&self->carveout[page_size], 3 * page_size, 1387 + PROT_READ | PROT_WRITE, 1388 + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); 1389 + ASSERT_NE(ptr_a, MAP_FAILED); 1390 + 1391 + /* 1392 + * Now move VMA B into position with MREMAP_DONTUNMAP to catch incorrect 1393 + * anon_vma propagation. 1394 + */ 1395 + ptr_b = mremap(ptr_b, 3 * page_size, 3 * page_size, 1396 + MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 1397 + &self->carveout[page_size + 3 * page_size]); 1398 + ASSERT_NE(ptr_b, MAP_FAILED); 1399 + 1400 + /* The VMAs should have merged. */ 1401 + ASSERT_TRUE(find_vma_procmap(procmap, ptr_a)); 1402 + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a); 1403 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size); 1404 + } 1405 + 1174 1406 TEST_HARNESS_MAIN