Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 142 additions & 64 deletions boot/bootutil/src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ static struct sector_buffer_t sector_buffers;
#endif
#endif

#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 && defined(MCUBOOT_OVERWRITE_ONLY) && \
defined(MCUBOOT_DOWNGRADE_PREVENTION)
/* s0/s1 package version of the current MCUboot image */
static const struct image_version mcuboot_s0_s1_image_version = {
.iv_major = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MAJOR,
.iv_minor = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MINOR,
.iv_revision = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_REVISION,
.iv_build_num = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_BUILD_NUMBER,
};
#endif

#if (BOOT_IMAGE_NUMBER > 1)
#define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x))
#else
Expand Down Expand Up @@ -153,15 +164,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all,
*
* Failure to read any headers is a fatal error.
*/
#ifdef PM_S1_ADDRESS
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
/* Patch needed for NCS. The primary slot of the second image
* (image 1) will not contain a valid image header until an upgrade
* of mcuboot has happened (filling S1 with the new version).
*/
if (BOOT_CURR_IMG(state) == 1 && i == 0) {
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER && i == 0) {
continue;
}
#endif /* PM_S1_ADDRESS */
#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */
if (i > 0 && !require_all) {
return 0;
} else {
Expand Down Expand Up @@ -1160,17 +1171,51 @@ boot_validate_slot(struct boot_loader_state *state, int slot,

#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) \
&& defined(CONFIG_PCD_APP) && defined(CONFIG_PCD_READ_NETCORE_APP_VERSION)
if (BOOT_CURR_IMG(state) == 1) {
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) {
rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SECONDARY_SLOT));
} else {
rc = boot_version_cmp(
&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver,
&boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver);

#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
/* Also check the new version of MCUboot against that of the current s0/s1 MCUboot
* trailer version to prevent downgrades
*/
int version_check;

version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver,
&mcuboot_s0_s1_image_version);

/* Only update rc if the currently running version is newer */
if (version_check < rc) {
rc = version_check;
}
}
#endif
}
#else
rc = boot_version_cmp(
&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver,
&boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver);

#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
/* Also check the new version of MCUboot against that of the current s0/s1 MCUboot
* trailer version to prevent downgrades
*/
int version_check;

version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver,
&mcuboot_s0_s1_image_version);

/* Only update rc if the currently running version is newer */
if (version_check < rc) {
rc = version_check;
}
}
#endif
#endif
if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) {
BOOT_LOG_ERR("insufficient version in secondary slot");
Expand Down Expand Up @@ -1229,36 +1274,55 @@ boot_validate_slot(struct boot_loader_state *state, int slot,
struct image_header *secondary_hdr = boot_img_hdr(state, slot);
uint32_t reset_value = 0;
uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value);
uint32_t min_addr, max_addr;
bool check_addresses = false;

rc = flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value));
if (rc != 0) {
fih_rc = FIH_NO_BOOTABLE_IMAGE;
goto out;
}

uint32_t min_addr, max_addr;

#ifdef PM_CPUNET_APP_ADDRESS
/* The primary slot for the network core is emulated in RAM.
* Its flash_area hasn't got relevant boundaries.
* Therfore need to override its boundaries for the check.
*/
if (BOOT_CURR_IMG(state) == 1) {
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) {
min_addr = PM_CPUNET_APP_ADDRESS;
max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE;
#ifdef PM_S1_ADDRESS
} else if (BOOT_CURR_IMG(state) == 0) {
check_addresses = true;
} else
#endif
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
min_addr = PM_S0_ADDRESS;
max_addr = pri_fa->fa_off + pri_fa->fa_size;
max_addr = (PM_S0_ADDRESS + PM_S0_SIZE);
#else
min_addr = PM_S1_ADDRESS;
max_addr = (PM_S1_ADDRESS + PM_S1_SIZE);
#endif
check_addresses = true;
} else
#endif
{
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
min_addr = MIN(pri_fa->fa_off, PM_S0_ADDRESS);
max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S0_ADDRESS + PM_S0_SIZE));
#else
min_addr = MIN(pri_fa->fa_off, PM_S1_ADDRESS);
max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S1_ADDRESS + PM_S1_SIZE));
#endif
#else
min_addr = pri_fa->fa_off;
max_addr = pri_fa->fa_off + pri_fa->fa_size;
#endif
check_addresses = true;
}

