Skip to content

Commit d7a9119

Browse files
authored
Merge pull request #1027 from littlefs-project/fix-seek-overflow-ub
Fix seek undefined behavior on signed integer overflow
2 parents 2ba4280 + abaec45 commit d7a9119

File tree

2 files changed

+113
-11
lines changed

2 files changed

+113
-11
lines changed

lfs.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3664,22 +3664,16 @@ static lfs_ssize_t lfs_file_write_(lfs_t *lfs, lfs_file_t *file,
36643664
static lfs_soff_t lfs_file_seek_(lfs_t *lfs, lfs_file_t *file,
36653665
lfs_soff_t off, int whence) {
36663666
// find new pos
3667+
//
3668+
// fortunately for us, littlefs is limited to 31-bit file sizes, so we
3669+
// don't have to worry too much about integer overflow
36673670
lfs_off_t npos = file->pos;
36683671
if (whence == LFS_SEEK_SET) {
36693672
npos = off;
36703673
} else if (whence == LFS_SEEK_CUR) {
3671-
if ((lfs_soff_t)file->pos + off < 0) {
3672-
return LFS_ERR_INVAL;
3673-
} else {
3674-
npos = file->pos + off;
3675-
}
3674+
npos = file->pos + (lfs_off_t)off;
36763675
} else if (whence == LFS_SEEK_END) {
3677-
lfs_soff_t res = lfs_file_size_(lfs, file) + off;
3678-
if (res < 0) {
3679-
return LFS_ERR_INVAL;
3680-
} else {
3681-
npos = res;
3682-
}
3676+
npos = (lfs_off_t)lfs_file_size_(lfs, file) + (lfs_off_t)off;
36833677
}
36843678

36853679
if (npos > lfs->file_max) {

tests/test_seek.toml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,111 @@ code = '''
405405
lfs_file_close(&lfs, &file) => 0;
406406
lfs_unmount(&lfs) => 0;
407407
'''
408+
409+
410+
# test possible overflow/underflow conditions
411+
#
412+
# note these need -fsanitize=undefined to consistently detect
413+
# overflow/underflow conditions
414+
415+
[cases.test_seek_filemax]
416+
code = '''
417+
lfs_t lfs;
418+
lfs_format(&lfs, cfg) => 0;
419+
lfs_mount(&lfs, cfg) => 0;
420+
lfs_file_t file;
421+
lfs_file_open(&lfs, &file, "kitty",
422+
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
423+
uint8_t buffer[1024];
424+
strcpy((char*)buffer, "kittycatcat");
425+
size_t size = strlen((char*)buffer);
426+
lfs_file_write(&lfs, &file, buffer, size) => size;
427+
428+
// seek with LFS_SEEK_SET
429+
lfs_file_seek(&lfs, &file, LFS_FILE_MAX, LFS_SEEK_SET) => LFS_FILE_MAX;
430+
431+
// seek with LFS_SEEK_CUR
432+
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_CUR) => LFS_FILE_MAX;
433+
434+
// the file hasn't changed size, so seek end takes us back to the offset=0
435+
lfs_file_seek(&lfs, &file, +10, LFS_SEEK_END) => size+10;
436+
437+
lfs_file_close(&lfs, &file) => 0;
438+
lfs_unmount(&lfs) => 0;
439+
'''
440+
441+
[cases.test_seek_underflow]
442+
code = '''
443+
lfs_t lfs;
444+
lfs_format(&lfs, cfg) => 0;
445+
lfs_mount(&lfs, cfg) => 0;
446+
lfs_file_t file;
447+
lfs_file_open(&lfs, &file, "kitty",
448+
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
449+
uint8_t buffer[1024];
450+
strcpy((char*)buffer, "kittycatcat");
451+
size_t size = strlen((char*)buffer);
452+
lfs_file_write(&lfs, &file, buffer, size) => size;
453+
454+
// underflow with LFS_SEEK_CUR, should error
455+
lfs_file_seek(&lfs, &file, -(size+10), LFS_SEEK_CUR) => LFS_ERR_INVAL;
456+
lfs_file_seek(&lfs, &file, -LFS_FILE_MAX, LFS_SEEK_CUR) => LFS_ERR_INVAL;
457+
lfs_file_seek(&lfs, &file, -(size+LFS_FILE_MAX), LFS_SEEK_CUR)
458+
=> LFS_ERR_INVAL;
459+
460+
// underflow with LFS_SEEK_END, should error
461+
lfs_file_seek(&lfs, &file, -(size+10), LFS_SEEK_END) => LFS_ERR_INVAL;
462+
lfs_file_seek(&lfs, &file, -LFS_FILE_MAX, LFS_SEEK_END) => LFS_ERR_INVAL;
463+
lfs_file_seek(&lfs, &file, -(size+LFS_FILE_MAX), LFS_SEEK_END)
464+
=> LFS_ERR_INVAL;
465+
466+
// file pointer should not have changed
467+
lfs_file_tell(&lfs, &file) => size;
468+
469+
lfs_file_close(&lfs, &file) => 0;
470+
lfs_unmount(&lfs) => 0;
471+
'''
472+
473+
[cases.test_seek_overflow]
474+
code = '''
475+
lfs_t lfs;
476+
lfs_format(&lfs, cfg) => 0;
477+
lfs_mount(&lfs, cfg) => 0;
478+
lfs_file_t file;
479+
lfs_file_open(&lfs, &file, "kitty",
480+
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
481+
uint8_t buffer[1024];
482+
strcpy((char*)buffer, "kittycatcat");
483+
size_t size = strlen((char*)buffer);
484+
lfs_file_write(&lfs, &file, buffer, size) => size;
485+
486+
// seek to LFS_FILE_MAX
487+
lfs_file_seek(&lfs, &file, LFS_FILE_MAX, LFS_SEEK_SET) => LFS_FILE_MAX;
488+
489+
// overflow with LFS_SEEK_CUR, should error
490+
lfs_file_seek(&lfs, &file, +10, LFS_SEEK_CUR) => LFS_ERR_INVAL;
491+
lfs_file_seek(&lfs, &file, +LFS_FILE_MAX, LFS_SEEK_CUR) => LFS_ERR_INVAL;
492+
493+
// LFS_SEEK_SET/END don't care about the current file position, but we can
494+
// still overflow with a large offset
495+
496+
// overflow with LFS_SEEK_SET, should error
497+
lfs_file_seek(&lfs, &file,
498+
+((uint32_t)LFS_FILE_MAX+10),
499+
LFS_SEEK_SET) => LFS_ERR_INVAL;
500+
lfs_file_seek(&lfs, &file,
501+
+((uint32_t)LFS_FILE_MAX+(uint32_t)LFS_FILE_MAX),
502+
LFS_SEEK_SET) => LFS_ERR_INVAL;
503+
504+
// overflow with LFS_SEEK_END, should error
505+
lfs_file_seek(&lfs, &file, +(LFS_FILE_MAX-size+10), LFS_SEEK_END)
506+
=> LFS_ERR_INVAL;
507+
lfs_file_seek(&lfs, &file, +(LFS_FILE_MAX-size+LFS_FILE_MAX), LFS_SEEK_END)
508+
=> LFS_ERR_INVAL;
509+
510+
// file pointer should not have changed
511+
lfs_file_tell(&lfs, &file) => LFS_FILE_MAX;
512+
513+
lfs_file_close(&lfs, &file) => 0;
514+
lfs_unmount(&lfs) => 0;
515+
'''

0 commit comments

Comments
 (0)