summaryrefslogtreecommitdiff
path: root/chap/chap2.tex
blob: 29a357befdd8f4b14781fc6aeec49018cfb4e48a (plain)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
% Copyright (c) 2014,2016 Casper Ti. Vector
% Public domain.

\chapter{Meltdown 和 Spectre 攻击与分析}\label{sec:attack}

本章首先介绍侧信道攻击,之后对已有的 Meltdown 和 Spectre 攻击的各种变
体进行分类讲解,最后简单地介绍已有攻击的其他使用形式。

\section{侧信道攻击}

侧信道攻击是利用计算的过程对环境产生的影响,从而获取秘密数据的技术。
1996年,Paul Kocher 发现通过对私钥的操作进行计时,可以找出
Diffie-Hellman,RSA 等密码的私钥。\supercite{TimingAttack} 这是最早的
侧信道攻击的研究。之后研究者发现了更多侧信道,如功耗
\supercite{MOP2010}、电磁辐射\supercite{EMpower}、温度
\supercite{hutter}、噪声\supercite{acoustic}等。

侧信道攻击的一个用途是用于构造隐蔽信道(covert channel)。隐蔽信道是指
在系统安全策略定义之下,不被允许传输数据的双方,用于传输数据的信道。

现代计算机系统采用多种优化手段,优化可以作用于处理的数据的值、数据的位
置、数据存放位置的访问频率等,攻击者可以通过这些优化产生的效果,推断出
计算机系统处理的秘密数据的值。基于软件的微体系结构侧信道攻击
\supercite{gruss}利用微体系结构优化造成的时间区别,这种攻击不需要物理访问
计算机系统,只需要在系统上运行代码。

高速缓存计时攻击是一种重要的侧信道攻击的方式。当高速缓存中存在一个地址
的数据时,访问这个地址的时间更短,利用这个性质,攻击者可以得知一个地址
是否曾经被访问过。缓存计时攻击的常见形式有:

\begin{enumerate}
\item Evict+Time: 攻击者清除一个缓存组,然后对一个算法的运行进行计时,
  检查它的运行时间是否受到影响
\item Prime+Probe: 受害者运行一段程序后,攻击者访问缓存某一组的每一路,
  观察访问时间是否有变化
\item Flush+Reload: 攻击者利用 x86 的 clflush 等指令清除一个共享内存地
  址对应的缓存行,在受害者运行一段程序后,攻击者重新读取该行对应的地址
  的数据,通过测量访问时间推断受害者是否访问了这个共享的地址
\end{enumerate}

% TODO: more side channels
\section{Meltdown 和 Spectre 攻击的各种变体}

处理器在执行指令遇到异常,或者推测式执行了错误的指令后,会回卷错误执行
的指令,丢弃它们对体系结构状态的修改,这些被回卷的指令称为暂态指令
(transient
instruction)。Meltdown\supercite{meltdown} 和
Spectre\supercite{spectre} 是最早发现的两个利用暂态指令的攻击。此后发现
了更多的 Meltdown 和 Spectre 的变体。为了找出更多的攻击类型和分析它们的
防御方案,Claudio Canella 等人对这些攻击及其防御做了系统性的研
究\supercite{systematic},将所有暂态执行攻击分为 Meltdown 型攻击
和 Spectre 型攻击两类。

\subsection{Meltdown 型攻击}

Meltdown 型攻击利用的是处理器异常产生的暂态指令,这些指令使用了体系结构
层次上不可访问的数据,从而绕过硬件的安全策略,泄露体系结构层次上不可访
问的数据。

\subsubsection{“熔断”(Meltdown)攻击}

内存隔离是现代操作系统的核心安全功能之一。操作系统确保用户程序无法访问
其他程序或内核空间的内存区域。现代计算环境利用这种隔离,使得系统上可
以同时运行多个应用程序,和多个用户的进程。

为了将不同的进程相互隔离,处理器为操作系统提供了虚拟地址空间机制,它将
虚拟地址翻译为物理地址。虚拟地址空间将内存划分为一系列的页,系统通过多
级页表将虚拟页翻译为物理页。页表是一个多级的结构,存放虚拟页到物理页的
映射,同时,它还保存了每个页的保护属性,用于处理器进行权限检查,例如可
读、可写、可执行、用户可读等。操作系统中每一个运行中的进程有一个对应的
页表,页表所在的位置存放在处理器的一个特定的寄存器中,在上下文切换时,
操作系统更新这个寄存器,切换至处理器上的新进程可以使用这个进程对应的页
表。利用这个机制,每个进程的虚拟地址空间映射到不同的物理内存区域,每个
进程只能使用自己地址空间中的数据,从而达到进程间隔离的效果。

每个地址空间都可以分为用户地址空间和内核地址空间。正在运行的进程可以访
问用户地址空间,而内核地址空间只有进入内核态后可以访问,操作系统通过在
页表中设置内核地址空间的页为用户不可访问,来实现这个特性。内核地址空间
除了包含内核所要使用的数据外,通常还保存了整个物理内存的映射、文件缓存
等内容。用户和内核地址空间的隔离,使得操作系统可以将内核映射到每个进程
的地址空间,在中断等事件发生时,可以在不改变内存地址映射的条件下,高效
地从用户进程切换至内核。

Meltdown\supercite{meltdown} 攻击使得任何用户进程都可以简单地读取所在
机器的整个内核区域的内存(包括映射在内核区域中的所有物理内存),从而完
全破坏了内存隔离。Meltdown 不利用任何软件漏洞,而是利用了现代处理器中
的侧信道,适用于所有主流的操作系统。这种攻击可以用于 2010 年以来的现代
Intel 处理器,也可能可用于其他厂商的处理器。

侧通道攻击通常需要非常具体地了解目标应用程序,并且只用于泄露其中的秘密
信息,但在易受攻击的处理器上,利用 Meltdown 攻击,可以通过运行代码获得
整个内核地址空间的数据,包括任何映射在内核中的物理内存。

在一个易受攻击的乱序处理器中,处理器允许非特权态的进程将数据从特权地址
装载至临时的寄存器中。进一步,处理器可以使用该寄存器的值进行计算,例如
用这个寄存器的值访问数组。如果一条指令不应该执行,通过简单地丢弃访问内
存得到的结果,处理器可以确保程序执行的正确性。因此,在体系结构层次,即
处理器执行计算的抽象定义下,不会出现安全问题。

