Skip to content

Commit 2ee5f7f

Browse files
committed
sim: Test software rollback protection
Signed-off-by: Håkon Øye Amundsen <[email protected]> Signed-off-by: David Brown <[email protected]>
1 parent 07e1381 commit 2ee5f7f

File tree

9 files changed

+104
-5
lines changed

9 files changed

+104
-5
lines changed

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ matrix:
4141
env: MULTI_FEATURES="sig-ecdsa enc-kw validate-primary-slot" TEST=sim
4242
- os: linux
4343
env: MULTI_FEATURES="sig-rsa validate-primary-slot overwrite-only large-write,sig-ecdsa enc-ec256 validate-primary-slot" TEST=sim
44+
- os: linux
45+
env: MULTI_FEATURES="sig-rsa validate-primary-slot overwrite-only downgrade-prevention" TEST=sim
4446

4547
- os: linux
4648
language: go

sim/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enc-ec256 = ["mcuboot-sys/enc-ec256"]
2020
bootstrap = ["mcuboot-sys/bootstrap"]
2121
multiimage = ["mcuboot-sys/multiimage"]
2222
large-write = []
23+
downgrade-prevention = ["mcuboot-sys/downgrade-prevention"]
2324

2425
[dependencies]
2526
byteorder = "1.3"

sim/mcuboot-sys/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ bootstrap = []
4747
# Support multiple images (currently 2 instead of 1).
4848
multiimage = []
4949

50+
# Check (in software) against version downgrades.
51+
downgrade-prevention = []
52+
5053
[build-dependencies]
5154
cc = "1.0.25"
5255

