boot/nxboot: add flush barriers and CRC-validate primary before boot

Two hardening fixes for nxboot power-loss resilience:

1. Add flash_partition_flush() calls between critical partition
   operations in perform_update(). Without explicit flush barriers,
   writes may remain buffered in RAM (e.g. via FTL rwbuffer) when
   nxboot proceeds to the next phase. A power loss between phases
   can leave the recovery image uncommitted while the staging
   partition has already been consumed.

   Flush points added:
   - After copy_partition(primary, recovery) completes
   - After copy_partition(update, primary) completes, before
     erasing the staging first sector

2. Replace validate_image_header() with validate_image() in the
   final primary validation path of nxboot_perform_update(). The
   header-only check validates magic and platform identifier but
   does not CRC-check the image body. After an interrupted update,
   a corrupt primary with an intact header would pass this check
   and be booted, resulting in a persistent boot failure.

Signed-off-by: Neil Berkman <neil@xuku.com>
This commit is contained in:
Neil Berkman 2026-03-17 15:39:02 -07:00 committed by Michal Lenc
parent 8cd9e9acc2
commit 4decc1699a
3 changed files with 40 additions and 2 deletions

View file

@ -417,6 +417,7 @@ static int perform_update(struct nxboot_state *state, bool check_only)
syslog(LOG_INFO, "Creating recovery image.\n");
nxboot_progress(nxboot_progress_start, recovery_create);
copy_partition(primary, recovery, state, false);
flash_partition_flush(recovery);
nxboot_progress(nxboot_progress_end);
nxboot_progress(nxboot_progress_start, validate_recovery);
successful = validate_image(recovery);
@ -444,6 +445,8 @@ static int perform_update(struct nxboot_state *state, bool check_only)
nxboot_progress(nxboot_progress_start, update_from_update);
if (copy_partition(update, primary, state, true) >= 0)
{
flash_partition_flush(primary);
/* Erase the first sector of update partition. This marks the
* partition as updated so we don't end up in an update loop.
* The sector is written back again during the image
@ -919,8 +922,7 @@ int nxboot_perform_update(bool check_only)
return ERROR;
}
get_image_header(primary, &header);
if (!validate_image_header(&header))
if (!validate_image(primary))
{
ret = ERROR;
}

View file

@ -76,6 +76,26 @@ int flash_partition_open(const char *path)
return fd;
}
/****************************************************************************
* Name: flash_partition_flush
*
* Description:
* Flushes any buffered writes to the underlying storage. This ensures
* data is physically committed to flash before the caller proceeds.
*
* Input parameters:
* fd: Valid file descriptor.
*
* Returned Value:
* 0 on success, -1 on failure.
*
****************************************************************************/
int flash_partition_flush(int fd)
{
return fsync(fd);
}
/****************************************************************************
* Name: flash_partition_close
*

View file

@ -79,6 +79,22 @@ int flash_partition_open(const char *path);
int flash_partition_close(int fd);
/****************************************************************************
* Name: flash_partition_flush
*
* Description:
* Flushes any buffered writes to the underlying storage.
*
* Input parameters:
* fd: Valid file descriptor.
*
* Returned Value:
* 0 on success, -1 on failure.
*
****************************************************************************/
int flash_partition_flush(int fd);
/****************************************************************************
* Name: flash_partition_write
*