但是,乱序执行中的内存访问会改变缓存状态,而缓存的状态又可以通过缓存侧
通道进行检测。因此,攻击者可以通过在乱序执行的指令流中,读取特权态的内
存,来得到整个内核内存的数据,并通过微体系结构隐蔽通道传送出来,然后攻
击者在隐蔽信道的接收端,找出这个临时寄存器的值。因此,在微体系结构层次,
即实际的硬件实现中,存在可利用的安全性问题。

操作系统将内核地址空间映射至每个用户进程的地址空间中。处理器执行访存指
令时,在将虚拟地址翻译为物理地址的时候,检查地址对应的权限位,判断用户
空间的指令是否可访问该地址。在 Meltdown 攻击中,攻击者让处理器从用户不
可访问的内核地址装载数据至寄存器,由于所有内核地址都可以翻译为合法的物
理地址,处理器可以读取内核地址空间的数据,在产生异常前,攻击者构造的暂
态指令序列可以将读取到的数据通过隐蔽信道发送给攻击者。

\begin{figure}
\centering
\begin{minted}{nasm}
xor rax, rax
retry:
mov al, byte [rcx] ; 读取内核空间的数据
shl rax, 0xc
jz retry
mov rbx, qword [rbx + rax] ; 访问探测数组
\end{minted}
\caption{Meltdown 示例代码}
\label{lst:meltdown}
\end{figure}

图\ref{lst:meltdown}是一段 Meltdown 攻击的示例代码,通过它可以读取内核地址空间的数据。
这段 x86 指令读取了以 rcx 为地址的内核空间的数据,在用户态下,这个读取
内核空间数据的指令会产生异常,但是在处理器产生异常之前,这条指令可以将
内核空间的数据读至寄存器,并且其后的指令也会执行,访问 rbx 指向的探测
数组。在处理器回卷了产生异常的指令和其后执行的指令后,高速缓存中仍然保
留了探测数组和内核数据相关的元素,从而攻击者可以通过扫描探测数组的每一
项,观察访问时间,以推测内核空间数据的值。

Meltdown 打破了处理器内存隔离功能提供的所有安全保障。这种攻击可以用于
现代台式机、笔记本电脑以及云服务器。 Meltdown 允许非特权进程读取映射在
内核地址空间中的数据,包括 Linux,Android 和 OS X 上的整个物理内存,以
及 Windows 上的大部分物理内存。这些内存可能包含其他进程的物理内存,以
及内核共享的容器沙箱(如 Docker,LXC)或半虚拟化模式下的 Xen 中,虚拟
化管理程序的内存,和其他虚拟化实例的内存。Meltdown 攻击可以以 3.2KB/s
到 503KB/s 的速度读取内核内存。

\subsubsection{“预兆”(Foreshadow)攻击}

% foreshadow introduction
现代广泛使用的操作系统和应用程序的大小可以轻松地达到数百万行代码,并且
单个漏洞通常会破坏所有安全保证,所以在安全上,现有的操作系统和应用程序
难以信任。为了应对这些挑战,学术界和工业界开发了可信执行环境(Trusted
  Execution Environment, TEE),其中包含一个处理器常规执行环境之外的非
分层保护模型,用于隔离应用程序,称为隔离区(enclave)。 TEE 利用只含有
处理器和微码的最小可信计算基(Trusted Computing Base),来保证相互不信
任的隔离区的保密性和完整性。 每个隔离区的私有处理器和内存状态只可以由
其内部运行的代码访问,并且在任何特权级别(包括潜在的恶意操作系统和虚拟
  机管理程序)上运行的软件和所有其他隔离区都无法访问。除了强大的内存隔
离外,TEE 通常还提供一个证明(attestation)原语,从而可以从本地或远程,
在运行时从密码学上验证特定的隔离区已经加载到真正的(从而被认为是安全的)
TEE 处理器上。

2013 年发布的 Intel 软件防护扩展(Software Guard eXtention, SGX)
\supercite{sgx},使得 x86 处理器可以使用硬件强制的 TEE 隔离和证明。
Intel SGX 承诺的强大安全保障,使越来越多的行业参与者将这项技术应用于可
受攻击者控制的机器上,和需要安全执行的各种应用程序中。Open Whisper
Systems 依靠 SGX 在 Signal 网络中实现隐私友好的联系人发现功能
\supercite{signal-sgx} 。微软和 IBM 最近都宣布在其云基础架构中支持 SGX。
各种现成的蓝光播放器以及 4K Netflix 客户端使用 SGX 对高分辨率视频流实
施数字版权管理(DRM)。新兴的加密货币\supercite{mobilecoin}和的区块链
\supercite{sawtooth}技术更是依赖于 SGX 的正确性。

然而,Foreshadow\supercite{foreshadow} 攻击表明,当前的 SGX 实现并没有
满足它的安全目标。这种攻击利用现代 Intel 处理器中的乱序执行机制,
可以从处理器的缓存中泄露明文的隔离区数据。Foreshadow 和 Meltdown 利用
相似的处理器漏洞,处理器的控制逻辑允许攻击者在暂态的乱序执行指令回滚之
前,使用未授权的内存访问的结果。和 Meltdown 攻击针对传统的分层保护域不
同,Foreshadow 考虑一种不同的攻击模型,攻击者的目标不是从用户空
间读取内核内存,而是破坏最先进的地址空间内隔离区保护域,它不在已经部署
的用于防御 Meltdown 的内核页表隔离技术的保护之内。在 Foreshadow 攻击中,
无特权的普通用户即可完成基本的攻击,而无需以 root 的身份访问受害者机器。
对于有 root 权限的攻击者,还可以使用一组可选的内核级优化技术,进一步降
低 Foreshadow 攻击的噪声。

Foreshadow 破坏了 Intel SGX 的安全模型,在没有微码补丁的情况下,当前的
SGX 处理器无法保证隔离区的数据保密性,也无法证明隔离区执行的完整性,包
括英特尔自己的隔离区架构。此外,尽管 SGX 期望可以防御内核级别的攻击者,
但现有的 SGX 处理器甚至无法在没有特权的用户空间攻击者面前保护隔离区中
的秘密数据。

所有先前的针对 SGX 的攻击都依赖于软件中的侧信道泄露或软件漏洞。
人们普遍认为,精心编写的隔离区可以通过坚持良好的编码实践防止信息泄露,
例如不使用依赖秘密数据的分支操作。这些攻击都没有破坏 Intel 定义的 SGX
的安全保证。 然而,Foreshadow
否定了这一论点,因为它完全依赖于基本的 Intel x86 处理器的行为,并且不
利用任何软件漏洞,甚至不需要知道受害者隔离区的源代码。

