Skip to content

Commit 98ea003

Browse files
Yongpeng YangJaegeuk Kim
authored andcommitted
f2fs: fix out-of-bounds access in sysfs attribute read/write
Some f2fs sysfs attributes suffer from out-of-bounds memory access and incorrect handling of integer values whose size is not 4 bytes. For example: vm:~# echo 65537 > /sys/fs/f2fs/vde/carve_out vm:~# cat /sys/fs/f2fs/vde/carve_out 65537 vm:~# echo 4294967297 > /sys/fs/f2fs/vde/atgc_age_threshold vm:~# cat /sys/fs/f2fs/vde/atgc_age_threshold 1 carve_out maps to {struct f2fs_sb_info}->carve_out, which is a 8-bit integer. However, the sysfs interface allows setting it to a value larger than 255, resulting in an out-of-range update. atgc_age_threshold maps to {struct atgc_management}->age_threshold, which is a 64-bit integer, but its sysfs interface cannot correctly set values larger than UINT_MAX. The root causes are: 1. __sbi_store() treats all default values as unsigned int, which prevents updating integers larger than 4 bytes and causes out-of-bounds writes for integers smaller than 4 bytes. 2. f2fs_sbi_show() also assumes all default values are unsigned int, leading to out-of-bounds reads and incorrect access to integers larger than 4 bytes. This patch introduces {struct f2fs_attr}->size to record the actual size of the integer associated with each sysfs attribute. With this information, sysfs read and write operations can correctly access and update values according to their real data size, avoiding memory corruption and truncation. Fixes: b59d0ba ("f2fs: add sysfs support for controlling the gc_thread") Cc: stable@kernel.org Signed-off-by: Jinbao Liu <liujinbao1@xiaomi.com> Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com> Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
1 parent c0c589f commit 98ea003

1 file changed

Lines changed: 52 additions & 8 deletions

File tree

fs/f2fs/sysfs.c

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct f2fs_attr {
5959
const char *buf, size_t len);
6060
int struct_type;
6161
int offset;
62+
int size;
6263
int id;
6364
};
6465

@@ -345,11 +346,30 @@ static ssize_t main_blkaddr_show(struct f2fs_attr *a,
345346
(unsigned long long)MAIN_BLKADDR(sbi));
346347
}
347348

349+
static ssize_t __sbi_show_value(struct f2fs_attr *a,
350+
struct f2fs_sb_info *sbi, char *buf,
351+
unsigned char *value)
352+
{
353+
switch (a->size) {
354+
case 1:
355+
return sysfs_emit(buf, "%u\n", *(u8 *)value);
356+
case 2:
357+
return sysfs_emit(buf, "%u\n", *(u16 *)value);
358+
case 4:
359+
return sysfs_emit(buf, "%u\n", *(u32 *)value);
360+
case 8:
361+
return sysfs_emit(buf, "%llu\n", *(u64 *)value);
362+
default:
363+
f2fs_bug_on(sbi, 1);
364+
return sysfs_emit(buf,
365+
"show sysfs node value with wrong type\n");
366+
}
367+
}
368+
348369
static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
349370
struct f2fs_sb_info *sbi, char *buf)
350371
{
351372
unsigned char *ptr = NULL;
352-
unsigned int *ui;
353373

354374
ptr = __struct_ptr(sbi, a->struct_type);
355375
if (!ptr)
@@ -429,9 +449,30 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
429449
atomic_read(&sbi->cp_call_count[BACKGROUND]));
430450
#endif
431451

432-
ui = (unsigned int *)(ptr + a->offset);
452+
return __sbi_show_value(a, sbi, buf, ptr + a->offset);
453+
}
433454

434-
return sysfs_emit(buf, "%u\n", *ui);
455+
static void __sbi_store_value(struct f2fs_attr *a,
456+
struct f2fs_sb_info *sbi,
457+
unsigned char *ui, unsigned long value)
458+
{
459+
switch (a->size) {
460+
case 1:
461+
*(u8 *)ui = value;
462+
break;
463+
case 2:
464+
*(u16 *)ui = value;
465+
break;
466+
case 4:
467+
*(u32 *)ui = value;
468+
break;
469+
case 8:
470+
*(u64 *)ui = value;
471+
break;
472+
default:
473+
f2fs_bug_on(sbi, 1);
474+
f2fs_err(sbi, "store sysfs node value with wrong type");
475+
}
435476
}
436477

437478
static ssize_t __sbi_store(struct f2fs_attr *a,
@@ -913,7 +954,7 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
913954
return count;
914955
}
915956

916-
*ui = (unsigned int)t;
957+
__sbi_store_value(a, sbi, ptr + a->offset, t);
917958

918959
return count;
919960
}
@@ -1060,24 +1101,27 @@ static struct f2fs_attr f2fs_attr_sb_##_name = { \
10601101
.id = F2FS_FEATURE_##_feat, \
10611102
}
10621103

1063-
#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \
1104+
#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset, _size) \
10641105
static struct f2fs_attr f2fs_attr_##_name = { \
10651106
.attr = {.name = __stringify(_name), .mode = _mode }, \
10661107
.show = _show, \
10671108
.store = _store, \
10681109
.struct_type = _struct_type, \
1069-
.offset = _offset \
1110+
.offset = _offset, \
1111+
.size = _size \
10701112
}
10711113

10721114
#define F2FS_RO_ATTR(struct_type, struct_name, name, elname) \
10731115
F2FS_ATTR_OFFSET(struct_type, name, 0444, \
10741116
f2fs_sbi_show, NULL, \
1075-
offsetof(struct struct_name, elname))
1117+
offsetof(struct struct_name, elname), \
1118+
sizeof_field(struct struct_name, elname))
10761119

10771120
#define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \
10781121
F2FS_ATTR_OFFSET(struct_type, name, 0644, \
10791122
f2fs_sbi_show, f2fs_sbi_store, \
1080-
offsetof(struct struct_name, elname))
1123+
offsetof(struct struct_name, elname), \
1124+
sizeof_field(struct struct_name, elname))
10811125

10821126
#define F2FS_GENERAL_RO_ATTR(name) \
10831127
static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)

0 commit comments

Comments
 (0)