C++투기적 실행 쪽 채널에 대 한 개발자 지침C++ Developer Guidance for Speculative Execution Side Channels

이 문서에는 식별 하 고에서 측면 채널 하드웨어 취약성 투기적 실행 완화에 도움이 되는 개발자를 위한 지침이 포함 되어 있습니다. C++ 소프트웨어입니다.This article contains guidance for developers to assist with identifying and mitigating speculative execution side channel hardware vulnerabilities in C++ software. 이러한 취약성 트러스트 경계에 걸쳐 중요 한 정보를 공개할 수 및 명령의 추측, 순서가의 실행을 지 원하는 프로세서에서 실행 되는 소프트웨어에 영향을 줄 수 있습니다.These vulnerabilities can disclose sensitive information across trust boundaries and can affect software that runs on processors that support speculative, out-of-order execution of instructions. 이 클래스의 취약점으로 인 한이 2018 년 1 월에서에서 설명 하는 첫 번째 및 추가 배경 및에서 지침을 찾을 수 있습니다 Microsoft 보안 권고합니다.This class of vulnerabilities was first described in January, 2018 and additional background and guidance can be found in Microsoft's security advisory.

이 문서에서 제공 하는 지침으로 표시 하는 취약성의 클래스는 관련이 있습니다.The guidance provided by this article is related to the classes of vulnerabilities represented by:

  1. CVE-2017-5753, Spectre variant 1 라고도 합니다.CVE-2017-5753, also known as Spectre variant 1. 이 하드웨어 취약점으로 인 한 클래스는 조건부 분기 오측의 결과로 발생 하는 투기적 실행으로 인해 발생할 수 있는 쪽 채널 관련이 있습니다.This hardware vulnerability class is related to side channels that can arise due to speculative execution that occurs as a result of a conditional branch misprediction. Microsoft C++ Visual Studio 2017 (버전 15.5.5부터 시작)에서 컴파일러에 대 한 지원을 포함 합니다 /Qspectre CVE 2017-5753에 관련 된 잠재적으로 취약 한 코딩 패턴의 제한 된 집합에 대 한 컴파일 시간 완화 조치를 제공 하는 스위치입니다.The Microsoft C++ compiler in Visual Studio 2017 (starting with version 15.5.5) includes support for the /Qspectre switch which provides a compile-time mitigation for a limited set of potentially vulnerable coding patterns related to CVE-2017-5753. 합니다 /Qspectre 스위치를 통해 Visual Studio 2015 업데이트 3에서 사용할 수 있는 이기도 KB 4338871합니다.The /Qspectre switch is also available in Visual Studio 2015 Update 3 through KB 4338871. 에 대 한 설명서는 /Qspectre 플래그 효과 및 사용에 자세한 정보를 제공 합니다.The documentation for the /Qspectre flag provides more information on its effects and usage.

  2. CVE-2018-3639, 라고도 잘못 된 저장소 사용 안 함 (SSB)합니다.CVE-2018-3639, also known as Speculative Store Bypass (SSB). 이 하드웨어 취약점으로 인 한 클래스는 메모리 액세스 오측 결과로 종속 저장소를 미리 로드의 투기적 실행으로 인해 발생할 수 있는 쪽 채널 관련이 있습니다.This hardware vulnerability class is related to side channels that can arise due to speculative execution of a load ahead of a dependent store as a result of a memory access misprediction.

투기적 실행 쪽 채널 취약성에 액세스할 수 있는 소개 라는 프레젠테이션에서 찾을 수 있습니다 The 사례 스펙터와 멜트다운 이러한 문제를 검색 하는 연구 팀 중 하나에서.An accessible introduction to speculative execution side channel vulnerabilities can be found in the presentation titled The Case of Spectre and Meltdown by one of the research teams that discovered these issues.

잘못 된 실행 쪽 채널 하드웨어 취약성은 무엇 인가요?What are Speculative Execution Side Channel hardware vulnerabilities?

최신 Cpu 만들어 높은 수준의 성능 제공 활용 한 추측 및 순서가 명령 실행 합니다.Modern CPUs provide higher degrees of performance by making use of speculative and out-of-order execution of instructions. 예를 들어,이 경우가 많습니다 추측을 통해 예측 된 분기 대상에 있는 지침을 실행 하려면 CPU 수 있도록 하는 대상 분기 (조건부 및 간접)를 예측 하 여 실제 분기 대상이 될 때까지 정지 방지 해결 합니다.For example, this is often accomplished by predicting the target of branches (conditional and indirect) which enables the CPU to begin speculatively executing instructions at the predicted branch target, thus avoiding a stall until the actual branch target is resolved. 이벤트의 CPU를 오측 발생 했음을 나중에 검색 항목을 추측 계산 된 컴퓨터 상태가 모두 삭제 됩니다.In the event that the CPU later discovers that a misprediction occurred, all of the machine state that was computed speculatively is discarded. 이렇게 하면 오측된 추론의 아키텍처 측면에서 볼 수 없는 효과 합니다.This ensures that there are no architecturally visible effects of the mispredicted speculation.

투기적 실행을 구조적으로 표시 되는 상태는 영향을 주지 않습니다, 하지만 잔여 추적 CPU에서 사용 되는 다양 한 캐시 같은 아키텍처 되지 않은 상태로 그대로 둘 수 있습니다.While speculative execution does not affect the architecturally visible state, it can leave residual traces in non-architectural state, such as the various caches that are used by the CPU. 이러한 잔여 추적 쪽 채널 취약성을 증가 제공할 수 있는 투기적 실행의 경우It is these residual traces of speculative execution that can give rise to side channel vulnerabilities. 이 더 잘 이해 하려면 CVE-2017-5753 (범위 확인 사용 안 함)의 예제를 제공 하는 다음 코드 조각은 고려 합니다.To better understand this, consider the following code fragment which provides an example of CVE-2017-5753 (Bounds Check Bypass):

// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;

unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}

