From 85ebdd088146be30e67612a5a05b61ed3a50febc Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 27 Apr 2023 00:41:24 -0500 Subject: [PATCH] Reintroduced Brent's algorithm for cycle detection in lfsr_mount --- lfs.c | 29 ++++++++++++++++++++++++-- scripts/dbgrbyd.py | 2 +- tests/test_superblocks.toml | 41 +++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/lfs.c b/lfs.c index 236ffae7..f5be8501 100644 --- a/lfs.c +++ b/lfs.c @@ -4108,6 +4108,12 @@ typedef struct lfsr_mpair { #define LFSR_MPAIR(block0, block1) ((lfsr_mpair_t){.blocks={block0, block1}}) +static inline bool lfsr_mpair_eq(lfsr_mpair_t a, lfsr_mpair_t b) { + // allow either order + return (a.blocks[0] == b.blocks[0] && a.blocks[1] == b.blocks[1]) + || (a.blocks[0] == b.blocks[1] && a.blocks[1] == b.blocks[0]); +} + // 2 leb128 => 10 bytes (worst case) #define LFSR_MPAIR_DSIZE (5+5) @@ -4378,13 +4384,32 @@ static int lfsr_mountinited(lfs_t *lfs) { // scan for the first non-fake superblock lfsr_mpair_t mpair = LFSR_MPAIR(0, 1); lfsr_mdir_t mdir; + // detect cycles using Brent's algorithm + lfsr_mpair_t tortoise = LFSR_MPAIR(-1, -1); + lfs_size_t tortoise_i = 1; + lfs_size_t tortoise_period = 1; while (true) { // TODO detect cycles with Brent's algorithm + // found a cycle? + if (lfsr_mpair_eq(mpair, tortoise)) { + LFS_WARN("Cycle detected in superblocks"); + return LFS_ERR_CORRUPT; + } + if (tortoise_i == tortoise_period) { + tortoise = mpair; + tortoise_i = 0; + tortoise_period *= 2; + } + tortoise_i += 1; // fetch next possible superblock int err = lfsr_mdir_fetch(lfs, &mdir, mpair, NULL); if (err) { LFS_ERROR("No littlefs superblock found"); + // treat corrupt errors as invalid littlefs images + if (err == LFS_ERR_CORRUPT) { + return LFS_ERR_INVAL; + } return err; } @@ -4667,7 +4692,7 @@ static int lfsr_formatinited(lfs_t *lfs) { for (int i = 0; i < 2; i++) { // write superblock to both rbyds in the root supermdir to hopefully // avoid mounting an older filesystem on disk - lfsr_rbyd_t rbyd = {.block=i, .rev=1, .off=0, .trunk=0}; + lfsr_rbyd_t rbyd = {.block=i, .rev=i+1, .off=0, .trunk=0}; int err = lfsr_bd_erase(lfs, rbyd.block); if (err) { @@ -8288,7 +8313,7 @@ static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { while (!lfs_pair_isnull(dir.tail)) { // detect cycles with Brent's algorithm if (lfs_pair_issync(dir.tail, tortoise)) { - LFS_WARN("Cycle detected in tail list"); + LFS_ERROR("Cycle detected in tail list"); err = LFS_ERR_CORRUPT; goto cleanup; } diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index fbe440e7..2d2c3377 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -878,7 +878,7 @@ def main(disk, block1=0, block2=None, *, weights.append(weight) # compare with sequence arithmetic - if off and ((rev - revs[i]) & 0x80000000): + if off and not ((rev - revs[i]) & 0x80000000): i = len(revs)-1 # print contents of the winning metadata block diff --git a/tests/test_superblocks.toml b/tests/test_superblocks.toml index 694ae731..85ecaa35 100644 --- a/tests/test_superblocks.toml +++ b/tests/test_superblocks.toml @@ -14,6 +14,47 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' +# reentrant format +[cases.test_superblocks_reentrant_format] +reentrant = true +code = ''' + lfs_t lfs; + int err = lfsr_mount(&lfs, cfg); + if (err) { + lfsr_format(&lfs, cfg) => 0; + lfsr_mount(&lfs, cfg) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# invalid mount +[cases.test_superblocks_invalid] +code = ''' + lfs_t lfs; + lfsr_mount(&lfs, cfg) => LFS_ERR_INVAL; +''' + +# superblock cycle detection +[cases.test_superblocks_cycle] +in = 'lfs.c' +code = ''' + // create a cycle + lfs_t lfs; + lfsr_format(&lfs, cfg) => 0; + lfsr_mount(&lfs, cfg) => 0; + lfsr_mdir_t mdir; + lfsr_mdir_fetch(&lfs, &mdir, LFSR_MPAIR(0, 1), NULL) => 0; + uint8_t buf[LFSR_MPAIR_DSIZE]; + lfs_ssize_t d = lfsr_mpair_todisk(&lfs, LFSR_MPAIR(0, 1), buf); + assert(d >= 0); + lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( + LFSR_ATTR(-1, SUPERMDIR, 0, buf, d))) => 0; + lfsr_unmount(&lfs) => 0; + + // now detect the cycle + lfsr_mount(&lfs, cfg) => LFS_ERR_CORRUPT; +''' +