if (reset_value < min_addr || reset_value> (max_addr)) {
if (check_addresses == true && (reset_value < min_addr || reset_value > max_addr)) {
BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot");
BOOT_LOG_ERR("Erasing image from secondary slot");

Expand Down Expand Up @@ -1335,36 +1399,42 @@ boot_update_security_counter(uint8_t image_index, int slot,
#define SEC_SLOT_TOUCHED 1
#define SEC_SLOT_ASSIGNED 2

#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \
!defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE)
/* This configuration is peculiar - the one physical secondary slot is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how it is resolved now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has it's own dedicated image number (present in MCUboot only, not present in the application)

* mocking two logical secondary
*/
#define SEC_SLOT_PHYSICAL_CNT 1
#else
#define SEC_SLOT_PHYSICAL_CNT MCUBOOT_IMAGE_NUMBER
#endif

static uint8_t sec_slot_assignmnet[SEC_SLOT_PHYSICAL_CNT] = {0};
static uint8_t sec_slot_assignmnet[MCUBOOT_IMAGE_NUMBER] = {0};

static inline void sec_slot_touch(struct boot_loader_state *state)
{
uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state);
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now sec_slot_assignment can be marked twice. I do not understand that.

Copy link
Contributor Author

@nordicjm nordicjm Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature is completely broken in child/parent image as I've said many time, depending on what you have, you can get MCUboot to mark an image for upgrade, after which the cleanup code will run, then it will upgrade the primary for 4 bytes, and bricks the module. And brick meaning if access port protection is enabled, the device is irrecoverably dead, likely out in the field. This code here actually works by checking the slots properly and marking both as used if it is a shared slot. Note that all of these test cases are checked as part of sdk-mcuboot CI and pass.

The original code was added at the complete wrong point, it should have been added after the update, not before the update runs, because essentially you have the equivalent of use-after-free-pointer

if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
if (sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) {
sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_TOUCHED;
}
} else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
if (sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) {
sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_TOUCHED;
}
}
#endif

if (SEC_SLOT_VIRGIN == sec_slot_assignmnet[idx]) {
sec_slot_assignmnet[idx] = SEC_SLOT_TOUCHED;
if (sec_slot_assignmnet[BOOT_CURR_IMG(state)] == SEC_SLOT_VIRGIN) {
sec_slot_assignmnet[BOOT_CURR_IMG(state)] = SEC_SLOT_TOUCHED;
}
}

static inline void sec_slot_mark_assigned(struct boot_loader_state *state)
{
uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state);
#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED;
} else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED;
}
#endif

sec_slot_assignmnet[idx] = SEC_SLOT_ASSIGNED;
sec_slot_assignmnet[BOOT_CURR_IMG(state)] = SEC_SLOT_ASSIGNED;
}