% foreshadow attack
\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.6\textwidth]{addr-trans.eps}
  \caption{地址翻译过程和 L1 终端错误的原理\supercite{foreshadowNG}}
  \label{fig:addr-tran}
\end{figure}

在现代 Intel x86 处理器中,用一个虚拟内存地址访问内存时,处理器先在
TLB 中查询对应的物理地址,如果 TLB 中没有这个地址对应的项,则会在页表
中找到虚拟地址对应的页表项,以获得对应的物理地址和访问权限信息。图
\ref{fig:addr-tran} 展示了访问页表的过程。如果是在虚拟机环境中,则需要
增加一个扩展页表(Extended Page Table,EPT)的访问过程,将客户机的物理
地址翻译为底层物理机的物理地址。最后,启用了 Intel SGX 的处理器还会进
一步检查地址翻译的结果,确保这个地址翻译遵守硬件强制的隔离区访问控制限
制。如果这三个独立的阶段出现了一个访问违例,则处理器抛出一个页错误,控
制流转向异常处理代码,而在启用了 SGX 的处理器中,大多数的隔离区内存违
例会被处理器忽视,将内存装载指令得到的值设为放弃页(abort page)的值 -1.

在体系结构层次上,以上的地址翻译过程在文档中有精确的描述。现代处理器为
了加快地址翻译和内存访问,使用了并行访问和额外的缓存结构。在 Meltdown
类型的攻击中,可以发现 Intel 的 CPU 将内存访问违例检查放到了指令提交的
时间,使得在页错误发生之前,未授权的内存内容仍然可以到达流水线中其后执
行的乱序执行的指令。Meltdown 攻击利用了这个时间窗口,在暂态指令中,将
只有特权态能访问的内存编码至持久的微体系结构状态。

现代 Intel 处理器使用虚拟地址索引,物理地址标签的一级缓存。如图
\ref{fig:addr-tran} 所示,这种缓存索引方式使得地址翻译可以和一级缓存的
查询并行。在得到虚拟地址对应的一级缓存组后,处理器还需要知道是否有某一
路缓存含有需要的数据,因此处理器要将地址翻译得到的物理地址和这组缓存每
一路的物理地址标签对比,在某一个有效的缓存路存在正确的物理地址时,
一级缓存命中,数据返回至处理器的执行单元。

如果启用 SGX 的处理器使用 Meltdown 攻击,攻击者读取未授权的隔离区内存
时,不会产生异常,而是读出放弃页,但是它只在页权
限检查通过后发生。攻击者可以将该页的存在(present)位清除,即将该页设
为不存在,此时处理器便会产生异常,但由于一级缓存使用虚拟地址索引,因此
仍然可以从一级缓存读取该虚拟地址中的数据,从而暂态指令可以泄露 SGX 隔
离区中的数据。

在 Skylake 微体系结构的 Intel i7-6700 中使用 Foreshadow 攻击,
可以以 81.14\% 的精确度提取一级缓存行的数据。

通过将一个页设为不存在,绕过地址翻译,从一级缓存读取数据的攻击方式,
Intel 称其为L1终端错误(L1 Terminal Fault)\supercite{l1tf}。 利用相
同的原理,Foreshadow-NG\supercite{foreshadowNG} 新增了Foreshadow-OS 和
Foreshadow-VMM 两种 Foreshadow 类型的攻击,可以绕过操作系统和虚拟机的
隔离。


% forshadow-OS
操作系统内核将虚拟内存页从内存交换至外部持久存储时,都需要清除页表项的
存在位。但是,根据处理器的体系结构规范\supercite{intel-spec},内核可以
根据需要,自由地使用标记为不存在的页表项中的剩余位。例如,操作系统可以
保持这些位不变,将它们归零,或者使用它们来存储用于把页从磁盘复制回内存
的元数据。因此,虽然非特权用户空间应用程序不能直接控制页表项,但是当页
表项取消映射相应的虚拟页时,操作系统在页表项中留下的元数据仍然可以指向
包含敏感数据的有效物理地址。在内核清除相应页表项中的存在位后,从用户空
间解引用未映射的页将导致终端错误。

Foreshadow-OS 利用了这种错误造成的暂态执行的指令。虽然访问页表中标记为
不存在的页会触发终端错误,在页表项中的元数据表示缓存中的物理地址的情况
下,处理器仍然将一级数据高速缓存中未授权的数据传递到暂态乱序执行中。然
后可以和其他暂态执行攻击一样,利用缓存等侧信道构造隐蔽信道,将秘密数据
从微体系结构暂态执行域带入体系结构状态中。如果操作系统支持大于 4KB 的
页(例如,2MB 或 1GB),攻击者甚至可以访问最大页面大小的内存范围。由于
所有软件本质上共享相同的物理地址空间,因此元数据创建的虚拟到物理映射可
能指向属于操作系统内核,VMM 内存,SGX 隔离区,或 SMM 内存的数据。在操
作系统通过 munmap 系统调用释放内存,将页表项清零的常见情况下,攻击者可
以访问存储在物理地址 0x00 的数据。

实验结果表明,在一个 i7-6820HQ CPU 上,Foreshadow-OS 通过利用瞬态执行,
可以从已清除存在位的页中读取一级缓存的数据。

% foreshadow-VMM

虽然上述 Foreshadow-OS 变体允许无特权的攻击者使用未经授权的物理内存位
置上的数据进行暂态计算,但他们无法直接控制访问哪些物理地址。因此,
Foreshadow 攻击使用在可以直接控制客户机器物理地址和一级缓存标签的比较
过程的不可信虚拟机时时,会变得更有破坏性。这种攻击称为 Foreshadow-VMM.

根据 Intel 处理器的架构规范\supercite{intel-spec},客户机物理地址经过
基于 EPT 的转换过程以找到底层主机物理内存地址。Intel 的分析
\supercite{l1tf}显示,终端错误发生时,虚拟地址到物理地址的转换处于早期
阶段,在经过 EPT 转换之前,将转换出来的客户机物理地址传到一级缓存中进
行标签比较。因此,恶意虚拟机可以控制用于访问一级缓存的暂态指令的物理地
址。特别的,不可信的恶意客户虚拟机可以在自己的客户页表中修改页表项,指
向客户机物理内存。该地址不会经过 EPT 转换,和一级缓存中的标签进行比较,
就像它是主机物理地址一样。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.6\textwidth]{foreshadow-vmm.eps}
  \caption{Foreshadow VMM 的一种使用场景\supercite{foreshadowNG}}
  \label{fig:foreshadow-vmm}
