Older arm64 Linux kernels have a bug that could lead to data corruption * during background save in certain scenarios. This function checks if the * kernel is affected. * The bug was fixed in commit ff1712f953e27f0b0718762ec17d0adb15c9fd0b * titled: "arm64: pgtable: Ensure dirty bit is preserved across pte_wrprotect()" * Return -1 on unexpected test failure, 1 if the kernel seems to be affect
| 5460 | * Return -1 on unexpected test failure, 1 if the kernel seems to be affected, |
| 5461 | * and 0 otherwise. */ |
| 5462 | int linuxMadvFreeForkBugCheck(void) { |
| 5463 | int ret, pipefd[2] = { -1, -1 }; |
| 5464 | pid_t pid; |
| 5465 | char *p = NULL, *q; |
| 5466 | int bug_found = 0; |
| 5467 | long page_size = sysconf(_SC_PAGESIZE); |
| 5468 | long map_size = 3 * page_size; |
| 5469 | |
| 5470 | /* Create a memory map that's in our full control (not one used by the allocator). */ |
| 5471 | p = mmap(NULL, map_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
| 5472 | if (p == MAP_FAILED) { |
| 5473 | serverLog(LL_WARNING, "Failed to mmap(): %s", strerror(errno)); |
| 5474 | return -1; |
| 5475 | } |
| 5476 | |
| 5477 | q = p + page_size; |
| 5478 | |
| 5479 | /* Split the memory map in 3 pages by setting their protection as RO|RW|RO to prevent |
| 5480 | * Linux from merging this memory map with adjacent VMAs. */ |
| 5481 | ret = mprotect(q, page_size, PROT_READ | PROT_WRITE); |
| 5482 | if (ret < 0) { |
| 5483 | serverLog(LL_WARNING, "Failed to mprotect(): %s", strerror(errno)); |
| 5484 | bug_found = -1; |
| 5485 | goto exit; |
| 5486 | } |
| 5487 | |
| 5488 | /* Write to the page once to make it resident */ |
| 5489 | *(volatile char*)q = 0; |
| 5490 | |
| 5491 | /* Tell the kernel that this page is free to be reclaimed. */ |
| 5492 | #ifndef MADV_FREE |
| 5493 | #define MADV_FREE 8 |
| 5494 | #endif |
| 5495 | ret = madvise(q, page_size, MADV_FREE); |
| 5496 | if (ret < 0) { |
| 5497 | /* MADV_FREE is not available on older kernels that are presumably |
| 5498 | * not affected. */ |
| 5499 | if (errno == EINVAL) goto exit; |
| 5500 | |
| 5501 | serverLog(LL_WARNING, "Failed to madvise(): %s", strerror(errno)); |
| 5502 | bug_found = -1; |
| 5503 | goto exit; |
| 5504 | } |
| 5505 | |
| 5506 | /* Write to the page after being marked for freeing, this is supposed to take |
| 5507 | * ownership of that page again. */ |
| 5508 | *(volatile char*)q = 0; |
| 5509 | |
| 5510 | /* Create a pipe for the child to return the info to the parent. */ |
| 5511 | ret = pipe(pipefd); |
| 5512 | if (ret < 0) { |
| 5513 | serverLog(LL_WARNING, "Failed to create pipe: %s", strerror(errno)); |
| 5514 | bug_found = -1; |
| 5515 | goto exit; |
| 5516 | } |
| 5517 | |
| 5518 | /* Fork the process. */ |
| 5519 | pid = fork(); |