/**
* Cleanu up all secondary slot which couldn't be assigned to any primary slot.
* Cleanup up all secondary slot which couldn't be assigned to any primary slot.
*
* This function erases content of each secondary slot which contains valid
* header but couldn't be assigned to any of supported primary images.
Expand All @@ -1376,7 +1446,7 @@ static void sec_slot_cleanup_if_unusable(void)
{
uint8_t idx;

for (idx = 0; idx < SEC_SLOT_PHYSICAL_CNT; idx++) {
for (idx = 0; idx < MCUBOOT_IMAGE_NUMBER; idx++) {
if (SEC_SLOT_TOUCHED == sec_slot_assignmnet[idx]) {
const struct flash_area *secondary_fa;
int rc;
Expand All @@ -1386,12 +1456,12 @@ static void sec_slot_cleanup_if_unusable(void)
if (!rc) {
rc = flash_area_erase(secondary_fa, 0, secondary_fa->fa_size);
if (!rc) {
BOOT_LOG_ERR("Cleaned-up secondary slot of %d. image.", idx);
BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", idx);
}
}

if (rc) {
BOOT_LOG_ERR("Can not cleanup secondary slot of %d. image.", idx);
BOOT_LOG_ERR("Failed to clean-up secondary slot of image %d: %d", idx, rc);
}
}
}
Expand Down Expand Up @@ -1428,7 +1498,7 @@ boot_validated_swap_type(struct boot_loader_state *state,
owner_nsib[BOOT_CURR_IMG(state)] = false;
#endif

#if defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP)
#if defined(PM_S1_ADDRESS) || defined(PM_CPUNET_B0N_ADDRESS)
const struct flash_area *secondary_fa =
BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
struct image_header *hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT);
Expand Down Expand Up @@ -1466,31 +1536,29 @@ boot_validated_swap_type(struct boot_loader_state *state,
}

/* Check start and end of primary slot for current image */
if (reset_addr < primary_fa->fa_off) {
#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE)
const struct flash_area *nsib_fa;

/* NSIB upgrade slot */
rc = flash_area_open((uint32_t)_image_1_primary_slot_id,
&nsib_fa);

if (rc != 0) {
return BOOT_SWAP_TYPE_FAIL;
}

/* Image is placed before Primary and within the NSIB slot */
if (reset_addr > nsib_fa->fa_off
&& reset_addr < (nsib_fa->fa_off + nsib_fa->fa_size)) {
/* Set primary to be NSIB upgrade slot */
BOOT_IMG_AREA(state, 0) = nsib_fa;
owner_nsib[BOOT_CURR_IMG(state)] = true;
}
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) {
#else
return BOOT_SWAP_TYPE_NONE;

if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) {
#endif
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
/* This is not the s0/s1 upgrade image but the application image, pretend
* there is no image so the NSIB update can be loaded
*/
return BOOT_SWAP_TYPE_NONE;
}

} else if (reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) {
owner_nsib[BOOT_CURR_IMG(state)] = true;
#if (CONFIG_NCS_IS_VARIANT_IMAGE)
} else if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) {
#else
} else if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) {
#endif
/* NSIB upgrade but for the wrong slot, must be erased */
BOOT_LOG_ERR("Image in slot is for wrong s0/s1 image");
flash_area_erase(secondary_fa, 0, secondary_fa->fa_size);
return BOOT_SWAP_TYPE_FAIL;
} else if (reset_addr < primary_fa->fa_off || reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) {
/* The image in the secondary slot is not intended for any */
return BOOT_SWAP_TYPE_NONE;
}
Expand All @@ -1503,7 +1571,7 @@ boot_validated_swap_type(struct boot_loader_state *state,
sec_slot_mark_assigned(state);
}

#endif /* PM_S1_ADDRESS || CONFIG_SOC_NRF5340_CPUAPP */
#endif /* PM_S1_ADDRESS || PM_CPUNET_B0N_ADDRESS */

swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state));
if (BOOT_IS_UPGRADE(swap_type)) {
Expand Down Expand Up @@ -2035,7 +2103,22 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs)
flash_area_close(fap);
}

swap_run(state, bs, copy_size);
#if defined(PM_S1_ADDRESS) && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1
if (owner_nsib[BOOT_CURR_IMG(state)]) {
if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) {
/* For NSIB, move the image instead of swapping it */
nsib_swap_run(state, bs);

#if defined(CONFIG_REBOOT)
/* Should also reboot at this point so the new S0/S1 update is applied */
sys_reboot(SYS_REBOOT_COLD);
#endif
}
} else
#endif
{
swap_run(state, bs, copy_size);
}

#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
extern int boot_status_fails;
Expand Down Expand Up @@ -2701,12 +2784,6 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
rc = boot_perform_update(state, &bs);
}
assert(rc == 0);
#if defined(PM_S1_ADDRESS) && defined(CONFIG_REBOOT)
if (owner_nsib[BOOT_CURR_IMG(state)]) {
sys_reboot(SYS_REBOOT_COLD);

}
#endif
break;

case BOOT_SWAP_TYPE_FAIL:
Expand Down Expand Up @@ -2780,7 +2857,8 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
* executing MCUBoot image, and is therefore already validated by NSIB and
* does not need to also be validated by MCUBoot.
*/
bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1;
bool image_validated_by_nsib = BOOT_CURR_IMG(state) ==
CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER;
if (!image_validated_by_nsib)
#endif
{
Expand Down
Loading