1# Tests for recovering from conditions which shouldn't normally 2# happen during normal operation of littlefs 3 4# invalid pointer tests (outside of block_count) 5 6[cases.test_evil_invalid_tail_pointer] 7defines.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL'] 8defines.INVALSET = [0x3, 0x1, 0x2] 9in = "lfs.c" 10code = ''' 11 // create littlefs 12 lfs_t lfs; 13 lfs_format(&lfs, cfg) => 0; 14 15 // change tail-pointer to invalid pointers 16 lfs_init(&lfs, cfg) => 0; 17 lfs_mdir_t mdir; 18 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 19 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 20 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), 21 (lfs_block_t[2]){ 22 (INVALSET & 0x1) ? 0xcccccccc : 0, 23 (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; 24 lfs_deinit(&lfs) => 0; 25 26 // test that mount fails gracefully 27 lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; 28''' 29 30[cases.test_evil_invalid_dir_pointer] 31defines.INVALSET = [0x3, 0x1, 0x2] 32in = "lfs.c" 33code = ''' 34 // create littlefs 35 lfs_t lfs; 36 lfs_format(&lfs, cfg) => 0; 37 // make a dir 38 lfs_mount(&lfs, cfg) => 0; 39 lfs_mkdir(&lfs, "dir_here") => 0; 40 lfs_unmount(&lfs) => 0; 41 42 // change the dir pointer to be invalid 43 lfs_init(&lfs, cfg) => 0; 44 lfs_mdir_t mdir; 45 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 46 // make sure id 1 == our directory 47 uint8_t buffer[1024]; 48 lfs_dir_get(&lfs, &mdir, 49 LFS_MKTAG(0x700, 0x3ff, 0), 50 LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer) 51 => LFS_MKTAG(LFS_TYPE_DIR, 1, strlen("dir_here")); 52 assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0); 53 // change dir pointer 54 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 55 {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, 8), 56 (lfs_block_t[2]){ 57 (INVALSET & 0x1) ? 0xcccccccc : 0, 58 (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; 59 lfs_deinit(&lfs) => 0; 60 61 // test that accessing our bad dir fails, note there's a number 62 // of ways to access the dir, some can fail, but some don't 63 lfs_mount(&lfs, cfg) => 0; 64 struct lfs_info info; 65 lfs_stat(&lfs, "dir_here", &info) => 0; 66 assert(strcmp(info.name, "dir_here") == 0); 67 assert(info.type == LFS_TYPE_DIR); 68 69 lfs_dir_t dir; 70 lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT; 71 lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT; 72 lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT; 73 lfs_file_t file; 74 lfs_file_open(&lfs, &file, "dir_here/file_here", 75 LFS_O_RDONLY) => LFS_ERR_CORRUPT; 76 lfs_file_open(&lfs, &file, "dir_here/file_here", 77 LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_CORRUPT; 78 lfs_unmount(&lfs) => 0; 79''' 80 81[cases.test_evil_invalid_file_pointer] 82in = "lfs.c" 83defines.SIZE = [10, 1000, 100000] # faked file size 84code = ''' 85 // create littlefs 86 lfs_t lfs; 87 lfs_format(&lfs, cfg) => 0; 88 // make a file 89 lfs_mount(&lfs, cfg) => 0; 90 lfs_file_t file; 91 lfs_file_open(&lfs, &file, "file_here", 92 LFS_O_WRONLY | LFS_O_CREAT) => 0; 93 lfs_file_close(&lfs, &file) => 0; 94 lfs_unmount(&lfs) => 0; 95 96 // change the file pointer to be invalid 97 lfs_init(&lfs, cfg) => 0; 98 lfs_mdir_t mdir; 99 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 100 // make sure id 1 == our file 101 uint8_t buffer[1024]; 102 lfs_dir_get(&lfs, &mdir, 103 LFS_MKTAG(0x700, 0x3ff, 0), 104 LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) 105 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); 106 assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); 107 // change file pointer 108 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 109 {LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)), 110 &(struct lfs_ctz){0xcccccccc, lfs_tole32(SIZE)}})) => 0; 111 lfs_deinit(&lfs) => 0; 112 113 // test that accessing our bad file fails, note there's a number 114 // of ways to access the dir, some can fail, but some don't 115 lfs_mount(&lfs, cfg) => 0; 116 struct lfs_info info; 117 lfs_stat(&lfs, "file_here", &info) => 0; 118 assert(strcmp(info.name, "file_here") == 0); 119 assert(info.type == LFS_TYPE_REG); 120 assert(info.size == SIZE); 121 122 lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; 123 lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; 124 lfs_file_close(&lfs, &file) => 0; 125 126 // any allocs that traverse CTZ must unfortunately must fail 127 if (SIZE > 2*BLOCK_SIZE) { 128 lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; 129 } 130 lfs_unmount(&lfs) => 0; 131''' 132 133[cases.test_evil_invalid_ctz_pointer] # invalid pointer in CTZ skip-list test 134defines.SIZE = ['2*BLOCK_SIZE', '3*BLOCK_SIZE', '4*BLOCK_SIZE'] 135in = "lfs.c" 136code = ''' 137 // create littlefs 138 lfs_t lfs; 139 lfs_format(&lfs, cfg) => 0; 140 // make a file 141 lfs_mount(&lfs, cfg) => 0; 142 lfs_file_t file; 143 lfs_file_open(&lfs, &file, "file_here", 144 LFS_O_WRONLY | LFS_O_CREAT) => 0; 145 for (int i = 0; i < SIZE; i++) { 146 char c = 'c'; 147 lfs_file_write(&lfs, &file, &c, 1) => 1; 148 } 149 lfs_file_close(&lfs, &file) => 0; 150 lfs_unmount(&lfs) => 0; 151 // change pointer in CTZ skip-list to be invalid 152 lfs_init(&lfs, cfg) => 0; 153 lfs_mdir_t mdir; 154 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 155 // make sure id 1 == our file and get our CTZ structure 156 uint8_t buffer[4*BLOCK_SIZE]; 157 lfs_dir_get(&lfs, &mdir, 158 LFS_MKTAG(0x700, 0x3ff, 0), 159 LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) 160 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); 161 assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); 162 struct lfs_ctz ctz; 163 lfs_dir_get(&lfs, &mdir, 164 LFS_MKTAG(0x700, 0x3ff, 0), 165 LFS_MKTAG(LFS_TYPE_STRUCT, 1, sizeof(struct lfs_ctz)), &ctz) 166 => LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)); 167 lfs_ctz_fromle32(&ctz); 168 // rewrite block to contain bad pointer 169 uint8_t bbuffer[BLOCK_SIZE]; 170 cfg->read(cfg, ctz.head, 0, bbuffer, BLOCK_SIZE) => 0; 171 uint32_t bad = lfs_tole32(0xcccccccc); 172 memcpy(&bbuffer[0], &bad, sizeof(bad)); 173 memcpy(&bbuffer[4], &bad, sizeof(bad)); 174 cfg->erase(cfg, ctz.head) => 0; 175 cfg->prog(cfg, ctz.head, 0, bbuffer, BLOCK_SIZE) => 0; 176 lfs_deinit(&lfs) => 0; 177 178 // test that accessing our bad file fails, note there's a number 179 // of ways to access the dir, some can fail, but some don't 180 lfs_mount(&lfs, cfg) => 0; 181 struct lfs_info info; 182 lfs_stat(&lfs, "file_here", &info) => 0; 183 assert(strcmp(info.name, "file_here") == 0); 184 assert(info.type == LFS_TYPE_REG); 185 assert(info.size == SIZE); 186 187 lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; 188 lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; 189 lfs_file_close(&lfs, &file) => 0; 190 191 // any allocs that traverse CTZ must unfortunately must fail 192 if (SIZE > 2*BLOCK_SIZE) { 193 lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; 194 } 195 lfs_unmount(&lfs) => 0; 196''' 197 198 199[cases.test_evil_invalid_gstate_pointer] 200defines.INVALSET = [0x3, 0x1, 0x2] 201in = "lfs.c" 202code = ''' 203 // create littlefs 204 lfs_t lfs; 205 lfs_format(&lfs, cfg) => 0; 206 207 // create an invalid gstate 208 lfs_init(&lfs, cfg) => 0; 209 lfs_mdir_t mdir; 210 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 211 lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){ 212 (INVALSET & 0x1) ? 0xcccccccc : 0, 213 (INVALSET & 0x2) ? 0xcccccccc : 0}); 214 lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0; 215 lfs_deinit(&lfs) => 0; 216 217 // test that mount fails gracefully 218 // mount may not fail, but our first alloc should fail when 219 // we try to fix the gstate 220 lfs_mount(&lfs, cfg) => 0; 221 lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT; 222 lfs_unmount(&lfs) => 0; 223''' 224 225# cycle detection/recovery tests 226 227[cases.test_evil_mdir_loop] # metadata-pair threaded-list loop test 228in = "lfs.c" 229code = ''' 230 // create littlefs 231 lfs_t lfs; 232 lfs_format(&lfs, cfg) => 0; 233 234 // change tail-pointer to point to ourself 235 lfs_init(&lfs, cfg) => 0; 236 lfs_mdir_t mdir; 237 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 238 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 239 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), 240 (lfs_block_t[2]){0, 1}})) => 0; 241 lfs_deinit(&lfs) => 0; 242 243 // test that mount fails gracefully 244 lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; 245''' 246 247[cases.test_evil_mdir_loop2] # metadata-pair threaded-list 2-length loop test 248in = "lfs.c" 249code = ''' 250 // create littlefs with child dir 251 lfs_t lfs; 252 lfs_format(&lfs, cfg) => 0; 253 lfs_mount(&lfs, cfg) => 0; 254 lfs_mkdir(&lfs, "child") => 0; 255 lfs_unmount(&lfs) => 0; 256 257 // find child 258 lfs_init(&lfs, cfg) => 0; 259 lfs_mdir_t mdir; 260 lfs_block_t pair[2]; 261 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 262 lfs_dir_get(&lfs, &mdir, 263 LFS_MKTAG(0x7ff, 0x3ff, 0), 264 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) 265 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); 266 lfs_pair_fromle32(pair); 267 // change tail-pointer to point to root 268 lfs_dir_fetch(&lfs, &mdir, pair) => 0; 269 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 270 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), 271 (lfs_block_t[2]){0, 1}})) => 0; 272 lfs_deinit(&lfs) => 0; 273 274 // test that mount fails gracefully 275 lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; 276''' 277 278[cases.test_evil_mdir_loop_child] # metadata-pair threaded-list 1-length child loop test 279in = "lfs.c" 280code = ''' 281 // create littlefs with child dir 282 lfs_t lfs; 283 lfs_format(&lfs, cfg) => 0; 284 lfs_mount(&lfs, cfg) => 0; 285 lfs_mkdir(&lfs, "child") => 0; 286 lfs_unmount(&lfs) => 0; 287 288 // find child 289 lfs_init(&lfs, cfg) => 0; 290 lfs_mdir_t mdir; 291 lfs_block_t pair[2]; 292 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 293 lfs_dir_get(&lfs, &mdir, 294 LFS_MKTAG(0x7ff, 0x3ff, 0), 295 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) 296 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); 297 lfs_pair_fromle32(pair); 298 // change tail-pointer to point to ourself 299 lfs_dir_fetch(&lfs, &mdir, pair) => 0; 300 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 301 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0; 302 lfs_deinit(&lfs) => 0; 303 304 // test that mount fails gracefully 305 lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; 306''' 307