Skip to content

Commit 2eba2e9

Browse files
rghaddabkartben
authored andcommitted
zms: add more robustiness in extreme cases
When power cuts during a GC operation, the sector is erased again in the next reboot cycle and the cycle_cnt of the empty ATE is incremented. If the same power cut happens 255 times in a row, the empty ATE cycle_cnt will become equal to the close ATE which causes a memory corruption. Fix this by checking the close ATE cycle_cnt before incrementing the empty ATE cycle_cnt. Fixes: zephyrproject-rtos#84874 Signed-off-by: Riadh Ghaddab <[email protected]>
1 parent f1c83bc commit 2eba2e9

File tree

1 file changed

+40
-2
lines changed

1 file changed

+40
-2
lines changed

subsys/fs/zms/zms.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,40 @@ static int zms_add_gc_done_ate(struct zms_fs *fs)
723723
return zms_flash_ate_wrt(fs, &gc_done_ate);
724724
}
725725

726+
/* This function verifies that the cycle_cnt of the close ATE will not be equal
727+
* to the cycle_cnt of the empty ATE after incrementing it.
728+
* This is possible only in these extreme conditions:
729+
* 1- A Garbage collection operation is interrupted due to a power cut
730+
* 2- When rebooting, the sector is erased (cycle_cnt incremented)
731+
* 3- Garbage collection restarts again and got interrupted again by a power cut
732+
* 4- Steps [2..3] occurred 255 times in a row
733+
* At this point the sector that we should erase becomes closed.
734+
*/
735+
static inline int zms_verify_and_increment_cycle_cnt(struct zms_fs *fs, uint64_t addr,
736+
uint8_t *cycle_cnt)
737+
{
738+
int rc;
739+
uint64_t close_addr;
740+
struct zms_ate close_ate;
741+
742+
close_addr = zms_close_ate_addr(fs, addr);
743+
/* Read the second ate in the sector to get the close ATE */
744+
rc = zms_flash_ate_rd(fs, close_addr, &close_ate);
745+
if (rc < 0) {
746+
return rc;
747+
}
748+
749+
*cycle_cnt = (*cycle_cnt + 1) % BIT(8);
750+
/* Verify that the close cycle_cnt is not equal to the incremented value.
751+
* If they are equal increment it again.
752+
*/
753+
if (close_ate.cycle_cnt == *cycle_cnt) {
754+
*cycle_cnt = (*cycle_cnt + 1) % BIT(8);
755+
}
756+
757+
return 0;
758+
}
759+
726760
static int zms_add_empty_ate(struct zms_fs *fs, uint64_t addr)
727761
{
728762
struct zms_ate empty_ate;
@@ -748,8 +782,12 @@ static int zms_add_empty_ate(struct zms_fs *fs, uint64_t addr)
748782
return rc;
749783
}
750784

751-
/* increase cycle counter */
752-
empty_ate.cycle_cnt = (cycle_cnt + 1) % BIT(8);
785+
/* Increase cycle counter */
786+
rc = zms_verify_and_increment_cycle_cnt(fs, addr, &cycle_cnt);
787+
if (rc < 0) {
788+
return rc;
789+
}
790+
empty_ate.cycle_cnt = cycle_cnt;
753791
zms_ate_crc8_update(&empty_ate);
754792

755793
/* Adding empty ate to this sector changes fs->ate_wra value

0 commit comments

Comments
 (0)