\end{figure}

图 \ref{fig:foreshadow-vmm} 展示了一种情况,一个恶意的虚拟机可以利用暂
态执行攻击窃取属于同一处理器核的另一个虚拟机中的数据。这种攻击并不局限
于使用 SMT 的情形,因为 Foreshadow-VMM 可以提取处理器物理核心一级数据高速
缓存中的任何秘密。


\subsubsection{延迟浮点寄存器状态还原(LazyFP)攻击}

% introduction
LazyFP\supercite{lazyfp} 是一个 Meltdown 类型的攻击,它可以跨越进程和
虚拟机的边界,泄露浮点处理单元(FPU)寄存器的状态。这种攻击利用 Intel
处理器处理设备不可用异常(\Fault{NM})的行为。这个异常用于在操作系统中
实现延迟 FPU 上下文切换的优化。在 LazyFP 攻击中,没有特权的本地执行的
代码,可以利用 \Fault{NM} 异常产生的暂态指令,恢复受害者进程的 FPU 寄
存器状态。由于 AES-NI 的密钥等数据可能存在 FPU 寄存器中,这种攻击导致
的数据泄露对信息安全是个很大的威胁。

% background

x87 浮点单元(FPU)是 x86 的处理器的一个扩展,其最初目的是加速浮点数的
运算。它有独立于 x86 处理器的一组指令和寄存器。在 1989 年推出 Intel
486DX 之前,它是一个可选的外部协处理器,此后成为 x86 微处理器不可或缺
的一部分。在上下文切换中,向内存保存 FPU 状态,以及从内存恢复 FPU 的状
态,曾经是一个开销很大的操作,因为内存速度慢并且大小有限。此外,那时
FPU 只在少数程序中被使用。在 FPU 不被所有进程使用的情况下,在每个上下
文切换时,切换 FPU 状态会产生不必要的开销。为了能够减少这种开销,x86
处理器引入了一个控制寄存器位 cr0.ts,允许操作系统打开和关闭 FPU,当 FPU
关闭时,它会保持其状态,但用户和内核代码都无法访问。

利用 cr0.ts 实现的优化称为延迟 FPU 上下文切换(lazy FPU context
switching)。核心思想是 FPU 寄存器状态仅在必要时进行上下文切换,并保留
在不使用 FPU 的进程中。这样,操作系统可以避免不必要的保存和恢复 FPU 上
下文的成本。

操作系统跟踪当前 FPU 寄存器状态所属的进程,此进程称为 FPU 所有者。FPU
可以在启用和禁用状态,在 FPU 启用状态下,当前进程是 FPU 所有者,可以自
由地执行 FPU 指令。当操作系统切换到另一个进程时,FPU 被禁用。在进程尝
试执行 FPU 指令之前,它将保持禁用状态。由于 FPU 被禁用,使用浮点指令时,
处理器会生成 \Fault{NM} 异常。操作系统处理 \Fault{NM} 异常的方式取决于
当前进程是否为 FPU 所有者,如果是,则 FPU 寄存器状态属于该进程,操作系
统启用 FPU,该进程再次可以自由执行 FPU 指令。否则,则操作系统在启用
FPU 的同时,将当前 FPU 寄存器状态存储到 FPU 所有者进程的状态保存区域,
并恢复当前进程的 FPU 寄存器状态,并将该进程设为 FPU 的所有者。这个算法
在不同操作系统中的实现可能不同,它们的共同点是,使用 FPU 的进程切换到
不使用 FPU 的进程时,操作系统不需要上下文切换 FPU 寄存器的状态。

虽然延迟 FPU 上下文切换仍然在现代的操作系统中广泛使用,但其基本假设通
常不再适用。从 Intel MMX 指令集扩展,到后续的 SSE,AVX 和 AVX-512,FPU
寄存器状态已经扩展到更大的 SIMD 寄存器。SSE 指令集是 64 位 x86 处理器
的必须组成部分,并且在几乎所有程序和库中用于实现不同的任务,例如内存复
制。因此,虽然更大的 FPU 寄存器状态使得避免 FPU 上下文切换更具吸引力,
但在当前的软件中,每个进程最终都会使用 FPU 寄存器并触发 \Fault{NM} 异
常。因此,Linux 内核已经默认将 FPU 寄存器作为普通上下文切换的一部分进
行切换,在正常操作中不会生成 \Fault{NM} 异常。

在使用 Intel 的硬件辅助虚拟化技术时,处理器在从客户机切换到管理程序时,
不会切换FPU寄存器状态。虚拟机管理程序负责上下文切换 FPU 寄存器的状态,
并且机制与非虚拟化操作系统的机制相同。因此,延迟 FPU 上下文切换也适用于
虚拟机管理程序。

在使用延迟 FPU 上下文切换的操作系统中,利用 LazyFP 攻击可以使没有特权
的攻击者获取同一处理器硬件线程中 FPU 寄存器的数据。图 \ref{lst:lazyfp}
的例子可以泄露1比特的数据。

\begin{figure}
  \centering
\begin{minted}{nasm}
movq rax, xmm0
and rax, 1
shl rax, 6
mov rax, dword [mem + rax]
\end{minted}
\caption{LazyFP 示例代码}
\label{lst:lazyfp}
\end{figure}

在该代码片段中,由于进程中 FPU 寄存器被禁用,movq 指令产生 \Fault{NM}
异常。操作系统会用对应用程序透明的方式处理该异常,恢复进程的状态并继续
执行。

而在处理器结束 movq 指令的执行并产生 \Fault{NM} 之前,已经执行了后续指
令,虽然最终这些指令引起的体系结构状态更改会被丢弃,但在缓存中的状态仍
然保留。这段代码使用了受害者进程的 FPU 寄存器和攻击者进程的通用寄存器
进行暂态指令的执行,通过探测 mem 指向的数组的缓存状态,可以恢复 xmm0
最低位的值。

利用 Intel TSX 等技术处理异常,攻击者可以高效地泄露所有 FPU 寄存器的数
据。在 Intel Core i7-5600U 处理器中,可以以 3.3MB/s 的速度泄露一个 AVX
寄存器的数据。

\subsection{Spectre 型攻击}

Spectre 型攻击利用处理器对控制流或数据流的预测,并进行推测式执行产生的
暂态指令。Spectre 型攻击中,暂态指令访问的是程序在体系结构层面能访问的
数据,可以绕过软件定义的安全策略,如边界检查等。

