Spectre logo

Spectre variant 1 detection and remediation using static code analysis

on May 2, 18 • by Roy Sarkar • with No Comments

How Klocwork static code analysis detects the Spectre variant 1 vulnerability, including a code walkthrough of how the exploit and checker work...

Home » Featured, Static Analysis » Spectre variant 1 detection and remediation using static code analysis

The Spectre vulnerability has dominated cybersecurity headlines since Google announced it earlier this year, and we first reported back in January how to patch Windows and Linux systems. Most stories are about its nature and potential prevalence, rather than solving the fundamental problem. But now, we have a solution. We’re happy to announce the release of a new Klocwork checker, SPECTRE.VARIANT1, that detects potential occurrences of Spectre variant 1 (CVE-2017-5753) in your code.

In this post, we’ll review what the Spectre variant 1 vulnerability is, describe why we focused on this variant, explain how the new checker works, and provide options for mitigation.

What is Spectre?

There are many articles describing Spectre in detail but, for a quick understanding, here’s an explanation.

Quick summary: The Spectre vulnerability involves a malicious program gaining access to data that it shouldn’t have the right to see. It does so by exploiting two important techniques used to speed up computer chips, called speculative execution and caching.

To skip the detailed explanation, you can jump to “What Klocwork does” below.

To exploit Spectre, an attacker relies on two things occurring on the target system: The hardware must make use of speculative execution; and certain patterns must be present in the code.

Speculative execution: For performance purposes, a processor may execute instructions past a branch (such as an if statement) before knowing whether those instructions should be executed. If the speculation proves to be correct, valuable CPU time is saved. If the speculation is incorrect, the CPU can discard the results and continue execution along the correct path.

Spectre relies on the fact that this speculation can result in modifications to cache memory that may include “incorrect” data, or data that wouldn’t normally result if instructions had executed along the correct path. Attackers may be able to extract this data using a timing, or side-channel, attack whereby memory access latency is measured to determine the cache location of sensitive information, which can then be retrieved. We’ll provide an example of this later.

Why Spectre variant 1?

Based on researching the vulnerability and strong customer feedback, the Klocwork development team focused on Spectre variant 1 for two reasons. First, Intel and other organizations have already released mitigation strategies for the other variants. Second, unlike these other strategies, there’s no “one-shot” mitigation for variant 1 in the field. In other words, variant 1 can’t currently be solved by a single patch or technique – it requires the detection and resolution of multiple, specific code patterns in source code. That’s where static code analysis comes in.

Of course, you could take the brute-force approach and turn off speculative execution entirely, but this would lead to significant performance degradations in your application.

How Spectre works

To best understand how the Klocwork Spectre checker works, we’ll walk through this example, derived from the Spectre white paper (PDF):

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23
struct array {
    unsigned long length;
    unsigned char data[];
};

struct array *createArrayOfSize(unsigned int size); 

void vulnerable_pattern(unsigned long offset)
{
    struct array *arr1 = createArrayOfSize(5);                  /* small array */
    struct array *arr2 = createArrayOfSize(0x500);              /* a large array */
    unsigned char secret_value;

    if (offset < arr1->length) {                                /* untrusted data controls the branch */
        secret_value = arr1->data[offset];                      /* out of boundary read in a mispredicted speculative execution */

        unsigned char value2 = arr2->data[secret_value * 256];  /* arr2->data[secret_value * 256] will be loaded to the CPU cache */

        /* some more code */
    } else {
        /* code for the else branch */
    }
}

 

Focusing on lines 14 and 15 above, the code looks generally secure as the execution of secret_value = arr1->data[offset] is protected by the bounds check on line 14. However, due to the possibility of speculative execution, the protection of this bounds check can be bypassed if these conditions exist:

• The protected data resides in an out-of-boundary memory area of arr1->data
• The protected data has the potential to be cached in a CPU register due to speculative execution
• The offset variable is also in the CPU cache due to speculative execution

If these conditions are satisfied, the exploit proceeds as follows:

1. On line 14, offset is compared with arr1->length
2. Given offset is a cache hit and arr1->length is a cache miss, the CPU requests the value of arr1->length to be loaded into the L1 cache
3. As it may take several hundred CPU cycles to load the value, the CPU may start executing line 15 speculatively
4. On line 15, the value of offset (assuming it’s been maliciously set by an attacker through a timing attack) points to the protected data and the value is read as a cache hit, becoming immediately available and stored in secret_value
5. On line 17, given the read arr2->data is a cache miss, the CPU requests the value be loaded from the next level cache (or DRAM)
6. The value of arr1->length finally arrives in L1 cache, the CPU realizes the branch was mis-predicted, and execution starts along the else branch instead
7. The value of arr2->data[secret_value * 256] now arrives in the L1 cache, exposed to an attacker via a timing attack

What Klocwork does

Fundamentally, Klocwork tracks any tainted (untrusted or otherwise suspicious) data along possible execution paths in code. The Klocwork SPECTRE.VARIANT1 checker does two things: It flags any possible instances of tainted data (in this case, the input parameter offset in the above code sample) and identifies suspect code patterns where they are used (in this case, lines 14 – 15 in the code sample due to the possibility of speculative execution), providing this finding:

There are several other patterns that Klocwork detects and it’s important to note that Spectre vulnerabilities can be reported even in code with no buffer overflows (found with Klocwork ABV.* checkers), a very similar type of attack, as it’s the presence of speculative execution that triggers the issue.

Remediation guidance

Once Klocwork identifies potentially vulnerable sections of code, there are two ways to remediate the exploit:

• Follow Intel’s recommendation to insert a barrier to stop speculative execution locally using the LFENCE instruction ( _mm_lfence()). More details about this mitigation are available in Intel’s white paper.
• If you have your own mitigation function, you can include a custom KB in the Klocwork analysis, identifying to the checker areas of code that you know aren’t vulnerable to the exploit. Instructions on how to include a custom KB are here.

The SPECTRE.VARIANT1 checker is available now for Klocwork 2018 and Klocwork 2017.3.

For those interested in detecting and mitigating the Spectre vulnerability as soon as possible, you can try a free trial of Klocwork now.

We also offer a Spectre audit of your code, where our professional services team analyzes your code base, identifies potential vulnerabilities, and provides remediation guidance tailored to your team.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top