mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
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
This commit is contained in:
parent
0367a80819
commit
3a5049042a
101
src/server.c
101
src/server.c
@ -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) {
|
|||||||
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) {
|
|||||||
* 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) {
|
|||||||
#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) {
|
|||||||
|
|
||||||
/* 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) {
|
|||||||
#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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user