推测式执行的常见形式是控制流的推测式执行。控制流的推测式执行依赖于分支
预测技术,包含了条件分支的预测、间接转移目标地址的预测和函数返回地址的
预测,对应于 Spectre 攻击中 Spectre-PHT, Spectre-BTB, Spectre-RSB. 数
据流的推测式执行也是一种推测式执行的形式,处理器预测装载指令和程序序列
之前访问地址未知的存储指令地址不冲突,推测式执行这样的装载指令,它对应
Spectre-STL.

Spectre 攻击使得受害者程序在暂态指令中执行在严格按序执行时不会执行的操
作,这些操作会将秘密信息通过隐蔽信道泄露给攻击者。

Spectre 攻击通常分为三个阶段。第一个阶段是设置阶段,攻
击者执行一些用于训练处理器的操作,使得处理器在后续的阶段中,产生可被攻
击者利用的错误的推测式执行。此外,在设置阶段中,需要增大推测式执行的窗
口,使得推测式执行中的指令足够多,从而可以执行攻击者希望处理器执行的指
令,实现方式包括操作高速缓存状态,从缓存中去除预测的分支所依赖的数据。
在这个阶段,攻击者还可以进行隐蔽信道的准备,如 Flush+Reload 攻击中清除
探测数组对应缓存行的操作。

在第二个阶段,处理器在推测式执行中,将受害者上下文中的秘密信息,传送至
微体系结构隐蔽信道。攻击者向受害者通过系统调用等方式发送请求,以出发这个推
测式执行。攻击者也可以通过自身代码的推测式执行,从同一进程获取敏感信息,
例如在利用 JIT 编译器的环境中,攻击者可以泄露这个环境中不希望其中执行
的代码访问的数据。

最后一个阶段是恢复敏感数据。对于使用 Flush+Reload 的 Spectre 攻击,恢
复过程的工作之一是对此前清除的缓存行对应的内存地址进行访问计时,从而推
断出敏感数据。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsubsection{Spectre-PHT}

Spectre-PHT 利用分支预测器,攻击者可以训练转移预测器,使得转移预测器执
行一个分支,该分支的指令序列可以泄露受害者地址空间或寄存器的信息。

Spectre v1 是最早提出的 Spectre 攻击,属于 Spectre-PHT 类型。它利用受
害者程序中的分支,一个例子如下:

\begin{figure}[htbp]
\centering
\begin{minted}[frame=single,linenos=true]{C}
if (x < array1_size)
    y = array2[array1[x] * 4096];
\end{minted}
\caption{Spectre v1 受害者代码}
\label{lst:spectre_v1}
\end{figure}

%%%%%%%%%%%%%%%%%%%%%%% gtran: spectre v1 %%%%%%%%%%%%%%%%%%%%%%%%%%%

% \verb|TODO|: Spectre 论文中 Spectre v1 的内容

% \verb|TODO|: 解释 Spectre 攻击中这段代码的运行方式,关于在 Javascript 和 eBPF 下的攻击

% \Fixme: 重新翻译

这段代码 x 的值做边界检查,确保后续执行代码的安全性,同时该检查也防止
处理器读取 array1 数组外的敏感内容。否则,越界的数组下标 x 可能触发异
常,也可以通过构造 x 用于访问敏感数据。

\begin{figure}[!htbp]
  \centering
  \includegraphics[width=0.4\textwidth]{spectre_v1.eps}
  \caption{边界检查结果和分支预测方向的可能情形\supercite{spectre}}
  \label{fig:spectre_v1}
\end{figure}

图\ref{fig:spectre_v1}是边界检查结果和预测的方向的四种组合。在已知边
界检查的结果之前,处理器预测最可能的结果,推测式地执行边界检查之后的代
码。有很多原因导致边界检查的结果不能及时得出,如边界检查期间发生缓存缺
失,缺少空闲的执行单元,存在复杂的数据依赖。在这些情况下,正确预测的可
以使执行时间更短。

但是,处理器可以在不正确的路径推测式执行指令。在这个例子中,假设攻击者
可以控制以如下方式运行:

\begin{enumerate}
\item 选择一个恶意越界的 \verb|x| 的值,使得 \verb|array1[x]| 指向受害
  者内存中的某个秘密字节 \verb|k|
\item \verb|array1_size| 和 \verb|array2| 不在缓存中,但 \verb|k| 在缓
  存中
\item 此前的操作中接收到的 \verb|x| 值均有效,使得分支预测器预测 if 为
  真
\end{enumerate}

攻击者可以构造这样的缓存条件,例如,通过使用一定的模式访问内存,造成
\verb|array1_size| 和 \verb|array2| 被驱逐出缓存,再使受害者在合法的操
作中使用秘密 \verb|k|.

在这种条件下,这段代码运行时,处理器比较恶意的 \verb|x| 和
\verb|array1_size|。读取 \verb|array1_size| 导致高速缓存缺失,从而产生
很大的延迟,需要处理器从主存中获得这个值,才可以产生分支结果。同时,分
支预测器预测 if 中的条件为真,因此在推测式执行中,处理器访问
\verb|array1[x]|,此时缓存命中,获得秘密 \verb|k| 的值,然后用它计算
\verb|array2[k*4096]| 的地址,再从存储系统发出读取这个地址的请求。当分
支结果在此之后确定时,处理器从错误的推测式执行恢复。但是推测式执行中读
取 \verb|array2| 的行为影响了缓存状态,而且读取的地址依赖于秘密
\verb|k|.

攻击者通过测量 \verb|array2| 中的哪个位置在缓存中,可以推断出 \verb|k|
的值,因为受害者在推测式执行中将 \verb|array2[k*4096]| 带入了缓存。

这种攻击变体可以用在其他场景下,例如分支的条件可以是类型检查,推测式执
行的代码可以将一个比较结果泄露到一个固定的地址。上述缓存状态在限制更多
的情形,如 \verb|array1_size| 在缓存中时,攻击也可能可以进行。

这种攻击已经在 Intel, AMD 的多个处理器微体系结构,和一些支持推测式执行的
ARM 处理器上进行验证,确认它们受 Spectre v1 的影响。

除了使用原生的机器指令,利用 JavaScript 和 Linux 内核的 eBPF 的 JIT 编
译器也可以进行 Spectre 攻击。

%% 我们在JavaScript中开发了一个概念验证,并在Google Chrome版本62.0.3202中
%% 对其进行了测试,该版本允许网站从其运行的进程中读取私有内存。代码如清单
%% 2所示。

%% 在分支预测器错误引用过程中,索引(通过位操作)设置为范围内值。在最后一
%% 次迭代中,index被设置为simpleByteArray的越界地址。我们使用变量
%% localJunk来确保不优化操作。根据ECMAScript 5.1 Section 11.10 [13],“|
%% 0”操作将值转换为32位整数,作为JavaScript解释器的优化提示。与其他优化的
%% JavaScript引擎一样,V8执行即时编译以将JavaScript转换为机器语言。虚拟操
%% 作放在清单2中的代码中,以使simpleByteArray.length存储在本地内存中,以
%% 便在攻击期间将其从缓存中删除。有关D8的结果反汇编输出,请参见清单3。

%% 由于无法从JavaScript访问clflush指令,我们使用缓存逐出[27,51],即,我们
%% 以某种方式访问​​其他存储器位置,以便之后逐出目标存储器位置。泄漏的结果通
%% 过probeTable [n * 4096]的缓存状态传递,n∈0..255,因此攻击者必须驱逐这
%% 256个缓存行。长度参数(JavaScript代码中的simpleByteArray.length和反汇
%% 编中的[ebp-0xe0])也需要逐出。 JavaScript不提供对rdtscp指令的访问,并
%% 且Chrome故意降低其高分辨率计时器的准确性以使用performance.now()[62]
%% 来阻止定时攻击。但是,HTML5的Web Workers功能使创建一个单独的线程变得简
%% 单,该线程反复递减共享内存位置中的值[24,60]。这种方法产生了一个提供足
%% 够分辨率的高分辨率计时器。

%% 作为利用条件分支的第三个例子,我们开发了一个可靠的概念验证,它通过滥用
%% eBPF(扩展BPF)接口从未修改的Linux内核泄漏内核内存,而没有针对Specter
%% 的补丁。 eBPF是一个基于伯克利数据包过滤器(BPF)[49]的Linux内核接口,
%% 可用于各种目的,包括根据数据包内容过滤数据包。 eBPF允许非特权用户在内
%% 核的上下文中触发解释或JIT编译以及随后执行用户提供的,内核验证的eBPF字
%% 节码。攻击的基本概念类似于针对JavaScript的攻击概念。

%% 在此次攻击中,我们仅将eBPF代码用于推测性执行的代码。我们在用户空间中使
%% 用本机代码来获取隐藏的信道信息。这与上面的JavaScript示例不同,后者的两
%% 个函数都是用脚本语言实现的。为了推测性地访问用户空间内存中依赖于机密的
%% 位置,我们对内核内存中的数组执行推测性的越界内存访问,其索引足够大,以
%% 便访问用户空间内存。概念验证假定目标处理器不支持超级用户模式访问保护
%% (SMAP)。但是,没有这种假设的攻击也是可能的。它在Intel Xeon Haswell
%% E5-1650 v3上进行了测试,它在默认解释模式和eBPF的非默认JIT编译模式下都
%% 可以使用。在高度优化的实现中,我们能够在此设置中泄漏高达2000B / s。它
%% 还在AMD PRO A8-9600 R7处理器上进行了测试,它只能在非默认的JIT编译模式
%% 下工作。我们将调查原因留给未来的工作。

%% eBPF子系统管理存储在内核内存中的数据结构。用户可以请求创建这些数据结构,
%% 然后可以从eBPF字节码访问这些数据结构。为了强制执行这些操作的内存安全性,
%% 内核存储与每个此类数据结构相关联的一些元数据,并对此元数据执行检查。特
%% 别地,元数据包括数据结构的大小(在创建数据结构时设置一次并用于防止越界
%% 访问)以及加载到内核中的eBPF程序的引用数量。引用计数跟踪引用数据结构的
%% 多少eBPF程序正在运行,确保不释放属于数据结构的内存
%% 加载的eBPF程序引用它。

%% 我们通过滥用错误共享来增加边界检查的延迟与eBPF管理的阵列的长度。内核将
%% 数组长度和引用计数存储在同一缓存行中,允许攻击者将包含数组长度的缓存行
%% 移动到处于Modified状态的另一个物理CPU核心(参见[16,53])。这是通过加载
%% 和丢弃引用另一个物理内核上的eBPF阵列的eBPF程序来完成的,这会导致内核在
%% 另一个物理内核上递增和递减阵列的引用计数器。这种攻击在Haswell CPU上实
%% 现了大约5000B / s的泄漏率。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% \verb|TODO|: Spectre v1.1的内容,需要更详细地解释攻击原理

推测式缓冲区溢出(Speculative Buffer Overflow)
\supercite{spec-buffer-overflow} 是 Spectre-PHT 的另一种形式,被称为
Spectre v1.1. 处理器存在一个存储缓冲区(store buffer),往存储系统写入
的数据在指令提交前将写入数据的地址和值存放在这个缓冲区中,后续的指令要
使用这个地址的值时,可以将缓冲区中的数据转发给需要的指令。推测式缓冲区
溢出利用了这个特性,例如图\ref{lst:spec-buf-overflow}中的程序:

\begin{figure}
\begin{minted}{C}
if (x < len)
    a[x] = z;
\end{minted}
  \centering
  \caption{推测式缓冲区溢出}
  \label{lst:spec-buf-overflow}
\end{figure}

攻击者给出一个超过数组 \verb|a| 大小的 \verb|x| 时,在推测式执行时,会
将 \verb|a[x]| 的地址和攻击者输入的值 \verb|z| 写入至存储缓冲区。如果
\verb|a[x]| 的地址是一个函数在栈中存放的返回地址,这段程序之后有返回指
令,返回指令就会从存储缓冲区读取这个函数的返回地址,并从这个地址开始执
行指令,从而攻击者可以使程序在推测式执行时执行以 \verb|z| 为地址开始的
指令流。通过构造输入 \verb|x| 和 \verb|z|,攻击者可以使处理器在推测式
执行中执行攻击者指定的代码,这些代码可能会将程序中的秘密数据泄露在微架
构状态中。

\subsubsection{Spectre-BTB}

处理器遇到间接转移时,使用 BTB 预测转移的目标地址。Spectre v2 利用间接
转移的错误推测式执行,这类攻击在分类上称为 Spectre-BTB.

在 Spectre v2 中,攻击者可以对间接转移进行投毒,从而可以利用由此产生的
转移预测错误,从另一个上下文(如另一进程)中读取任意内存。如果由于缓存
缺失等原因,需要较长延迟得到间接转移的目标地址,则处理器从此前预测的目
标地址处进行推测式执行。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.4\textwidth]{spectre_v2.eps}
  \caption{转移预测器在不同上下文中训练和使用\supercite{spectre}}
  \label{fig:spectre_v2}
\end{figure}

攻击者可以用恶意的目标地址训练转移预测器,从而处理器转移到攻击者选择的
地址进行推测式执行。在图\ref{fig:spectre_v2}中,转移预测器可以在上下文
中 A 中训练,在上下文 B 中,转移预测器根据在 A 中的训练结果进行预测,
导致处理器跳转至攻击者选择的目标地址处进行推测式执行。攻击者可以引导受
害者推测式执行在正确的程序执行中永远不会执行的位置。由于推测性执行带来
了可测量的副作用,这种攻击对于攻击者来说极其强大。

举一个简单的攻击例子,攻击者试图读取受害者的内存,当发生间接转移时,攻
击者可以控制两个寄存器。这在现实的二进制程序中很常见,因为操作从外部接
收数据的函数通常会进行函数调用,此时寄存器包含攻击者控制的值。

攻击者还需要找到一个 Spectre 组件,它是一个用于在推测式执行中,将受害者
的敏感信息转移到隐蔽通道中的代码片段。在这个例子中,一个有效的组件可以
由两个指令组成。一个将攻击者控制的 R1 指向的内存加到攻击者控制的寄存
器R2 上,接下来一个指令用 R2 访问内存。在这种情况下,Spectre 组件可以让
攻击者通过 R1 控制泄露的地址,以及攻击者通过 R2 控制第二条指令读取泄露
数据对应的地址。Spectre 组件需要在受害者可执行的内存中,由于大多数进程
在内存中映射了很大的共享库,攻击者有足够多的空间找到这样的组件,而不需
要搜索受害者程序的代码。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% gtran %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% \verb|TODO|: Spectre 的论文中关于 Spectre v2 一节的剩余内容,一直到实验结果部
%% 分

%% \Fixme: 重新翻译以下内容

%% 许多其他攻击是可能的,这取决于对手所知道或控制的状态,对手所寻求的信息
%% 所在的位置(例如,寄存器,堆栈,内存等),对手控制推测执行的能力,指令
%% 序列可用于形成小工具,以及哪些渠道可能会泄漏来自投机操作的信息。例如,
%% 如果攻击者可以简单地在将来自寄存器中指定的地址的存储器带入高速缓存的指
%% 令中诱导推测执行,则在寄存器中返回秘密值的加密函数可能变得可利用。同样,
%% 尽管上面的示例假设攻击者控制两个寄存器,但攻击者控制单个寄存器,堆栈上
%% 的值或内存值对于某些小工具来说已足够。

Spectre v2 的攻击方式和面向返回编程(ROP)\supercite{ret2libc} 有相似
之处。在ROP 中,攻击者从受害程序所用的 C 库等代码中寻找大量的指令序列,
合并到一起形成一个组件,攻击者可以构造出能进行任意计算的组件。而在
Spectre v2 攻击中,受攻击的程序是正确编写的软件,构造出的组件是暂态执
行的,指令数目有限,然而攻击者仍然可以让处理器推测式执行复杂的指令序列,
利用侧信道泄露数据。

对处理器的分支预测器进行错误的训练是攻击的一个关键步骤。为此攻击者需要
通过逆向分析出 BTB 的工作方式,用于制造地址冲突。此前已有工作分析了
Intel 处理器的 BTB.\supercite{jump-over-aslr}

%% 请注意,历史错误要求因CPU而异。例如,在Haswell i7-4650U上,使用了大约
%% 29个先前目标地址的低20位,尽管观察到在这些地址上进行了一些进一步的散列。
%% 在AMD Ryzen上,仅使用大约前9个分支的低12位。附录A中提供了用于更新Intel
%% Xeon Haswell E5-1650 v3上的分支历史缓冲区的反向工程伪代码。

%% 此外,我们在攻击者的同一个虚拟地址处设置了一个跳跃,就像在受害者进程中
%% 一样。注意,这可能不是必需的,例如,如果CPU仅基于跳转地址的低位索引预
%% 测。在设计分支预测器时,我们只需要模拟虚拟地址;物理地址,时间和进程ID
%% 似乎并不重要。由于分支预测不受其他内核上的操作的影响(参见第II-C节),
%% 因此必须在同一CPU内核上进行任何错误训练。

%% 我们还观察到分支预测器从非法目的地的跳跃中学习。虽然在攻击者的进程中触
%% 发了异常,但这可以很容易地捕获,例如,使用Linux上的信号处理程序或
%% Windows上的结构化异常处理。与前一种情况一样,分支预测器然后将进行预测,
%% 将其他进程发送到相同的目标地址,但是在受害者的虚拟地址空间(即,小工具
%% 所在的地址空间)中。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\subsubsection{Spectre-RSB}

返回栈(RSB)用于预测程序函数调用的返回地址。RSB 是一个栈结构,当函数调用指令执行时,处理器将返回地址压入 RSB,用于该函数调用对应的返回指令执行时,预测返回的目标地址。当函数返回时,处理器从 RSB 的栈顶得到一个地址,作为预测的返回地址,处理器从这个地址开始推测式执行。当处理器从体系结构的软件栈中得到返回地址,该地址和预测的返回地址相同时,推测式执行正确,否则推测式执行错误。

Spectre-RSB\supercite{spectre-returns}\supercite{ret2spec} 利用了 RSB
预测返回地址错误,导致错误的推测式执行。RSB 预测返回地址错误有四种原因。

第一个原因是由于 RSB 大小有限,会产生上溢和下溢的行为。RSB 的大小通常
和程序调用栈的大小相匹配。调用深度超过 RSB 的大小时,会造成RSB 溢出,
覆盖其中最老的条目,在函数返回时,会发生 RSB 下溢出。在 RSB 下溢出时,
不同处理器用不同方式处理 RSB 为空的情形,如 Intel 的处理器使用 BTB 进
行转移预测,攻击者可以用 Spectre-BTB 的攻击方式进行攻击。

第二个原因是直接污染 RSB,在这种场景下,攻击者可以替换或删除软件调用栈
中的值,从而 RSB 中的值和软件栈中的值不同,导致错误的推测式执行。

