Skip to content

Commit 4ef30b9

Browse files
Yongpeng Yanggregkh
authored andcommitted
f2fs: fix out-of-bounds access in sysfs attribute read/write
commit 98ea003 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent b72d775 commit 4ef30b9

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
@@ -58,6 +58,7 @@ struct f2fs_attr {
5858
const char *buf, size_t len);
5959
int struct_type;
6060
int offset;
61+
int size;
6162
int id;
6263
};
6364

@@ -325,11 +326,30 @@ static ssize_t main_blkaddr_show(struct f2fs_attr *a,
325326
(unsigned long long)MAIN_BLKADDR(sbi));
326327
}
327328

329+
static ssize_t __sbi_show_value(struct f2fs_attr *a,
330+
struct f2fs_sb_info *sbi, char *buf,
331+
unsigned char *value)
332+
{
333+
switch (a->size) {
334+
case 1:
335+
return sysfs_emit(buf, "%u\n", *(u8 *)value);
336+
case 2:
337+
return sysfs_emit(buf, "%u\n", *(u16 *)value);
338+
case 4:
339+
return sysfs_emit(buf, "%u\n", *(u32 *)value);
340+
case 8:
341+
return sysfs_emit(buf, "%llu\n", *(u64 *)value);
342+
default:
343+
f2fs_bug_on(sbi, 1);
344+
return sysfs_emit(buf,
345+
"show sysfs node value with wrong type\n");
346+
}
347+
}
348+
328349
static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
329350
struct f2fs_sb_info *sbi, char *buf)
330351
{
331352
unsigned char *ptr = NULL;
332-
unsigned int *ui;
333353

334354
ptr = __struct_ptr(sbi, a->struct_type);
335355
if (!ptr)
@@ -409,9 +429,30 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
409429
atomic_read(&sbi->cp_call_count[BACKGROUND]));
410430
#endif
411431

412-
ui = (unsigned int *)(ptr + a->offset);
432+
return __sbi_show_value(a, sbi, buf, ptr + a->offset);
433+
}
413434

414-
return sysfs_emit(buf, "%u\n", *ui);
435+
static void __sbi_store_value(struct f2fs_attr *a,
436+
struct f2fs_sb_info *sbi,
437+
unsigned char *ui, unsigned long value)
438+
{
439+
switch (a->size) {
440+
case 1:
441+
*(u8 *)ui = value;
442+
break;
443+
case 2:
444+
*(u16 *)ui = value;
445+
break;
446+
case 4:
447+
*(u32 *)ui = value;
448+
break;
449+
case 8:
450+
*(u64 *)ui = value;
451+
break;
452+
default:
453+
f2fs_bug_on(sbi, 1);
454+
f2fs_err(sbi, "store sysfs node value with wrong type");
455+
}
415456
}
416457

417458
static ssize_t __sbi_store(struct f2fs_attr *a,
@@ -868,7 +909,7 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
868909
return count;
869910
}
870911

871-
*ui = (unsigned int)t;
912+
__sbi_store_value(a, sbi, ptr + a->offset, t);
872913

873914
return count;
874915
}
@@ -1015,24 +1056,27 @@ static struct f2fs_attr f2fs_attr_sb_##_name = { \
10151056
.id = F2FS_FEATURE_##_feat, \
10161057
}
10171058

1018-
#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \
1059+
#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset, _size) \
10191060
static struct f2fs_attr f2fs_attr_##_name = { \
10201061
.attr = {.name = __stringify(_name), .mode = _mode }, \
10211062
.show = _show, \
10221063
.store = _store, \
10231064
.struct_type = _struct_type, \
1024-
.offset = _offset \
1065+
.offset = _offset, \
1066+
.size = _size \
10251067
}
10261068

10271069
#define F2FS_RO_ATTR(struct_type, struct_name, name, elname) \
10281070
F2FS_ATTR_OFFSET(struct_type, name, 0444, \
10291071
f2fs_sbi_show, NULL, \
1030-
offsetof(struct struct_name, elname))
1072+
offsetof(struct struct_name, elname), \
1073+
sizeof_field(struct struct_name, elname))
10311074

10321075
#define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \
10331076
F2FS_ATTR_OFFSET(struct_type, name, 0644, \
10341077
f2fs_sbi_show, f2fs_sbi_store, \
1035-
offsetof(struct struct_name, elname))
1078+
offsetof(struct struct_name, elname), \
1079+
sizeof_field(struct struct_name, elname))
10361080

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

0 commit comments

Comments
 (0)