이 예제에서는 ReadByte 가 해당 버퍼에 버퍼, 버퍼 크기와 인덱스를 제공 합니다.In this example, ReadByte is supplied a buffer, a buffer size, and an index into that buffer. 인덱스 매개 변수에서 지정한 대로 untrusted_index, less에서 제공 하는 관리자가 아닌 프로세스와 같은 권한 있는 컨텍스트.The index parameter, as specified by untrusted_index, is supplied by a less privileged context, such as a non-administrative process. 하는 경우 untrusted_index 는 보다 작은 buffer_size에서 해당 인덱스에 있는 문자를 읽은 다음 buffer 공유에서 참조 하는 메모리 영역에 인덱스를 사용 하 고 shared_buffer합니다.If untrusted_index is less than buffer_size, then the character at that index is read from buffer and used to index into a shared region of memory referred to by shared_buffer.

아키텍처 관점에서이 코드 시퀀스는 안전 하다는 보장이 untrusted_index 은 항상 미만 buffer_size합니다.From an architectural perspective, this code sequence is perfectly safe as it is guaranteed that untrusted_index will always be less than buffer_size. 그러나 투기적 실행 시 있기 CPU는 조건부 분기 예측 실패 하 고 if의 본문을 실행는 문의 경우에 untrusted_index 보다 크거나 같음 buffer_size합니다.However, in the presence of speculative execution, it is possible that the CPU will mispredict the conditional branch and execute the body of the if statement even when untrusted_index is greater than or equal to buffer_size. 따라서 CPU의 범위에서 일어난 바이트 읽을 추측 수 있습니다 buffer (하는 암호를 사용 가능)를 통해 후속 부하의 주소를 계산 하는 바이트 값을 사용할 수 없습니다 및 shared_buffer합니다.As a consequence of this, the CPU may speculatively read a byte from beyond the bounds of buffer (which could be a secret) and could then use that byte value to compute the address of a subsequent load through shared_buffer.

의도 하지 않은 남아 있는 CPU이이 오측 감지 최종적으로 동안 경계에서 읽은 바이트 값에 대 한 정보를 표시 하는 CPU 캐시에 남아 있을 수 있습니다 buffer합니다.While the CPU will eventually detect this misprediction, residual side effects may be left in the CPU cache that reveal information about the byte value that was read out of bounds from buffer. 이러한 부작용 작음 하 여 검색할 수에서 얼마나 신속 하 게 검색 하 여 시스템에서 실행 되는 권한 있는 컨텍스트 각 캐시 줄 shared_buffer 액세스.These side effects can be detected by a less privileged context running on the system by probing how quickly each cache line in shared_buffer is accessed. 이 위해 수행할 수 있는 단계를 다음과 같습니다.The steps that can be taken to accomplish this are:

  1. 호출할 ReadByte 으로 여러 번 untrusted_index 되 미만 buffer_size 합니다.Invoke ReadByte multiple times with untrusted_index being less than buffer_size. 공격 컨텍스트를 호출 하 여 컨텍스트를 발생할 수 있습니다 ReadByte (예: 통해 RPC)으로 not 수행 되도록 학습 분기 predictor는 되도록 untrusted_index 는 보다 작은 buffer_size합니다.The attacking context can cause the victim context to invoke ReadByte (e.g. via RPC) such that the branch predictor is trained to be not-taken as untrusted_index is less than buffer_size.

  2. 모든 캐시 라인 플러시 shared_buffer 합니다.Flush all cache lines in shared_buffer. 공격 컨텍스트 공유 지역에서 참조 하는 메모리에 캐시 줄을 모두 플러시해야 shared_buffer합니다.The attacking context must flush all of the cache lines in the shared region of memory referred to by shared_buffer. 메모리 영역을 공유 하므로이 간단 하며 같은 내장 함수를 사용 하 여 수행할 수 있습니다 _mm_clflush합니다.Since the memory region is shared, this is straightforward and can be accomplished using intrinsics such as _mm_clflush.

  3. 호출할 ReadByte 사용 하 여 untrusted_index 크거나 buffer_size 합니다.Invoke ReadByte with untrusted_index being greater than buffer_size. 공격 컨텍스트를 호출 하 여 컨텍스트를 하면 ReadByte 는 올바르게 예측 하지 분기 수행 되지 것입니다.The attacking context causes the victim context to invoke ReadByte such that it incorrectly predicts that the branch will not be taken. 블록 프로세서가 추측 경우 본문을 실행 하는이 원인 untrusted_index 보다 큰지 buffer_size범위를 벗어나는 읽기에 따라서 선행의 buffer합니다.This causes the processor to speculatively execute the body of the if block with untrusted_index being greater than buffer_size, thus leading to an out-of-bounds read of buffer. 따라서 shared_buffer 읽은 범위를 벗어나는, CPU에서 로드할 각 캐시 라인이 발생 하는 잠재적으로 비밀 값을 사용 하 여 인덱싱됩니다.Consequently, shared_buffer is indexed using a potentially secret value that was read out-of-bounds, thus causing the respective cache line to be loaded by the CPU.

  4. 각 캐시 줄 읽기 shared_buffer 가장 빠르게 액세스 보려는합니다.Read each cache line in shared_buffer to see which is accessed most quickly. 공격 컨텍스트는 각 캐시 라인에 읽을 수 shared_buffer 하 고 다른 항목 보다 훨씬 더 빠르게 로드 하는 캐시 라인을 검색 합니다.The attacking context can read each cache line in shared_buffer and detect the cache line that loads significantly faster than the others. 3 단계 가져온 후 발생할 수 있는 캐시 라인입니다.This is the cache line that is likely to have been brought in by step 3. 이 예에서 바이트 값 및 캐시 줄 사이 1:1 관계 이므로 이렇게 하면 공격자가 범위를 벗어나는 읽은 바이트의 실제 값을 유추할 수 있습니다.Since there is a 1:1 relationship between byte value and cache line in this example, this allows the attacker to infer the actual value of the byte that was read out-of-bounds.

위의 단계를 플러시 + 다시 로드 CVE 2017-5753 인스턴스의 악용 함께에서 이라는 기술을 사용 하는 예제를 제공 합니다.The above steps provide an example of using a technique known as FLUSH+RELOAD in conjunction with exploiting an instance of CVE-2017-5753.

어떤 소프트웨어 시나리오에 영향이 있을 수 있습니다.What software scenarios can be impacted?

과 같은 프로세스를 사용 하 여 보안 소프트웨어를 개발 합니다 Security Development Lifecycle (SDL)에 일반적으로 응용 프로그램에 존재 하는 신뢰 경계를 식별 하는 개발자가 필요 합니다.Developing secure software using a process like the Security Development Lifecycle (SDL) typically requires developers to identify the trust boundaries that exist in their application. 트러스트 경계 경우 시스템에서 다른 프로세스 또는 커널 모드 장치 드라이버의 경우 관리자가 아닌 사용자 모드 프로세스와 같은 신뢰할 수 없는 context에서 제공 하는 데이터와 응용 프로그램 상호 작용할 수 있습니다 위치에 있습니다.A trust boundary exists in places where an application may interact with data provided by a less-trusted context, such as another process on the system or a non-administrative user mode process in the case of a kernel-mode device driver. 투기적 실행 쪽 채널 관련 된 취약점의 새 클래스 코드 및 장치에서 데이터를 격리 하는 기존 소프트웨어 보안 모델의 트러스트 경계 중 많은 부분에 관련이 있습니다.The new class of vulnerabilities involving speculative execution side channels is relevant to many of the trust boundaries in existing software security models that isolate code and data on a device.

다음 표에서 발생 하는 이러한 취약성에 대 한 고려 개발자가 해야 할 수 있는 소프트웨어 보안 모델의 요약을 제공 합니다.The following table provides a summary of the software security models where developers may need to be concerned about these vulnerabilities occurring:

신뢰 경계Trust boundary 설명Description
가상 컴퓨터 경계Virtual machine boundary 다른 가상 컴퓨터에서 신뢰할 수 없는 데이터를 수신 하는 별도 virtual machines에서 작업을 격리 하는 응용 프로그램은 위험할 수 있습니다.Applications that isolate workloads in separate virtual machines that receive untrusted data from another virtual machine may be at risk.
커널 경계Kernel boundary 관리자가 아닌 사용자 모드 프로세스에서 신뢰할 수 없는 데이터를 수신 하는 커널 모드 장치 드라이버는 위험할 수 있습니다.A kernel-mode device driver that receives untrusted data from a non-administrative user mode process may be at risk.
프로세스 경계Process boundary 위험에 노출 될 수 있습니다 메커니즘을 원격 프로시저 호출 (RPC), 공유 메모리 또는 다른 프로세스 간 통신 (IPC)을 통해 같은 로컬 시스템에서 실행 중인 다른 프로세스에서 신뢰할 수 없는 데이터를 수신 하는 응용 프로그램입니다.An application that receives untrusted data from another process running on the local system, such as through a Remote Procedure Call (RPC), shared memory, or other Inter-Process Communication (IPC) mechanisms may be at risk.
Enclave 경계Enclave boundary Enclave 외부에서 신뢰할 수 없는 데이터를 수신 하는 (예: Intel SGX) 보안 enclave 내에서 실행 하는 응용 프로그램은 위험할 수 있습니다.An application that executes within a secure enclave (such as Intel SGX) that receives untrusted data from outside of the enclave may be at risk.
언어의 경계Language boundary 해석 하는 응용 프로그램 또는 jit (JUST-IN-TIME) 컴파일 및 작성 하는 신뢰할 수 없는 코드를 실행 합니다. 상위 수준 언어를 위험에 노출 될 수 있습니다.An application that interprets or Just-In-Time (JIT) compiles and executes untrusted code written in a higher-level language may be at risk.

응용 프로그램을 공격 노출 하려면 위 신뢰 경계에서 공격 노출 영역을 파악 및 완화할 수 인스턴스의 투기적 실행 쪽 채널 취약성에 대 한 코드를 검토 해야 합니다.Applications that have attack surface exposed to any of the above trust boundaries should review code on the attack surface to identify and mitigate possible instances of speculative execution side channel vulnerabilities. 원격 네트워크 프로토콜과 같은 원격 공격에 노출 하는 신뢰 경계 투기적 실행 쪽 채널 취약성에 노출 되도록 설명 하지는 점에 유의 해야 합니다.It should be noted that trust boundaries exposed to remote attack surfaces, such as remote network protocols, have not been demonstrated to be at risk to speculative execution side channel vulnerabilities.

잠재적으로 취약 한 코딩 패턴Potentially vulnerable coding patterns

투기적 실행 쪽 채널 취약성은 여러 코딩 패턴의 결과로 발생할 수 있습니다.Speculative execution side channel vulnerabilities can arise as a consequence of multiple coding patterns. 이 섹션에서는 잠재적으로 취약 한 코딩 패턴에 설명 하 고 각각에 대 한 예제를 제공 하지만 이러한 테마에서 각기 다르게 있을 인식 되도록 해야 합니다.This section describes potentially vulnerable coding patterns and provides examples for each, but it should be recognized that variations on these themes may exist. 따라서 개발자가 모든 잠재적으로 취약 한 코딩 패턴의 전체 목록을 아니라 예제로 이러한 패턴을 사용 하는 것이 좋습니다.As such, developers are advised to take these patterns as examples and not as an exhaustive list of all potentially vulnerable coding patterns. 지금 소프트웨어에 존재할 수 있는 메모리 안전성 취약점의 동일한 클래스 투기적 함께 존재할 수도 있습니다 및 버퍼 오버런에 국한 되지 않음 등 실행의 순서가 경로 범위를 벗어나는 액세스, 초기화 되지 않은 메모리 사용 형식 배열 혼동을 및 등입니다.The same classes of memory safety vulnerabilities that can exist in software today may also exist along speculative and out-of-order paths of execution, including but not limited to buffer overruns, out-of-bounds array accesses, uninitialized memory use, type confusion, and so on. 공격자가 아키텍처 경로 따라 메모리 안전성 취약점을 악용 하는 데 사용할 수 있는 동일한 기본 형식을 잘못 된 경로에 적용할 수 있습니다.The same primitives that attackers can use to exploit memory safety vulnerabilities along architectural paths may also apply to speculative paths.

일반적으로 투기적 실행 쪽 채널 조건부 관련이 분기 오측 조건식 제어 하거나 신뢰할 수 없는 상황에 맞는 영향 수 있는 데이터에 작동 하는 경우 발생할 수 있습니다.In general, speculative execution side channels related to conditional branch misprediction can arise when a conditional expression operates on data that can be controlled or influenced by a less-trusted context. 예를 들어,이에서 사용 되는 조건식 if, for, while, switch, 3 개로 구성 된 문 또는 합니다.For example, this can include conditional expressions used in if, for, while, switch, or ternary statements. 이러한 문은 각각에 대 한 컴파일러는 CPU 수 다음 런타임 시에 대 한 분기 대상이 예측 하는 조건부 분기를 생성할 수 있습니다.For each of these statements, the compiler may generate a conditional branch that the CPU may then predict the branch target for at runtime.

각 예를 들어 개발자는 장벽 문제 완화를 얻는데 이러한 여기서 "추론 장벽을" 라는 문구를 사용 하 여 주석을 삽입 됩니다.For each example, a comment with the phrase "SPECULATION BARRIER" is inserted where a developer could introduce a barrier as a mitigation. 완화에 섹션에서 자세히 설명 되어 있습니다.This is discussed in more detail in the section on mitigations.

잘못 된 범위를 벗어나는 로드Speculative out-of-bounds load

이 범주의 코딩 패턴을 발생 시키는 잘못 된 범위를 벗어나는 조건부 분기 오측은 메모리 액세스 합니다.This category of coding patterns involves a conditional branch misprediction that leads to a speculative out-of-bounds memory access.

배열 범위를 벗어나는 로드 부하를 제공 합니다.Array out-of-bounds load feeding a load

이 코딩 패턴에는 CVE-2017-5753 (범위 확인 사용 안 함)에 대 한 원래 설명된 취약 한 코딩 패턴이입니다.This coding pattern is the originally described vulnerable coding pattern for CVE-2017-5753 (Bounds Check Bypass). 이 문서의 백그라운드 섹션에이 패턴을 자세히 설명합니다.The background section of this article explains this pattern in detail.

// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;

unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        // SPECULATION BARRIER
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}

마찬가지로 부하의 종료를 초과 하는 루프와 함께에서 나타날 수 있습니다 배열 범위를 벗어나는 오측 인해 조건입니다.Similarly, an array out-of-bounds load may occur in conjunction with a loop that exceeds its terminating condition due to a misprediction. 이 예에서 조건부 분기와 연결 합니다 x < buffer_size 식 예측 실패 하 고 추측의 본문을 실행할 수 있습니다를 for 때 루프 x 보다 크거나 같음 buffer_size, 따라서 결과 잘못 된 범위를 벗어나는 로드 합니다.In this example, the conditional branch associated with the x < buffer_size expression may mispredict and speculatively execute the body of the for loop when x is greater than or equal to buffer_size, thus resulting in a speculative out-of-bounds load.

// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;

unsigned char ReadBytes(unsigned char *buffer, unsigned int buffer_size) {
    for (unsigned int x = 0; x < buffer_size; x++) {
        // SPECULATION BARRIER
        unsigned char value = buffer[x];
        return shared_buffer[value * 4096];
    }
}

배열 범위를 벗어나는 로드 간접 분기를 제공 합니다.Array out-of-bounds load feeding an indirect branch

이 코딩 패턴에서는 조건부 분기 오측 발생할 수 있는 대/소문자를 대상에는 간접 분기에는 다음 잠재 고객 문제를 해결 하는 함수 포인터의 배열에 대 한 액세스 범위를 벗어나는 읽은 범위를 벗어나는.This coding pattern involves the case where a conditional branch misprediction can lead to an out-of-bounds access to an array of function pointers which then leads to an indirect branch to the target address that was read out-of-bounds. 다음 코드 조각은이 보여 주는 예제를 제공 합니다.The following snippet provides an example that demonstrates this.

이 예제에서는 신뢰할 수 없는 메시지 식별자 제공 되어 통해 DispatchMessage는 untrusted_message_id 매개 변수입니다.In this example, an untrusted message identifier is provided to DispatchMessage through the untrusted_message_id parameter. 하는 경우 untrusted_message_id 는 보다 작은 MAX_MESSAGE_ID, 함수 포인터의 배열로 인덱스 및 분기에 사용 됩니다 해당 분기 대상입니다.If untrusted_message_id is less than MAX_MESSAGE_ID, then it is used to index into an array of function pointers and branch to the corresponding branch target. 아키텍처 측면에서이 코드는 안전 하지만 CPU mispredicts 조건부 분기를에서 될 수 있습니다 DispatchTable 에 의해 인덱싱되고 untrusted_message_id 해당 값 보다 크거나 같은 경우 MAX_MESSAGE_ID를 따라서는 범위를 벗어나는 액세스 합니다.This code is safe architecturally, but if the CPU mispredicts the conditional branch, it could result in DispatchTable being indexed by untrusted_message_id when its value is greater than or equal to MAX_MESSAGE_ID, thus leading to an out-of-bounds access. 이 추측을 통해 실행 되는 코드에 따라 정보 공개 문제점는 배열의 범위를 넘어 파생 되는 분기 대상 주소에서 투기적 실행 될 수 있습니다.This could result in speculative execution from a branch target address that is derived beyond the bounds of the array which could lead to information disclosure depending on the code that is executed speculatively.

#define MAX_MESSAGE_ID 16

typedef void (*MESSAGE_ROUTINE)(unsigned char *buffer, unsigned int buffer_size);

const MESSAGE_ROUTINE DispatchTable[MAX_MESSAGE_ID];

void DispatchMessage(unsigned int untrusted_message_id, unsigned char *buffer, unsigned int buffer_size) {
    if (untrusted_message_id < MAX_MESSAGE_ID) {
        // SPECULATION BARRIER
        DispatchTable[untrusted_message_id](buffer, buffer_size);
    }
}

부하가 배열 범위를 벗어나는 대/소문자를 사용 하 여 다른 부하를 제공 하면이 문제는 오측 인해 종료 조건 초과 하는 루프와 함께에서 발생할 수 있습니다.As with the case of an array out-of-bounds load feeding another load, this condition may also arise in conjunction with a loop that exceeds its terminating condition due to a misprediction.

배열 범위를 벗어나는 저장 간접 분기를 제공 합니다.Array out-of-bounds store feeding an indirect branch

앞의 예제에 설명 했습니다는 잘못 된 범위를 벗어나는 방법을 부하를 간접 분기 대상에 영향을 줄 수는 것도 수는 함수 포인터 또는 반송 주소 등의 간접 분기 대상을 수정 하려면 범위를 벗어나는 저장 합니다.While the previous example showed how a speculative out-of-bounds load can influence an indirect branch target, it is also possible for an out-of-bounds store to modify an indirect branch target, such as a function pointer or a return address. 이 잠재적으로 투기적 실행 공격자가 지정 된 주소에서 발생할 수 있습니다.This can potentially lead to speculative execution from an attacker-specified address.

이 예제에서는 신뢰할 수 없는 인덱스를 통해 전달 됩니다는 untrusted_index 매개 변수입니다.In this example, an untrusted index is passed through the untrusted_index parameter. 경우 untrusted_index 의 요소 수보다 작습니다 합니다 pointers 배열 (256 요소)를 다음에서 제공 된 포인터 값 ptr 에 기록 됩니다는 pointers 배열입니다.If untrusted_index is less than the element count of the pointers array (256 elements), then the provided pointer value in ptr is written to the pointers array. 이 코드는 아키텍처 측면에서 안전 하지만 CPU mispredicts 조건부 분기를에서 될 수 있습니다 ptr 스택에 할당의 경계를 벗어나 추측 쓰고 pointers 배열입니다.This code is safe architecturally, but if the CPU mispredicts the conditional branch, it could result in ptr being speculatively written beyond the bounds of the stack-allocated pointers array. 이 대 한 반환 주소 투기적 손상을 초래할 수 WriteSlot입니다.This could lead to speculative corruption of the return address for WriteSlot. 공격자의 값을 제어할 수 있는 경우 ptr, 임의의에서 투기적 실행 시킬 수 있습니다 때 주소 WriteSlot 잘못 된 경로 반환 합니다.If an attacker can control the value of ptr, they may be able to cause speculative execution from an arbitrary address when WriteSlot returns along the speculative path.

unsigned char WriteSlot(unsigned int untrusted_index, void *ptr) {
    void *pointers[256];
    if (untrusted_index < 256) {
        // SPECULATION BARRIER
        pointers[untrusted_index] = ptr;
    }
}

마찬가지로, 명명 된 함수 포인터 로컬 변수가 func 추측 주소를 수정할 수 있습니다 다음 스택에 할당 된는 func 조건부 분기 오측 발생 하는 경우를 말합니다.Similarly, if a function pointer local variable named func were allocated on the stack, then it may be possible to speculatively modify the address that func refers to when the conditional branch misprediction occurs. 이 함수 포인터를 통해 호출 될 때 임의의 주소에서 투기적 실행 될 수 있습니다.This could result in speculative execution from an arbitrary address when the function pointer is called through.

unsigned char WriteSlot(unsigned int untrusted_index, void *ptr) {
    void *pointers[256];
    void (*func)() = &callback;
    if (untrusted_index < 256) {
        // SPECULATION BARRIER
        pointers[untrusted_index] = ptr;
    }
    func();
}

두이 예제 모두 잘못 된 스택 할당 간접 분기 포인터 수정 한다는 점에 유의 해야 합니다.It should be noted that both of these examples involve speculative modification of stack-allocated indirect branch pointers. 잘못 된 수정에도 읽기 전용 메모리 일부 Cpu에서 전역 변수, 힙 할당 메모리에 발생할 가능성이 있습니다.It is possible that speculative modification could also occur for global variables, heap-allocated memory, and even read-only memory on some CPUs. 스택 할당 된 메모리의 경우 Microsoft C++ 컴파일러에는 이미 추측 버퍼 보안에 인접 한 배치 되는 로컬 변수 다시 정렬 하 여 같은 간접 분기 스택 할당 대상을 수정 하기 더 어려운 확인 하는 단계는 쿠키의 일부로 합니다 /GS 컴파일러 보안 기능입니다.For stack-allocated memory, the Microsoft C++ compiler already takes steps to make it more difficult to speculatively modify stack-allocated indirect branch targets, such as by reordering local variables such that buffers are placed adjacent to a security cookie as part of the /GS compiler security feature.

잘못 된 형식 혼동Speculative type confusion

이 범주에 다룹니다 코딩 패턴을 잘못 된 형식 혼란이 발생할 수 있습니다.This category deals with coding patterns that can give rise to a speculative type confusion. 이 아키텍처 패스를 따라는 잘못 된 문자를 사용 하 여 투기적 실행 하는 동안 메모리에 액세스할 때 발생 합니다.This occurs when memory is accessed using an incorrect type along a non-architectural path during speculative execution. 조건부 분기 오측와 잘못 된 저장소 바이패스 잠재적으로 잘못 된 형식 혼란이 발생할 수 있습니다.Both conditional branch misprediction and speculative store bypass can potentially lead to a speculative type confusion.

잘못 된 저장소 바이패스에 대 한이 시나리오는 컴파일러 다시 여러 종류의 변수에 대 한 스택 위치를 사용 하는 위치에서 발생할 수 있습니다.For speculative store bypass, this could occur in scenarios where a compiler reuses a stack location for variables of multiple types. 왜냐하면 형식 변수의의 아키텍처 저장소 A 있으므로 다음 형식의 부하 우회 될 수 있습니다 A 드리는 변수에 할당 되기 전에 실행 하 합니다.This is because the architectural store of a variable of type A may be bypassed, thus allowing the load of type A to speculatively execute before the variable is assigned. 이전에 저장 된 변수의 다른 종류의 경우 잘못 된 형식 혼동 조건을 만들 수이.If the previously stored variable is of a different type, then this can create the conditions for a speculative type confusion.

조건부 분기 오측 다음 코드 조각 설명에 사용 됩니다 잘못 된 형식 혼동을 제공할 수 있는 다양 한 조건을 정상에 오 르다.For conditional branch misprediction, the following code snippet will be used to describe different conditions that speculative type confusion can give rise to.

enum TypeName {
    Type1,
    Type2
};

class CBaseType {
public:
    CBaseType(TypeName type) : type(type) {}
    TypeName type;
};

class CType1 : public CBaseType {
public:
    CType1() : CBaseType(Type1) {}
    char field1[256];
    unsigned char field2;
};

class CType2 : public CBaseType {
public:
    CType2() : CBaseType(Type2) {}
    void (*dispatch_routine)();
    unsigned char field2;
};

// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;

unsigned char ProcessType(CBaseType *obj)
{
    if (obj->type == Type1) {
        // SPECULATION BARRIER
        CType1 *obj1 = static_cast<CType1 *>(obj);

        unsigned char value = obj1->field2;

        return shared_buffer[value * 4096];
    }
    else if (obj->type == Type2) {
        // SPECULATION BARRIER
        CType2 *obj2 = static_cast<CType2 *>(obj);

        obj2->dispatch_routine();

        return obj2->field2;
    }
}

잘못 된 형식 혼동 하는 범위를 벗어나는 로드Speculative type confusion leading to an out-of-bounds load

이 코딩 패턴에서는 잘못 된 형식 혼동 될 수 있습니다 위치 하는 경우는 범위를 벗어나는 또는 로드 된 값 이후에 로드 주소를 피드 하는 위치 필드 형식 혼동 액세스 합니다.This coding pattern involves the case where a speculative type confusion can result in an out-of-bounds or type-confused field access where the loaded value feeds a subsequent load address. 이 배열 범위를 벗어나는 코딩 패턴 유사 하지만 위에 표시 된 대로 시퀀스를 코딩 하는 대신 통해 매니페스트 됩니다.This is similar to the array out-of-bounds coding pattern but it is manifested through an alternative coding sequence as shown above. 이 예제에서는 공격 컨텍스트를 실행을 위해 공격 대상 컨텍스트를 일으킬 수 있습니다 ProcessType 형식의 개체와 여러 번 CType1 (type 필드 값과 같음 Type1).In this example, an attacking context could cause the victim context to execute ProcessType multiple times with an object of type CType1 (type field is equal to Type1). 첫 번째 조건부 분기를 학습의 효과 갖습니다이 if 문을 예측 수행 되지 않습니다.This will have the effect of training the conditional branch for the first if statement to predict not taken. 공격 컨텍스트 실행을 위해 컨텍스트 교착 상태가 발생 하면 다음 ProcessType 형식의 개체를 사용 하 여 CType2입니다.The attacking context can then cause the victim context to execute ProcessType with an object of type CType2. 첫 번째 조건부 분기 하는 경우이 잘못 된 형식 혼동 될 수 있습니다 if mispredicts 문과의 본문을 실행 합니다 if 형식의 개체를 캐스팅 하므로 문을 CType2CType1입니다.This can result in a speculative type confusion if the conditional branch for the first if statement mispredicts and executes the body of the if statement, thus casting an object of type CType2 to CType1. 하므로 CType2 보다 작습니다 CType1에 대 한 메모리 액세스 CType1::field2 는 결과에 잘못 된 범위를 벗어나는 로드 보안 되지 않은 데이터입니다.Since CType2 is smaller than CType1, the memory access to CType1::field2 will result in a speculative out-of-bounds load of data that may be secret. 이 값은 다음에서 로드 사용 shared_buffer 배열 마찬가지로 눈에 띄는 부작용을 만들 수 있는 범위를 벗어나는 예제에서는 앞에서 설명한 합니다.This value is then used in a load from shared_buffer which can create observable side effects, as with the array out-of-bounds example described previously.

간접 분기 앞에 잘못 된 형식 혼동Speculative type confusion leading to an indirect branch

이 코딩 패턴에는 투기적 실행 하는 동안 안전 하지 않은 간접 분기의 잘못 된 형식 혼동 될 수 있습니다 위치 하는 경우 포함 됩니다.This coding pattern involves the case where a speculative type confusion can result in an unsafe indirect branch during speculative execution. 이 예제에서는 공격 컨텍스트를 실행을 위해 공격 대상 컨텍스트를 일으킬 수 있습니다 ProcessType 형식의 개체와 여러 번 CType2 (type 필드 값과 같음 Type2).In this example, an attacking context could cause the victim context to execute ProcessType multiple times with an object of type CType2 (type field is equal to Type2). 첫 번째 조건부 분기를 학습 효과 해야 if 수행할 문 및 else if 문을 수행 하지 않습니다.This will have the effect of training the conditional branch for the first if statement to be taken and the else if statement to be not taken. 공격 컨텍스트 실행을 위해 컨텍스트 교착 상태가 발생 하면 다음 ProcessType 형식의 개체를 사용 하 여 CType1입니다.The attacking context can then cause the victim context to execute ProcessType with an object of type CType1. 첫 번째 조건부 분기 하는 경우이 잘못 된 형식 혼동 될 수 있습니다 if 문은 예측 수행 및 else if 본문의 실행 문은 예측 하지 수행한는 else if 형식의개체를캐스팅하고CType1CType2입니다.This can result in a speculative type confusion if the conditional branch for the first if statement predicts taken and the else if statement predicts not taken, thus executing the body of the else if and casting an object of type CType1 to CType2. 이후를 CType2::dispatch_routine 필드와 겹치는 char 배열 CType1::field1,이 인해 잘못 된 간접 분기에서 의도 하지 않은 분기 대상입니다.Since the CType2::dispatch_routine field overlaps with the char array CType1::field1, this could result in a speculative indirect branch to an unintended branch target. 공격 상황에 맞는 경우 바이트 값을 제어할 수는 CType1::field1 배열 수 분기 대상 주소를 제어할 수 있습니다.If the attacking context can control the byte values in the CType1::field1 array, they may be able to control the branch target address.

잘못 된 초기화 되지 않은 사용Speculative uninitialized use

이 범주의 코딩 패턴에는 투기적 실행 될 수 있습니다 초기화 되지 않은 메모리에 액세스 하 고 사용 하 여 이후 부하 또는 간접 분기 피드 시나리오 포함 됩니다.This category of coding patterns involves scenarios where speculative execution may access uninitialized memory and use it to feed a subsequent load or indirect branch. 악용 되도록 이러한 코딩 패턴을 공격자를 제어 하거나 맥락에서 사용 되는 컨텍스트별 초기화 되지 않은 상태로 사용 되는 메모리 내용을 영향을 줄 수 해야 합니다.For these coding patterns to be exploitable, an attacker needs to be able to control or meaningfully influence the contents of the memory that is used without being initialized by the context that it is being used in.

투기적 초기화 되지 않은 사용 하는 범위를 벗어나는 로드Speculative uninitialized use leading to an out-of-bounds load

잘못 된 초기화 되지 않은 사용을 잠재적으로 발생 시킬 수는 범위를 벗어나는 공격자가 제어 값을 사용 하 여를 로드 합니다.A speculative uninitialized use can potentially lead to an out-of-bounds load using an attacker controlled value. 값 아래 예에서 index 할당 됩니다 trusted_index 아키텍처 모든 경로에 및 trusted_index 보다 작거나 같은 것으로 간주 됩니다 buffer_size.In the example below, the value of index is assigned trusted_index on all architectural paths and trusted_index is assumed to be less than or equal to buffer_size. 그러나 컴파일러에서 생성 된 코드에 따라 있기 부하를 허용 하는 잘못 된 저장소 바이패스를 발생할 수 있습니다 buffer[index] 및 미리 할당을 실행 하는 종속 식 index합니다.However, depending on the code produced by the compiler, it is possible that a speculative store bypass may occur that allows the load from buffer[index] and dependent expressions to execute ahead of the assignment to index. 이 경우에 대 한 초기화 되지 않은 값 index 에 대 한 오프셋으로 사용할 buffer 공격자가 범위를 벗어나는 중요 한 정보를 읽고 종속 부하를 통해 쪽 채널을 통해이 전달할 수 있는 shared_buffer .If this occurs, an uninitialized value for index will be used as the offset into buffer which could enable an attacker to read sensitive information out-of-bounds and convey this through a side channel through the dependent load of shared_buffer.

// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;

void InitializeIndex(unsigned int trusted_index, unsigned int *index) {
    *index = trusted_index;
}

unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int trusted_index) {
    unsigned int index;

    InitializeIndex(trusted_index, &index); // not inlined

    // SPECULATION BARRIER
    unsigned char value = buffer[index];
    return shared_buffer[value * 4096];
}

간접 분기 선행 투기적 초기화 되지 않은 사용Speculative uninitialized use leading to an indirect branch

잘못 된 초기화 되지 않은 사용 될 수 있습니다 잠재적으로 간접 분기를 분기 대상이 공격자가 제어 하는 위치입니다.A speculative uninitialized use can potentially lead to an indirect branch where the branch target is controlled by an attacker. 아래 예에서 routine 중 하나에 할당 됩니다 DefaultMessageRoutine1 또는 DefaultMessageRoutine 값에 따라 mode합니다.In the example below, routine is assigned to either DefaultMessageRoutine1 or DefaultMessageRoutine depending on the value of mode. 아키텍처 경로에서 이렇게 하면 routine 항상 간접 분기 미리 초기화 되 고 있습니다.On the architectural path, this will result in routine always being initialized ahead of the indirect branch. 그러나 컴파일러에서 생성 된 코드에 따라 잘못 된 저장소 바이패스를 발생할 수 있습니다 통해 간접 분기 수 있도록 routine 할당을 사전 실행할 추측 routine합니다.However, depending on the code produced by the compiler, a speculative store bypass may occur that allows the indirect branch through routine to be speculatively executed ahead of the assignment to routine. 이 경우 공격자 할 수 있습니다는 임의의 주소에서 추측을 통해 실행 가정 공격자가 영향을 줄 수의 초기화 되지 않은 값을 제어 하 고 routine입니다.If this occurs, an attacker may be able to speculatively execute from an arbitrary address, assuming the attacker can influence or control the uninitialized value of routine.

#define MAX_MESSAGE_ID 16

typedef void (*MESSAGE_ROUTINE)(unsigned char *buffer, unsigned int buffer_size);

const MESSAGE_ROUTINE DispatchTable[MAX_MESSAGE_ID];
extern unsigned int mode;

void InitializeRoutine(MESSAGE_ROUTINE *routine) {
    if (mode == 1) {
        *routine = &DefaultMessageRoutine1;
    }
    else {
        *routine = &DefaultMessageRoutine;
    }
}

void DispatchMessage(unsigned int untrusted_message_id, unsigned char *buffer, unsigned int buffer_size) {
    MESSAGE_ROUTINE routine;

    InitializeRoutine(&routine); // not inlined

    // SPECULATION BARRIER
    routine(buffer, buffer_size);
}

완화 하는 방법Mitigation options

소스 코드를 변경 하 여 투기적 실행 쪽 채널 취약성을 완화할 수 있습니다.Speculative execution side channel vulnerabilities can be mitigated by making changes to source code. 이러한 변경 내용을 추가 하 여 같은 특정 인스턴스의 취약점을 완화 하는 포함 될 수 있습니다는 추론 장벽을, 또는 응용 프로그램의 중요 한 정보에 액세스할 수 없는 잘못 된 디자인을 변경 하 여 실행 합니다.These changes can involve mitigating specific instances of a vulnerability, such as by adding a speculation barrier, or by making changes to the design of an application to make sensitive information inaccessible to speculative execution.

수동 계측이 통해 추론 장벽Speculation barrier via manual instrumentation

A 추론 장벽을 아키텍처 경로 따라 계속에서 투기적 실행을 방지 하기 위해 개발자가 수동으로 삽입할 수 있습니다.A speculation barrier can be manually inserted by a developer to prevent speculative execution from proceeding along a non-architectural path. 개발자가 조건부 분기) (이후에 블록의 시작 부분에서 조건부 블록의 본문에 위험한 코딩 패턴을 하기 전에 추론 장벽의 삽입할 수는 예를 들어, 또는 관련 된 첫 번째 로드 전에 합니다.For example, a developer can insert a speculation barrier before a dangerous coding pattern in the body of a conditional block, either at the beginning of the block (after the conditional branch) or before the first load that is of concern. 이 아키텍처 경로에서 실행을 직렬화 하 여 위험한 코드를 실행 하는 조건부 분기 오측을 못하게 됩니다.This will prevent a conditional branch misprediction from executing the dangerous code on a non-architectural path by serializing execution. 추론 장벽을 시퀀스는 다음 표에 설명 된 대로 하드웨어 아키텍처에 따라 다릅니다.The speculation barrier sequence differs by hardware architecture as described by the following table:

아키텍처Architecture 추론 장벽을 CVE 2017-5753에 대해 내장 함수Speculation barrier intrinsic for CVE-2017-5753 추론 장벽을 CVE-2018-3639 내장 함수Speculation barrier intrinsic for CVE-2018-3639
x86/x64x86/x64 _mm_lfence()_mm_lfence() _mm_lfence()_mm_lfence()
ARMARM 현재 사용할 수 없음not currently available __dsb(0)__dsb(0)
ARM64ARM64 현재 사용할 수 없음not currently available __dsb(0)__dsb(0)

예를 들어, 사용 하 여 다음과 같은 코드 패턴을 완화할 수 있습니다는 _mm_lfence 아래와 같이 내장 함수입니다.For example, the following code pattern can be mitigated by using the _mm_lfence intrinsic as shown below.

// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;

unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        _mm_lfence();
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}

추론 장벽을 컴파일러 타임 계측을 통해Speculation barrier via compiler-time instrumentation

Microsoft C++ Visual Studio 2017 (버전 15.5.5부터 시작)에서 컴파일러에 대 한 지원을 포함 합니다 /Qspectre 자동으로 제한 된 집합만 잠재적으로 취약 한 코딩 패턴에 대 한 추론 장벽을 삽입 하는 스위치와 관련 된 CVE-2017-5753 합니다.The Microsoft C++ compiler in Visual Studio 2017 (starting with version 15.5.5) includes support for the /Qspectre switch which automatically inserts a speculation barrier for a limited set of potentially vulnerable coding patterns related to CVE-2017-5753. 에 대 한 설명서는 /Qspectre 플래그 효과 및 사용에 자세한 정보를 제공 합니다.The documentation for the /Qspectre flag provides more information on its effects and usage. 이 플래그는 모든 잠재적으로 취약 한 코딩 패턴은 다루지 않습니다을 같이 개발자에 의존 하지 않아야 하의 취약점으로 인 한이이 클래스에 대 한 포괄적인 완화 하는 것이 반드시 합니다.It is important to note that this flag does not cover all of the potentially vulnerable coding patterns and as such developers should not rely on it as a comprehensive mitigation for this class of vulnerabilities.

마스킹 배열 인덱스Masking array indices

여기서는 잘못 된 범위를 벗어나는 로드 하는 경우에 발생할 수 있습니다, 배열 인덱스를 명시적으로 바인딩된 논리를 추가 하 여 아키텍처 및 아키텍처 경로에 배열 인덱스를 강력한 제한 될 수 있습니다.In cases where a speculative out-of-bounds load may occur, the array index can be strongly bounded on both the architectural and non-architectural path by adding logic to explicitly bound the array index. 예를 들어, 배열 2의 거듭제곱에 맞춰진 크기를 할당할 경우 다음 간단한 마스크를 도입할 수 있습니다.For example, if an array can be allocated to a size that is aligned to a power of two, then a simple mask can be introduced. 이 나와 있는 것으로 가정 하는 아래 샘플에서 buffer_size 2의 거듭제곱에 맞춥니다.This is illustrated in the sample below where it is assumed that buffer_size is aligned to a power of two. 이렇게 untrusted_index 는 항상 미만 buffer_size조건부 분기 오측 발생 하는 경우에, 및 untrusted_index 보다 크거나 같은 값을 사용 하 여 전달 된 buffer_size합니다.This ensures that untrusted_index is always less than buffer_size, even if a conditional branch misprediction occurs and untrusted_index was passed in with a value greater than or equal to buffer_size.

컴파일러에서 생성 되는 코드에 따라 잘못 된 저장소 바이패스를 여기서 수행할 인덱스 마스킹 발생할 수 있습니다는 점에 유의 해야 합니다.It should be noted that the index masking performed here could be subject to speculative store bypass depending on the code that is generated by the compiler.

// A pointer to a shared memory region of size 1MB (256 * 4096)
unsigned char *shared_buffer;

unsigned char ReadByte(unsigned char *buffer, unsigned int buffer_size, unsigned int untrusted_index) {
    if (untrusted_index < buffer_size) {
        untrusted_index &= (buffer_size - 1);
        unsigned char value = buffer[untrusted_index];
        return shared_buffer[value * 4096];
    }
}

메모리에서 중요 한 정보를 제거합니다.Removing sensitive information from memory

투기적 실행 쪽 채널 취약성을 완화 하기 위해 사용할 수 있는 또 다른 방법은 메모리에서 중요 한 정보를 제거 하는 것입니다.Another technique that can be used to mitigate speculative execution side channel vulnerabilities is to remove sensitive information from memory. 소프트웨어 개발자는 투기적 실행 하는 동안 중요 한 정보에 액세스할 수 없는 응용 프로그램을 리팩터링 하는 기회 찾을 수 있습니다.Software developers can look for opportunities to refactor their application such that sensitive information is not accessible during speculative execution. 이 별도 프로세스에 중요 한 정보를 격리 하는 응용 프로그램의 디자인을 리팩터링에서 수행할 수 있습니다.This can be accomplished by refactoring the design of an application to isolate sensitive information into separate processes. 예를 들어, 웹 브라우저 응용 프로그램을 하나의 프로세스 투기적 실행을 통해 크로스-원본 데이터에 액세스할 수 없도록 방지 하는 별도 프로세스에 사용 하 여 각 웹 연결 된 데이터를 격리 하려고 할 수 있습니다.For example, a web browser application can attempt to isolate the data associated with each web origin into separate processes, thus preventing one process from being able to access cross-origin data through speculative execution.

참고자료See also

투기적 실행 사이드 채널 취약성을 완화 하기 위한 지침Guidance to mitigate speculative execution side-channel vulnerabilities
투기적 실행 쪽 채널 하드웨어 취약성을 완화 하기Mitigating speculative execution side channel hardware vulnerabilities