6

Since the corresponding research papers offer quite explicit descriptions publicly, I suppose that publishing my code below is not considered as encouraging or endorsing exploits. Nevertheless, I am aware that some answerers may prefer to stay somewhat vague ...

I got confused by various tests whether or not my systems are not protected against the Spectre (and Meltdown) vulnerabilities after patching as patch can: They sometimes seemed to disagree or it was unclear whether an additional piece of software still needed yet another patch. Therefore, I intended to do my own testing by actually exploiting the vulnerabilities - in contrast to most of the said testing tools that rather read out several kinds of CPU and status information.

Consider the following code snippet ...

#define CACHELINESIZE   4096
typedef char line[CACHELINESIZE];
line A[512];

char secret = 'H';
char any = 'X';

#define REP     100
char* pcheck[REP];
line* pwrite[REP];
for (int i=0; i<REP; i++) {
  pcheck[i] = &any; 
  pwrite[i] = A; 
}
pcheck[REP-1] = &secret;
pwrite[REP-1] = A+256;
char dummy;
for (int i=0; i<REP; i++) {
  if (i != (REP-1)) {
    dummy = pwrite[i][*pcheck[i]][0];
  }
}

The if evaluates to true 99 times in a row (causing a line determined by other to be fetched to cache and read from) and then once to false. Thus I assume that in the last run, branch prediction is fooled into speculatively executing (but later abandoning) the assignment. In particular, a line determined by the content of secret is fetched into the cache, even though the content of secret is never "really" (i.e., non-speculatively) read.

After that, I time access to the various lines and expect a significantly shorter time for the line determined by the content of secret (i.e., reading from A[256+'H'] should be faster than accesses to other A[256+c]).

However, I consistently measure > 1000 cycles for all test accesses, whereas I would expect < 100 cycles for accessing an already cached line. (I could verify the shorter time by changing the if condition and making the access "real" instead of only speculative).

To me, this looks as if the Spectre vulnerability does not exist at all, i.e., speculative cache fetching does not happen!? But I specifically tried this on a system that does not yet have updated kernel/compiler/BIOS/firmware/microcode. At least that is what spectre-meltdown-checker.sh seems to confirm:

Spectre and Meltdown mitigation detection tool v0.29

Checking for vulnerabilities against running kernel Linux 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64
CPU is Intel(R) Celeron(R) CPU G530 @ 2.40GHz

CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'
* Checking count of LFENCE opcodes in kernel:  NO 
> STATUS:  VULNERABLE  (only 33 opcodes found, should be >= 70, heuristic to be improved when official patches become available)

CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'
* Mitigation 1
*   Hardware (CPU microcode) support for mitigation:  NO 
*   Kernel support for IBRS:  NO 
*   IBRS enabled for Kernel space:  NO 
*   IBRS enabled for User space:  NO 
* Mitigation 2
*   Kernel compiled with retpoline option:  NO 
*   Kernel compiled with a retpoline-aware compiler:  NO 
> STATUS:  VULNERABLE  (IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability)

CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'
* Kernel supports Page Table Isolation (PTI):  NO 
* PTI enabled and active:  NO 
> STATUS:  VULNERABLE  (PTI is needed to mitigate the vulnerability)

A false sense of security is worse than no security at all, see --disclaimer

So - ironically - that last line of this output seems to apply to my home-grown test: It fails to expose the vulnerability even though the system is vulnerable. Q: But why does my test fail this way? What is wrong with my argument above "proving" that it should expose the vulnerability?

Remark:, Of course, I compiled my code without optimization, so rest assured that the generated assembler matches the structure of the source (I verified this with objdump). Also, the above is only the most basic version of code I tried; in order to "encourage" speculation, I made the if depend on a relatively lengthy computation and the then-body consist of a single instruction (disassembled as

   imul   -0x66c(%rbp),%eax
   cmp    %eax,%edx
   je     4006d7 <main+0xf5>
   movzbq (%rdi),%rdi
4006d7: ...
Hagen von Eitzen
  • 1,098
  • 8
  • 19
  • 1
    Just curious.... did you try other sample Spectre exploits? For example: https://www.exploit-db.com/exploits/43427/. I was able to successfuly test with it on my AMD, however I had to adjust cache hit treshold – dev Jan 14 '18 at 14:48
  • 1
    @android_dev Nah, I thought the exploit sounds so simple that I should be able to build it from scratch :) I plan to take a look at existing proofs of concept, but am also curious what crucial point I might have missed. What I think I have found out so far is that maybe I'm "attacking" the wrong level of cache and/or not stalling the pipeline for long enough ... – Hagen von Eitzen Jan 14 '18 at 17:27
  • I looked at your pseudo code, I know I ask a question in a question, but shouldn't it be like this: https://0bin.net/paste/YTfG8iaSp3abZken#xJ+gjgF2VKeSk8FA1wxDhRVmDDacK1EYBUjBT+vBj2N Don't understand how suddenly pwrite becomes a 3 dimensional array in your pseudocode. Now, I would need to know how to access cache (in c or asm) and how to measure the access times. Do you have any code available you can share? – dev Jan 15 '18 at 09:38
  • @android_dev `pwrite[i]` is a `line*`, so `pwrite[i][...]` is a line, i..e, an array of `char`, so ? pwrite[i][...][0]` makes sense. - Timeing can be done with `t0 = __rdtsp (); dummy = A[i]; time_taken = __rdtscp() - t0;` The `time_taken` is shorter if `A[i]` was already cached. – Hagen von Eitzen Jan 15 '18 at 15:01
  • What CPU do you have? Not necessarily all out of order x86 CPUs are vulnerable. – Alex Cannon Feb 02 '18 at 00:08

1 Answers1

1

The reason your unpatched system appears not to be vulnerable to Spectre (and Meltdown) is that your processor is not affected¹ by the vulnerability. Your processor Intel(R) Celeron(R) CPU G530 is not on the list of affected products released by Intel [1]. The Spectre/Meltdown [2] website only states that "every Intel processor which implements out-of-order execution is potentially affected, which is effectively every processor since 1995 (except Intel Itanium and Intel Atom before 2013)" [2]. The Celeron G530 either does not implement out-of-order execution or is not affected by the exploit or vulnerability. Regarding the second case, it could be vulnerable but by cannot be exploited by your exploit or is not vulnerable in the (exact) way the paper describes it.

¹according to Intel

[1] https://security-center.intel.com/advisory.aspx?intelid=intel-sa-00088&languageid=en-fr
[2] https://meltdownattack.com/

sven.to
  • 586
  • 3
  • 5