Playbook¶
The Playbook directory contains a group of Python classes and scripts designed to simplify the process of writing various rowhammer-related tests. These tests can be executed against a hardware platform.
Payload¶
Tests are generated as payload
data. After generation, this data is transferred to a memory area in the device reserved for this purpose called payload memory
. The payload contains an instruction list that can be interpreted by the payload executor
module in hardware. The payload executor translates these instructions into DRAM commands. The payload executor connects directly to the DRAM PHY, bypassing the DRAM controller, as explained in Architecture.
Changing payload memory size¶
Payload memory size can be changed. Of course it can’t exceed the memory available on the hardware platform used. Currently, the payload memory size is defined in common.py, as an argument to LiteX:
add_argument("--payload-size", default="1024", help="Payload memory size in bytes")
The examples shown in this chapter don’t require any changes. When writing your own Configurations, you may need to change the default value.
Row mapping¶
In the case of DRAM modules, the physical layout of the memory rows in hardware can be different from the logical numbers assigned to them. The nature of rowhammer attack is that only the physically adjacent rows are affected by the aggressor row
. To deal with the problem of disparity between physical location and logical enumeration, we various mapping strategies can be implemented:
TrivialRowMapping
- logical address is the same as physical oneTypeARowMapping
- more complex mapping method, reverse-engineered as a part of this researchTypeBRowMapping
- logical address is the physical one multiplied by 2, taken from this paper
Row Generator class¶
Row Generator class is responsible for creating a list of row numbers used in a rowhammer attack. Currently, only one instance of this class is available.
EvenRowGenerator¶
Generates a list of even numbered rows. Uses the row mapping specified by the Payload generator class used. Two configuration parameters are needed for EvenRowGenerator:
nr_rows
- number of rows to be generatedmax_row
- maximal number to be used. The chosen numbers will be modulo max_row
HalfDoubleRowGenerator¶
Generates a list of rows for a Half-Double attack. The list will repeat rows to create weight difference between attacks at different distances. Used by HalfDoubleAnalysisPayloadGenerator
.
nr_rows
- number of rows in the attack pattern.distance_one
- does the attack have a distance one component?double_sided
- is the attack double-sided?distance_two
- does the attack have a distance two component?attack_rows_start
- the index of the first attack row.max_attack_row_idx
- the position of the last attack row relative toattack_rows_start
.decoy_rows_start
- the index of the first decoy row. There are 3 decoy rows that are used as placebos in various situations: to hammer away from the victim but to keep the number of hammers and timing constant.
Payload generator class¶
The purpose of the payload generator is to prepare a payload and process the test outcome. It is a class that can be reused in different tests Configurations. Payload generators are located in payload_generators directory
Available payload generators¶
Row mapping and row generator settings are combined into a payload generator class. Currently, only two payload generators are available.
RowListPayloadGenerator¶
RowListPayloadGenerator is a simple payload generator that can use a RowGenerator class to generate rows, and then generate a payload that hammers that list of rows
(hammering
is a term used to describe multiple read operations on the same row).
It can also issue refresh commands to the DRAM module.
Here are the configs that can be used in payload_generator_config for this payload generator:
row_mapping
- this is the Row mapping usedrow_generator
- this is the Row Generator class used to generate the rowsrow_generator_config
- parameters for the row generatorverbose
- should verbose output be generated (true or false)fill_local
- when enabled, permits shrinking the filled memory area to just the aggressors and the victimread_count
- number of hammers (reads) per rowrefresh
- should refresh be enabled (true or false)
Example Configurations for this test were provided as configs/example_row_list_*.cfg
files.
Some of them require a significant amount of memory declared as payload memory
.
To execute a minimalistic example from within rowhammer-tester repo, enter:
source venv/bin/activate
export TARGET=arty # change accordingly
cd rowhammer_tester/scripts/playbook/
python playbook.py configs/example_row_list_minimal.cfg
Expected output:
Progress: [========================================] 65536 / 65536
Row sequence:
[0, 2, 4, 6, 14, 12, 10, 8, 16, 18]
Generating payload:
tRAS = 5
tRP = 3
tREFI = 782
tRFC = 32
Repeatable unit: 930
Repetitions: 93
Payload size = 0.10KB / 1.00KB
Payload per-row toggle count = 0.010K x10 rows
Payload refreshes (if enabled) = 10 (disabled)
Expected execution time = 1903 cycles = 0.019 ms
Transferring the payload ...
Executing ...
Time taken: 0.738 ms
Progress: [== ] 3338 / 65536 (Errors: 1287)
...
HammerTolerancePayloadGenerator¶
HammerTolerancePayloadGenerator is a payload generator for measuring and characterizing rowhammer tolerance. It can provide information about how many rows and bits are susceptible to the rowhammer attack. It can also provide information about where the susceptible bits are located.
A series of double-sided hammers against the available group of victim rows is performed.
The double-sided hammers increase in intensity based on read_count_step
parameter.
Here are the parameters that can be specified in payload_generator_config for this payload generator:
row_mapping
- this is the Row mapping usedrow_generator
- this is the Row Generator class used to generate the rowsrow_generator_config
- parameters for the row generatorverbose
- should verbose output be generated (true or false)fill_local
- when enabled, permits shrinking the filled memory area to just the aggressors and the victimnr_rows
- number of rows to conduct the experiment over. This is the number of aggressor rows. Victim rows will be 2 times fewer than this number. For example, to perform hammering for 32 victim rows, use 34 as the parameter valueread_count_step
- this is how much to increment the hammer count between multiple tests for the same row. This is the number of hammers on single side (total number of hammers on both sides is 2x this value)initial_read_count
- hammer count for the first test for a given row. Defaults toread_count_step
if unspecified.distance
- distance between aggressors and victim. Defaults to 1.baseline
- when enabled, a retention effect baseline is collected by hammering distant rows for the same amount of time that the aggressors would be hammered.first_dummy_row
- location of the first of two dummy rows used for baselining.iters_per_row
- number of times the hammer count is incremented for each row
The results are a series of histograms with appropriate labeling.
Example Configurations for this test were provided as configs/example_hammer_*.cfg
files.
Some of them require a significant amount of memory declared as payload memory
.
To execute a minimalistic example from within rowhammer-tester repo, enter:
source venv/bin/activate
export TARGET=arty # change accordingly
cd rowhammer_tester/scripts/playbook/
python playbook.py configs/example_hammer_minimal.cfg
Expected output:
Progress: [========================================] 3072 / 3072
Generating payload:
tRAS = 5
tRP = 3
tREFI = 782
tRFC = 32
Repeatable unit: 186
Repetitions: 93
Payload size = 0.04KB / 1.00KB
Payload per-row toggle count = 0.010K x2 rows
Payload refreshes (if enabled) = 10 (disabled)
Expected execution time = 1263 cycles = 0.013 ms
Transferring the payload ...
Executing ...
Time taken: 0.647 ms
Progress: [============ ] 323 / 1024 (Errors: 320)
...
HalfDoubleAnalysisPayloadGenerator¶
Half-Double is a Rowhammer phenomenon where accesses to both distance-one and distance-two neighbours of a victim row are used to generate bit flips. This payload generator allows us to characterize the Half-Double effect on a memory part.
For each candidate victim row, the analysis starts out with the maximum number of hammers and minimum dilution level. Then, we proceed as follows:
Dilution is increased until pure distance-one attacks stop working.
Verify that pure distance-two attack doesn’t work.
Increase dilution level and record the number of bit flips in the victim until either the bit flips stop or maximum dilution level is reached.
Once maximum dilution level is reached or bit flips stop, reduce hammer count by step and reset dilution to initial level and retry step 3. Repeat until the lowest hammer count is reached.
Note: the hammer count changes on a linear scale and dilution changes on an exponential scale.
Results are presented as a table of values with columns representing hammer count and the rows representing dilution levels. See Tables 2 and 3 in the Half-Double white paper as examples.
max_total_read_count
- maximum number of hammers issued to any given row during an iteration.read_count_steps
- the amount to decrement the number of hammers for each iteration of the outer loop.initial_dilution
- initial value for dilution. Dilution resets to this value at the beginning of the inner loop.dilution_multiplier
- dilution is multiplied by this value for each iteration of the inner loop.verbose
- generates more output.row_mapping
- specifies the style in which the rows are mapped on the chip.attack_rows_start
- starting row number for rows actually used to attack the victim.max_attack_row_idx
- index measured fromattack_rows_start
for the last attack row.decoy_rows_start
- the position of the first decoy row. There are three decoy rows. They are used as placebos during pure distance one portions of the experiment to make the number of hammers and their timing comparable.max_dilution
- maximum value for dilution.fill_local
- only reinitialize affected rows between experiments, as an optimization.
Configurations¶
Test configuration files are represented as JSON files. An example:
{
"inversion_divisor" : 2,
"inversion_mask" : "0b10",
"payload_generator" : "RowListPayloadGenerator",
"payload_generator_config" : {
"row_mapping" : "TypeARowMapping",
"row_generator" : "EvenRowGenerator",
"read_count" : 27,
"max_iteration" : 10,
"verbose" : true,
"refresh" : false,
"fill_local" : true,
"row_generator_config" : {
"nr_rows" : 10,
"max_row" : 64
}
}
}
Different parameters are supported:
payload_generator
- name of the Payload generator class to userow_pattern
- pattern that will be stored in rowsinversion_divisor and inversion_mask - controls which rows get the inverted pattern described in Inversion
payload_generator_config
- these parameters are specific for Payload generator class used
Inversion¶
If needed, the data pattern used for some of the tested rows can be bitwise-inverted
.
Two parameters are used to specify which rows are to be inverted:
inversion_divisor
inversion_mask
An example. inversion_divisor = 8
, inversion_mask = 0b10010010
(bits 1, 4 and 7 are “on”).
We iterate through all row numbers 0,1,2,3,4,…,8,9,10,…
First, a modulo inversion_divisor
operation is performed on a row number. In our case it’s mod 8
.
Next, we check if the bit in inversion_mask
in the position corresponding to our row number (after modulo) is “on” or “off”.
If it’s “on”, this whole row will be inverted. The results for our example are shown in a table below.
Row number |
Row number modulo divisor (8) |
Value |
---|---|---|
0 |
0 |
pattern |
1 |
1 |
inverted pattern |
2 |
2 |
pattern |
3 |
3 |
pattern |
4 |
4 |
inverted pattern |
5 |
5 |
pattern |
6 |
6 |
pattern |
7 |
7 |
inverted pattern |
8 |
0 |
pattern |
9 |
1 |
inverted pattern |
10 |
2 |
pattern |
11 |
3 |
pattern |
12 |
4 |
inverted pattern |