sim/mcuboot-sys/build.rs

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ fn main() {
2222
let enc_ec256 = env::var("CARGO_FEATURE_ENC_EC256").is_ok();
2323
let bootstrap = env::var("CARGO_FEATURE_BOOTSTRAP").is_ok();
2424
let multiimage = env::var("CARGO_FEATURE_MULTIIMAGE").is_ok();
25+
let downgrade_prevention = env::var("CARGO_FEATURE_DOWNGRADE_PREVENTION").is_ok();
2526

2627
let mut conf = cc::Build::new();
2728
conf.define("__BOOTSIM__", None);
@@ -31,6 +32,10 @@ fn main() {
3132
conf.define("MCUBOOT_MAX_IMG_SECTORS", Some("128"));
3233
conf.define("MCUBOOT_IMAGE_NUMBER", Some(if multiimage { "2" } else { "1" }));
3334

35+
if downgrade_prevention && !overwrite_only {
36+
panic!("Downgrade prevention requires overwrite only");
37+
}
38+
3439
if bootstrap {
3540
conf.define("MCUBOOT_BOOTSTRAP", None);
3641
}
@@ -39,6 +44,10 @@ fn main() {
3944
conf.define("MCUBOOT_VALIDATE_PRIMARY_SLOT", None);
4045
}
4146

47+
if downgrade_prevention {
48+
conf.define("MCUBOOT_DOWNGRADE_PREVENTION", None);
49+
}
50+
4251
// Currently no more than one sig type can be used simultaneously.
4352
if vec![sig_rsa, sig_rsa3072, sig_ecdsa, sig_ed25519].iter()
4453
.fold(0, |sum, &v| sum + v as i32) > 1 {

sim/src/caps.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub enum Caps {
2222
Ed25519 = (1 << 9),
2323
EncEc256 = (1 << 10),
2424
SwapUsingMove = (1 << 11),
25+
DowngradePrevention = (1 << 12),
2526
}
2627

2728
impl Caps {

sim/src/depends.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,28 @@ pub trait Depender {
2222
/// A boring image is used when we aren't testing dependencies. There will
2323
/// be meaningful version numbers. The size field is the image number we
2424
/// are.
25-
pub struct BoringDep(pub usize);
25+
pub struct BoringDep {
26+
number: usize,
27+
test: DepTest,
28+
}
29+
30+
impl BoringDep {
31+
pub fn new(number: usize, test: &DepTest) -> BoringDep {
32+
BoringDep {
33+
number: number,
34+
test: test.clone(),
35+
}
36+
}
37+
}
2638

2739
impl Depender for BoringDep {
2840
fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion {
29-
ImageVersion::new_synthetic(self.0 as u8, slot as u8, 0)
41+
let slot = if self.test.downgrade {
42+
1 - slot
43+
} else {
44+
slot
45+
};
46+
ImageVersion::new_synthetic(self.number as u8, slot as u8, 0)
3047
}
3148

3249
fn my_deps(&self, _offset: usize, _slot: usize) -> Vec<ImageVersion> {
@@ -48,6 +65,10 @@ pub struct DepTest {
4865

4966
/// What is the expected outcome of the upgrade.
5067
pub upgrades: [UpgradeInfo; 2],
68+
69+
/// Should this be considered a downgrade (cause the version number to
70+
/// decrease).
71+
pub downgrade: bool,
5172
}
5273

5374
/// Describes the various types of dependency information that can be
@@ -81,6 +102,15 @@ pub enum UpgradeInfo {
81102
pub static NO_DEPS: DepTest = DepTest {
82103
depends: [DepType::Nothing, DepType::Nothing],
83104
upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded],
105+
downgrade: false,
106+
};
107+
108+
/// A "test" with no dependency information, and the images marked as a
109+
/// downgrade.
110+
pub static REV_DEPS: DepTest = DepTest {
111+
depends: [DepType::Nothing, DepType::Nothing],
112+
upgrades: [UpgradeInfo::Held, UpgradeInfo::Held],
113+
downgrade: true,
84114
};
85115

86116
/// A PairDep describes the dependencies between two pairs.
@@ -106,6 +136,11 @@ impl PairDep {
106136

107137
impl Depender for PairDep {
108138
fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion {
139+
let slot = if self.test.downgrade {
140+
1 - slot
141+
} else {
142+
slot
143+
};
109144
ImageVersion::new_synthetic(self.number as u8, slot as u8, 0)
110145
}
111146

sim/src/image.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use crate::depends::{
4545
Depender,
4646
DepTest,
4747
DepType,
48+
NO_DEPS,
4849
PairDep,
4950
UpgradeInfo,
5051
};
@@ -177,7 +178,7 @@ impl ImagesBuilder {
177178
let dep: Box<dyn Depender> = if num_images > 1 {
178179
Box::new(PairDep::new(num_images, image_num, deps))
179180
} else {
180-
Box::new(BoringDep(image_num))
181+
Box::new(BoringDep::new(image_num, deps))
181182
};
182183
let primaries = install_image(&mut flash, &slots[0], 42784, &*dep, false);
183184
let upgrades = match deps.depends[image_num] {
@@ -222,7 +223,7 @@ impl ImagesBuilder {
222223
pub fn make_bad_secondary_slot_image(self) -> Images {
223224
let mut bad_flash = self.flash;
224225
let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
225-
let dep = BoringDep(image_num);
226+
let dep = BoringDep::new(image_num, &NO_DEPS);
226227
let primaries = install_image(&mut bad_flash, &slots[0], 32784, &dep, false);
227228
let upgrades = install_image(&mut bad_flash, &slots[1], 41928, &dep, true);
228229
OneImage {
@@ -569,6 +570,37 @@ impl Images {
569570
fails > 0
570571
}
571572

573+
// Test that an upgrade is rejected. Assumes that the image was build
574+
// such that the upgrade is instead a downgrade.
575+
pub fn run_nodowngrade(&self) -> bool {
576+
if !Caps::DowngradePrevention.present() {
577+
return false;
578+
}
579+
580+
let mut flash = self.flash.clone();
581+
let mut fails = 0;
582+
583+
info!("Try no downgrade");
584+
585+
// First, do a normal upgrade.
586+
let (result, _) = c::boot_go(&mut flash, &self.areadesc, None, false);
587+
if result != 0 {
588+
warn!("Failed first boot");
589+
fails += 1;
590+
}
591+
592+
if !self.verify_images(&flash, 0, 0) {
593+
warn!("Failed verification after downgrade rejection");
594+
fails += 1;
595+
}
596+
597+
if fails > 0 {
598+
error!("Error testing downgrade rejection");
599+
}
600+
601+
fails > 0
602+
}
603+
572604
// Tests a new image written to the primary slot that already has magic and
573605
// image_ok set while there is no image on the secondary slot, so no revert
574606
// should ever happen...
@@ -1450,6 +1482,7 @@ fn install_ptable(flash: &mut SimMultiFlash, areadesc: &AreaDesc) {
14501482

14511483
/// The image header
14521484
#[repr(C)]
1485+
#[derive(Debug)]
14531486
pub struct ImageHeader {
14541487
magic: u32,
14551488
load_addr: u32,

sim/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ pub use crate::{
2323
DepTest,
2424
DepType,
2525
UpgradeInfo,
26-
NO_DEPS,},
26+
NO_DEPS,
27+
REV_DEPS,
28+
},
2729
image::{
2830
ImagesBuilder,
2931
Images,

sim/tests/core.rs

+13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use bootsim::{
1212
ImagesBuilder,
1313
Images,
1414
NO_DEPS,
15+
REV_DEPS,
1516
testlog,
1617
};
1718
use std::{
@@ -54,6 +55,7 @@ sim_test!(perm_with_random_fails, make_image(&NO_DEPS, true), run_perm_with_rand
5455
sim_test!(norevert, make_image(&NO_DEPS, true), run_norevert());
5556
sim_test!(status_write_fails_complete, make_image(&NO_DEPS, true), run_with_status_fails_complete());
5657
sim_test!(status_write_fails_with_reset, make_image(&NO_DEPS, true), run_with_status_fails_with_reset());
58+
sim_test!(downgrade_prevention, make_image(&REV_DEPS, true), run_nodowngrade());
5759

5860
// Test various combinations of incorrect dependencies.
5961
test_shell!(dependency_combos, r, {
@@ -75,18 +77,21 @@ pub static TEST_DEPS: &[DepTest] = &[
7577
DepTest {
7678
depends: [DepType::Nothing, DepType::Nothing],
7779
upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded],
80+
downgrade: false,
7881
},
7982

8083
// If all of the dependencies are met, we should also upgrade.
8184
DepTest {
8285
depends: [DepType::Correct, DepType::Correct],
8386
upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded],
87+
downgrade: false,
8488
},
8589

8690
// If none of the dependencies are met, the images should be held.
8791
DepTest {
8892
depends: [DepType::Newer, DepType::Newer],
8993
upgrades: [UpgradeInfo::Held, UpgradeInfo::Held],
94+
downgrade: false,
9095
},
9196

9297
// If the first image is not met, we should hold back on the
@@ -95,50 +100,58 @@ pub static TEST_DEPS: &[DepTest] = &[
95100
DepTest {
96101
depends: [DepType::Newer, DepType::Correct],
97102
upgrades: [UpgradeInfo::Held, UpgradeInfo::Held],
103+
downgrade: false,
98104
},
99105

100106
// Test the variant in the other direction.
101107
DepTest {
102108
depends: [DepType::Correct, DepType::Newer],
103109
upgrades: [UpgradeInfo::Held, UpgradeInfo::Held],
110+
downgrade: false,
104111
},
105112

106113
// Test where only the first image is upgraded, and there are no
107114
// dependencies.
108115
DepTest {
109116
depends: [DepType::Nothing, DepType::NoUpgrade],
110117
upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Held],
118+
downgrade: false,
111119
},
112120

113121
// Test one image with a valid dependency on the first image.
114122
DepTest {
115123
depends: [DepType::OldCorrect, DepType::NoUpgrade],
116124
upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Held],
125+
downgrade: false,
117126
},
118127

119128
// Test one image with an invalid dependency on the first image.
120129
DepTest {
121130
depends: [DepType::Newer, DepType::NoUpgrade],
122131
upgrades: [UpgradeInfo::Held, UpgradeInfo::Held],
132+
downgrade: false,
123133
},
124134

125135
// Test where only the second image is upgraded, and there are no
126136
// dependencies.
127137
DepTest {
128138
depends: [DepType::NoUpgrade, DepType::Nothing],
129139
upgrades: [UpgradeInfo::Held, UpgradeInfo::Upgraded],
140+
downgrade: false,
130141
},
131142

132143
// Test one image with a valid dependency on the second image.
133144
DepTest {
134145
depends: [DepType::NoUpgrade, DepType::OldCorrect],
135146
upgrades: [UpgradeInfo::Held, UpgradeInfo::Upgraded],
147+
downgrade: false,
136148
},
137149

138150
// Test one image with an invalid dependency on the second image.
139151
DepTest {
140152
depends: [DepType::NoUpgrade, DepType::Newer],
141153
upgrades: [UpgradeInfo::Held, UpgradeInfo::Held],
154+
downgrade: false,
142155
},
143156
];
144157

0 commit comments

Comments
 (0)