You need to sign in or sign up before continuing.
Unverified Commit 3a504904 authored by Yossi Gottlieb's avatar Yossi Gottlieb Committed by GitHub
Browse files

Avoid assertions when testing arm64 cow bug. (#8405)

At least in one case the arm64 cow kernel bug test triggers an assert, which is a problem because it cannot be ignored like cases where the bug is found.

On older systems (Linux <4.5) madvise fails because MADV_FREE is not supported. We treat these failures as an indication the system is not affected.

Fixes #8351, #8406
parent 0367a808
...@@ -5200,7 +5200,7 @@ static int smapsGetSharedDirty(unsigned long addr) { ...@@ -5200,7 +5200,7 @@ static int smapsGetSharedDirty(unsigned long addr) {
FILE *f; FILE *f;
f = fopen("/proc/self/smaps", "r"); f = fopen("/proc/self/smaps", "r");
serverAssert(f); if (!f) return -1;
while (1) { while (1) {
if (!fgets(buf, sizeof(buf), f)) if (!fgets(buf, sizeof(buf), f))
...@@ -5211,8 +5211,8 @@ static int smapsGetSharedDirty(unsigned long addr) { ...@@ -5211,8 +5211,8 @@ static int smapsGetSharedDirty(unsigned long addr) {
in_mapping = from <= addr && addr < to; in_mapping = from <= addr && addr < to;
if (in_mapping && !memcmp(buf, "Shared_Dirty:", 13)) { if (in_mapping && !memcmp(buf, "Shared_Dirty:", 13)) {
ret = sscanf(buf, "%*s %d", &val); sscanf(buf, "%*s %d", &val);
serverAssert(ret == 1); /* If parsing fails, we remain with val == -1 */
break; break;
} }
} }
...@@ -5226,23 +5226,33 @@ static int smapsGetSharedDirty(unsigned long addr) { ...@@ -5226,23 +5226,33 @@ static int smapsGetSharedDirty(unsigned long addr) {
* kernel is affected. * kernel is affected.
* The bug was fixed in commit ff1712f953e27f0b0718762ec17d0adb15c9fd0b * The bug was fixed in commit ff1712f953e27f0b0718762ec17d0adb15c9fd0b
* titled: "arm64: pgtable: Ensure dirty bit is preserved across pte_wrprotect()" * titled: "arm64: pgtable: Ensure dirty bit is preserved across pte_wrprotect()"
* Return 1 if the kernel seems to be affected, and 0 otherwise. */ * Return -1 on unexpected test failure, 1 if the kernel seems to be affected,
* and 0 otherwise. */
int linuxMadvFreeForkBugCheck(void) { int linuxMadvFreeForkBugCheck(void) {
int ret, pipefd[2]; int ret, pipefd[2] = { -1, -1 };
pid_t pid; pid_t pid;
char *p, *q, bug_found = 0; char *p = NULL, *q;
const long map_size = 3 * 4096; int bug_found = 0;
long page_size = sysconf(_SC_PAGESIZE);
long map_size = 3 * page_size;
/* Create a memory map that's in our full control (not one used by the allocator). */ /* Create a memory map that's in our full control (not one used by the allocator). */
p = mmap(NULL, map_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); p = mmap(NULL, map_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
serverAssert(p != MAP_FAILED); if (p == MAP_FAILED) {
serverLog(LL_WARNING, "Failed to mmap(): %s", strerror(errno));
return -1;
}
q = p + 4096; q = p + page_size;
/* Split the memory map in 3 pages by setting their protection as RO|RW|RO to prevent /* Split the memory map in 3 pages by setting their protection as RO|RW|RO to prevent
* Linux from merging this memory map with adjacent VMAs. */ * Linux from merging this memory map with adjacent VMAs. */
ret = mprotect(q, 4096, PROT_READ | PROT_WRITE); ret = mprotect(q, page_size, PROT_READ | PROT_WRITE);
serverAssert(!ret); if (ret < 0) {
serverLog(LL_WARNING, "Failed to mprotect(): %s", strerror(errno));
bug_found = -1;
goto exit;
}
/* Write to the page once to make it resident */ /* Write to the page once to make it resident */
*(volatile char*)q = 0; *(volatile char*)q = 0;
...@@ -5251,8 +5261,16 @@ int linuxMadvFreeForkBugCheck(void) { ...@@ -5251,8 +5261,16 @@ int linuxMadvFreeForkBugCheck(void) {
#ifndef MADV_FREE #ifndef MADV_FREE
#define MADV_FREE 8 #define MADV_FREE 8
#endif #endif
ret = madvise(q, 4096, MADV_FREE); ret = madvise(q, page_size, MADV_FREE);
serverAssert(!ret); if (ret < 0) {
/* MADV_FREE is not available on older kernels that are presumably
* not affected. */
if (errno == EINVAL) goto exit;
serverLog(LL_WARNING, "Failed to madvise(): %s", strerror(errno));
bug_found = -1;
goto exit;
}
/* Write to the page after being marked for freeing, this is supposed to take /* Write to the page after being marked for freeing, this is supposed to take
* ownership of that page again. */ * ownership of that page again. */
...@@ -5260,37 +5278,47 @@ int linuxMadvFreeForkBugCheck(void) { ...@@ -5260,37 +5278,47 @@ int linuxMadvFreeForkBugCheck(void) {
/* Create a pipe for the child to return the info to the parent. */ /* Create a pipe for the child to return the info to the parent. */
ret = pipe(pipefd); ret = pipe(pipefd);
serverAssert(!ret); if (ret < 0) {
serverLog(LL_WARNING, "Failed to create pipe: %s", strerror(errno));
bug_found = -1;
goto exit;
}
/* Fork the process. */ /* Fork the process. */
pid = fork(); pid = fork();
serverAssert(pid >= 0); if (pid < 0) {
if (!pid) { serverLog(LL_WARNING, "Failed to fork: %s", strerror(errno));
/* Child: check if the page is marked as dirty, expecing 4 (kB). bug_found = -1;
goto exit;
} else if (!pid) {
/* Child: check if the page is marked as dirty, page_size in kb.
* A value of 0 means the kernel is affected by the bug. */ * A value of 0 means the kernel is affected by the bug. */
if (!smapsGetSharedDirty((unsigned long)q)) ret = smapsGetSharedDirty((unsigned long) q);
if (!ret)
bug_found = 1; bug_found = 1;
else if (ret == -1) /* Failed to read */
bug_found = -1;
ret = write(pipefd[1], &bug_found, 1); if (write(pipefd[1], &bug_found, sizeof(bug_found)) < 0)
serverAssert(ret == 1); serverLog(LL_WARNING, "Failed to write to parent: %s", strerror(errno));
exit(0); exit(0);
} else { } else {
/* Read the result from the child. */ /* Read the result from the child. */
ret = read(pipefd[0], &bug_found, 1); ret = read(pipefd[0], &bug_found, sizeof(bug_found));
serverAssert(ret == 1); if (ret < 0) {
serverLog(LL_WARNING, "Failed to read from child: %s", strerror(errno));
bug_found = -1;
}
/* Reap the child pid. */ /* Reap the child pid. */
serverAssert(waitpid(pid, NULL, 0) == pid); waitpid(pid, NULL, 0);
} }
exit:
/* Cleanup */ /* Cleanup */
ret = close(pipefd[0]); if (pipefd[0] != -1) close(pipefd[0]);
serverAssert(!ret); if (pipefd[1] != -1) close(pipefd[1]);
ret = close(pipefd[1]); if (p != NULL) munmap(p, map_size);
serverAssert(!ret);
ret = munmap(p, map_size);
serverAssert(!ret);
return bug_found; return bug_found;
} }
...@@ -5901,10 +5929,17 @@ int main(int argc, char **argv) { ...@@ -5901,10 +5929,17 @@ int main(int argc, char **argv) {
#ifdef __linux__ #ifdef __linux__
linuxMemoryWarnings(); linuxMemoryWarnings();
#if defined (__arm64__) #if defined (__arm64__)
if (linuxMadvFreeForkBugCheck()) { int ret;
serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. Please upgrade to the latest stable kernel."); if ((ret = linuxMadvFreeForkBugCheck())) {
if (ret == 1)
serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. "
"Please upgrade to the latest stable kernel.");
else
serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
"Your system could be affected, please report this error.");
if (!checkIgnoreWarning("ARM64-COW-BUG")) { if (!checkIgnoreWarning("ARM64-COW-BUG")) {
serverLog(LL_WARNING,"Redis will now exit to prevent data corruption. Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG"); serverLog(LL_WARNING,"Redis will now exit to prevent data corruption. "
"Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
exit(1); exit(1);
} }
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment