구글의 폴 터너 (Paul Turner)가 쓴 주석에서 sgbj가 언급 한 기사 는 다음을 훨씬 더 자세히 설명하지만 한 번 살펴 보겠다.
지금은 제한된 정보로 이것을 함께 정리할 수있는 한, 레토 폴린은 폴린 귀환 트램펄린입니다 CPU가 간접 점프의 대상을 추측하지 못하도록 실행되지 않는 무한 루프를 사용하는 입니다.
기본 접근 방식은 Andi Kleen의 커널 브랜치 에서 볼 수 있습니다이 문제를 해결하는 .
__x86.indirect_thunk
메모리 주소 (내가 호출 할 ADDR
)가 스택 위에 저장된 호출 대상을로드 하고 RET
명령을 사용하여 점프를 실행 하는 새로운 호출을 소개합니다 . 썽크 자체는 NOSPEC_JMP / CALL 매크로를 사용하여 호출 되는데, 이는 많은 간접 호출과 점프를 대체하는 데 사용되었습니다. 매크로는 단순히 호출 대상을 스택에 배치하고 필요한 경우 반환 주소를 올바르게 설정합니다 (비선형 제어 흐름 참고).
.macro NOSPEC_CALL target
jmp 1221f /* jumps to the end of the macro */
1222:
push \target /* pushes ADDR to the stack */
jmp __x86.indirect_thunk /* executes the indirect jump */
1221:
call 1222b /* pushes the return address to the stack */
.endm
call
간접 호출을 마치면 NOSPEC_CALL
매크로 사용 후 제어 흐름이 계속 되어 일반 대신에 사용할 수 있도록 끝에 배치 가 필요합니다.call
썽크 자체는 다음과 같습니다.
call retpoline_call_target
2:
lfence /* stop speculation */
jmp 2b
retpoline_call_target:
lea 8(%rsp), %rsp
ret
제어 흐름은 여기에서 약간 혼란 스러울 수 있으므로 명확히하십시오.
call
현재 명령 포인터 (라벨 2)를 스택으로 푸시합니다.
lea
스택 포인터에 8을 추가 하여 가장 최근에 푸시 된 쿼드 워드 (마지막 리턴 주소)를 효과적으로 버립니다 (라벨 2로). 그런 다음 스택의 맨 위가 실제 리턴 주소 ADDR을 다시 가리 킵니다.
ret
*ADDR
호출 포인터의 시작 부분으로 스택 포인터를 이동하여 재설정합니다.
결국이 전체 동작은 실제로로 바로 이동하는 것과 같습니다 *ADDR
. 우리가 얻는 한 가지 이점은 call
명령어를 실행할 때 리턴 명령문 (RSB)에 사용되는 분기 예측 변수 가 해당하는 것으로 가정한다는 것입니다.ret
명령문이 레이블 2로 이동 것입니다.
레이블 2 다음 부분은 실제로 실행되지 않으며 이론적으로 명령 파이프 라인을 JMP
명령으로 채우는 무한 루프 일뿐 입니다. 사용하여 LFENCE
, PAUSE
또는 더 일반적으로 지시 명령 파이프 라인 스톨이되게 추측 실행에 어떤 전력과 시간 낭비의 CPU를 정지한다. 이는 retpoline_call_target에 대한 호출이 정상적으로 리턴 LFENCE
될 경우 다음에 실행될 명령이되기 때문입니다. 이것은 또한 분기 예측자가 원래 반송 주소 (라벨 2)를 기반으로 예측하는 것입니다.
인텔 아키텍처 매뉴얼에서 인용하려면 :
LFENCE 이전의 명령어는 LFENCE 전에 메모리에서 가져올 수 있지만 LFENCE가 완료 될 때까지 실행되지 않습니다.
그러나 사양에 LFENCE와 PAUSE가 파이프 라인을 멈추게한다고 언급하지 않았으므로 여기서 줄 사이를 조금 읽습니다.
이제 원래 질문으로 돌아가십시오. 두 가지 아이디어의 조합으로 인해 커널 메모리 정보 공개가 가능합니다.
추측이 잘못되었을 때 추측 실행은 부작용이 없어야하지만, 추측 실행은 여전히 캐시 계층에 영향을 미칩니다 . 이는 메모리로드가 추론 적으로 실행될 때 여전히 캐시 라인이 제거 될 수 있음을 의미합니다. 캐시 계층의 이러한 변경은 동일한 캐시 세트에 맵핑 된 메모리에 대한 액세스 시간을 신중하게 측정하여 식별 할 수 있습니다.
메모리 읽기의 소스 주소 자체가 커널 메모리에서 읽 혔을 때 임의의 비트의 임의 메모리가 누출 될 수도 있습니다.
Intel CPU의 간접 분기 예측기는 소스 명령의 최하위 12 비트 만 사용하므로 사용자 제어 메모리 주소를 사용하여 2 ^ 12 가능한 예측 기록을 모두 독살하기 쉽습니다. 그러면 커널 내에서 간접 점프가 예측 될 때 커널 권한으로 추측 적으로 실행될 수 있습니다. 캐시 타이밍 사이드 채널을 사용하면 임의의 커널 메모리가 누출 될 수 있습니다.
업데이트 : 커널 메일 링리스트 에서 RSB (Return Stack Buffer)가 비었을 때 retpoline이 브랜치 예측 문제를 완전히 완화하지 못한다고 생각하는 지속적인 논의가 있습니다. 최신 Intel 아키텍처 (Skylake +) 취약한 분기 대상 버퍼 (BTB)에 :
완화 전략으로서의 Retpoline은 공격자가 중독 될 수 있기 때문에 BTB에서 오는 예측을 사용하지 않기 위해 간접 분기를 반환으로 교환합니다. Skylake +의 문제점은 RSB 언더 플로가 BTB 예측을 사용하게되어 공격자가 추측을 제어 할 수 있다는 것입니다.