第三种方法是推测式污染 RSB. 在一个函数调用指令推测式执行时,返回地址进
入软件栈和 RSB,当推测式执行错误时,虽然调用指令被撤销,但返回地址仍然
在 RSB 中。

第四种方法是跨上下文使用 RSB. 因为上下文切换时,正在执行线程的 RSB 会
被新的线程使用,如果新的线程执行返回指令,则会从原线程提供的 RSB 中的
地址处进行推测式执行。

\subsubsection{Spectre-STL}

Spectre v4 利用的是装载指令的推测式执行。处理器执行装载指令时,如
果此前有地址未知的存储指令,会认为这个装载指令和这些地址未知的
存储指令地址不同,并推测式执行这个装载指令,将其地址中的值装入目的
寄存器。如果在计算出存储的地址后,发现和已执行装载访问了相同数据,
则处理器需要重新执行这个装载和之后的指令。在推测式执行的过程中,暂态
指令可以泄露这个地址中的旧值。

\section{Meltdown 和 Spectre 的其他形式}

在研究 Meltdown 和 Spectre 攻击的过程中,研究者还发现了一起其他的攻击
形式,它们基于已有的 Meltdown 和 Spectre 攻击,使用不同的利用方式。

\subsection{NetSpectre}

NetSpectre\supercite{netspectre} 是通过网络进行的 Spectre 攻击,使得
Spectre 攻击可以用于不运行攻击者控制的代码的系统上,从而将 Spectre 攻
击的影响扩大至更多的设备。

和本地 Spectre 攻击类似,远程的 Spectre 攻击需要受害者存在 Spectre 组
件代码,当这些代码可以通过网络接口调用时,攻击者可以向受害者发送一系列
精心构造的请求,通过测量响应时间,泄露受害者内存中的秘密数据。

访问存储器的延迟可以反映在网络请求的延迟中,因此攻击者可以通过大量的测
量,区分远程的机器缓存是否命中。因此,通过使用 Thrash+Reload,一种基于
Evict+Reload 的远程缓存攻击方式,将 Spectre 攻击用于网络场景,可以每小
时从目标系统泄露 15 比特数据。通过使用基于 AVX2 指令执行时间的侧信道,
可以在 NetSpectre 中获得更高的性能,在局域网中可以每小时泄露 60 比特。
通过使用 NetSpectre 攻击,攻击者可以破坏远程系统中部署的地址空间布局随
机化(ASLR)防御机制。

在 NetSpectre 攻击中,攻击者需要利用两个 Spectre 组件:泄露组件和传输
组件,这些组件在收到网络数据包时执行。泄漏组件在攻击者控制的内存地址处
访问数据,并根据访问的数据改变一些微体系结构状态。传输组件可以进行任意操作,
它的运行时间取决于泄露组件修改的微体系结构状态。这些组件可以存在于网络驱动
程序,以及网络协议和网络服务的实现。

图 \ref{fig:netspectre_leak} 是一个 Spectre v1 组件,它是处理网络数据
包的程序的一部分,其中 x 由攻击者控制,它构成了 NetSpectre 的泄露组件。

\begin{figure}[htbp]
  \centering
\begin{minted}{C}
if (x < bitstream_length)
  if (bitstream[x])
    flag = true;
\end{minted}
\caption{NetSpectre 的一个泄露组件}
\label{fig:netspectre_leak}
\end{figure}

为了在远程攻击中使目标系统在推测式执行中改变微体系结构状态,攻击者采用原始
Spectre 的方法。为了触发远程系统的推测式执行,攻击者进行以下操作:

\begin{enumerate}
\item 攻击者发送多个网络数据包,使得攻击者选择的 x 值总是在边界内,训
  练分支预测器在后续执行此边界检查的分支时,预测 x 在边界内
\item 攻击者发送一个数据包,使得 x 越界,\verb|bitstream[x]| 是目标系
  统中的一个秘密数据位
\item 分支预测器预测边界检查结果为真,推测式执行存储器的访问
\end{enumerate}

在图 \ref{fig:netspectre_leak} 的代码中,当 x 越界时,虽然 flag 的值没
有被修改,但是 flag 的缓存状态会发生变化。如果 \verb|bitstream[x]| 为
1,则 flag 会进入缓存,通过 flag 的缓存状态,可以推断
\verb|bitstream[x]| 的值。

传输组件更简单,它只需要在一个操作中使用 flag,它的执行时间取决于 flag
的缓存状态,例如,最简单的一个传输组件是直接返回 flag 的值。由于传输组
件的响应时间取决于 flag 的缓存状态,因此可以泄露秘密数据位
\verb|bitstream[x]| 的值。

要完成 NetSpectre 攻击,攻击者需要测量一个秘密值泄露时的网络响应时间。
由于响应时间的差异在纳秒级,因此攻击者需要进行大量的测量,此时可以得到
明显的微体系结构状态差异。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.5\textwidth]{netspectre.eps}
  \caption{NetSpectre 的泄露组件和传输组件\supercite{netspectre}}
  \label{fig:netspectre_gadgets}
\end{figure}

\subsection{SgxPectre}

SgxPectre \supercite{sgxpectre} 将 Spectre 攻击用于泄露 Intel SGX 环境
中的数据。执行隔离区内的代码时,使用的分支预测器仍然受隔离区外代码影响,
并且暂态执行的代码可以在隔离区外产生可见的缓存状态更改。因此攻击者可以从
推断出隔离区内的内存或寄存器的数据,破坏了 SGX 保证的保密性。
SgxPectre 通过在 SGX 隔离区之外污染隔离区的 BTB 和 RSB,改变 SGX
隔离区中的控制流,将秘密数据泄露至 SGX 之外。

% \subsection{MeltdownPrime 和 SpectrePrime}
% 
% MeltdownPrime 和 SpectrePrime \supercite{meltdownprime} 是一种使用
% Prime+Probe 方式进行 Meltdown 和 Spectre 攻击的形式。通过利用缓存一致
% 性协议的缓存行失效机制,可以达到和 Flush+Reload 方式的同等精度。

\section{小结}

本章首先介绍侧信道攻击,接着分类介绍各个变体的 Meltdown 型攻击和
Spectre 型攻击,其中 Meltdown 型攻击利用来自异常的暂态指令,主要的攻击
有 Meltdown, Foreshadow 和 LazyFP,Spectre 型攻击利用错误的推测式执行
产生的暂态指令,根据执行预测的部件分为 Spectre-PHT, Spectre-BTB,
Spectre-RSB 和 Spectre-STL,最后介绍 NetSpectre 等攻击的其他利用形式。