Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

debugfs: fix error when writing negative value to atomic_t debugfs file

The simple attribute files do not accept a negative value since the commit
488dac0c9237 ("libfs: fix error cast of negative value in
simple_attr_write()"), so we have to use a 64-bit value to write a
negative value for a debugfs file created by debugfs_create_atomic_t().

This restores the previous behaviour by introducing
DEFINE_DEBUGFS_ATTRIBUTE_SIGNED for a signed value.

Link: https://lkml.kernel.org/r/20220919172418.45257-4-akinobu.mita@gmail.com
Fixes: 488dac0c9237 ("libfs: fix error cast of negative value in simple_attr_write()")
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Reported-by: Zhao Gongyi <zhaogongyi@huawei.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Rafael J. Wysocki <rafael@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Wei Yongjun <weiyongjun1@huawei.com>
Cc: Yicong Yang <yangyicong@hisilicon.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Akinobu Mita and committed by
Andrew Morton
d472cf79 f883c3ed

+43 -14
+4 -6
Documentation/fault-injection/fault-injection.rst
··· 83 83 - /sys/kernel/debug/fail*/times: 84 84 85 85 specifies how many times failures may happen at most. A value of -1 86 - means "no limit". Note, though, that this file only accepts unsigned 87 - values. So, if you want to specify -1, you better use 'printf' instead 88 - of 'echo', e.g.: $ printf %#x -1 > times 86 + means "no limit". 89 87 90 88 - /sys/kernel/debug/fail*/space: 91 89 ··· 282 284 echo Y > /sys/kernel/debug/$FAILTYPE/task-filter 283 285 echo 10 > /sys/kernel/debug/$FAILTYPE/probability 284 286 echo 100 > /sys/kernel/debug/$FAILTYPE/interval 285 - printf %#x -1 > /sys/kernel/debug/$FAILTYPE/times 287 + echo -1 > /sys/kernel/debug/$FAILTYPE/times 286 288 echo 0 > /sys/kernel/debug/$FAILTYPE/space 287 289 echo 2 > /sys/kernel/debug/$FAILTYPE/verbose 288 290 echo Y > /sys/kernel/debug/$FAILTYPE/ignore-gfp-wait ··· 336 338 echo N > /sys/kernel/debug/$FAILTYPE/task-filter 337 339 echo 10 > /sys/kernel/debug/$FAILTYPE/probability 338 340 echo 100 > /sys/kernel/debug/$FAILTYPE/interval 339 - printf %#x -1 > /sys/kernel/debug/$FAILTYPE/times 341 + echo -1 > /sys/kernel/debug/$FAILTYPE/times 340 342 echo 0 > /sys/kernel/debug/$FAILTYPE/space 341 343 echo 2 > /sys/kernel/debug/$FAILTYPE/verbose 342 344 echo Y > /sys/kernel/debug/$FAILTYPE/ignore-gfp-wait ··· 367 369 echo N > /sys/kernel/debug/$FAILTYPE/task-filter 368 370 echo 100 > /sys/kernel/debug/$FAILTYPE/probability 369 371 echo 0 > /sys/kernel/debug/$FAILTYPE/interval 370 - printf %#x -1 > /sys/kernel/debug/$FAILTYPE/times 372 + echo -1 > /sys/kernel/debug/$FAILTYPE/times 371 373 echo 0 > /sys/kernel/debug/$FAILTYPE/space 372 374 echo 1 > /sys/kernel/debug/$FAILTYPE/verbose 373 375
+22 -6
fs/debugfs/file.c
··· 378 378 } 379 379 EXPORT_SYMBOL_GPL(debugfs_attr_read); 380 380 381 - ssize_t debugfs_attr_write(struct file *file, const char __user *buf, 382 - size_t len, loff_t *ppos) 381 + static ssize_t debugfs_attr_write_xsigned(struct file *file, const char __user *buf, 382 + size_t len, loff_t *ppos, bool is_signed) 383 383 { 384 384 struct dentry *dentry = F_DENTRY(file); 385 385 ssize_t ret; ··· 387 387 ret = debugfs_file_get(dentry); 388 388 if (unlikely(ret)) 389 389 return ret; 390 - ret = simple_attr_write(file, buf, len, ppos); 390 + if (is_signed) 391 + ret = simple_attr_write_signed(file, buf, len, ppos); 392 + else 393 + ret = simple_attr_write(file, buf, len, ppos); 391 394 debugfs_file_put(dentry); 392 395 return ret; 393 396 } 397 + 398 + ssize_t debugfs_attr_write(struct file *file, const char __user *buf, 399 + size_t len, loff_t *ppos) 400 + { 401 + return debugfs_attr_write_xsigned(file, buf, len, ppos, false); 402 + } 394 403 EXPORT_SYMBOL_GPL(debugfs_attr_write); 404 + 405 + ssize_t debugfs_attr_write_signed(struct file *file, const char __user *buf, 406 + size_t len, loff_t *ppos) 407 + { 408 + return debugfs_attr_write_xsigned(file, buf, len, ppos, true); 409 + } 410 + EXPORT_SYMBOL_GPL(debugfs_attr_write_signed); 395 411 396 412 static struct dentry *debugfs_create_mode_unsafe(const char *name, umode_t mode, 397 413 struct dentry *parent, void *value, ··· 754 738 *val = atomic_read((atomic_t *)data); 755 739 return 0; 756 740 } 757 - DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get, 741 + DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic_t, debugfs_atomic_t_get, 758 742 debugfs_atomic_t_set, "%lld\n"); 759 - DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_ro, debugfs_atomic_t_get, NULL, 743 + DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic_t_ro, debugfs_atomic_t_get, NULL, 760 744 "%lld\n"); 761 - DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, 745 + DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, 762 746 "%lld\n"); 763 747 764 748 /**
+17 -2
include/linux/debugfs.h
··· 45 45 46 46 extern struct dentry *arch_debugfs_dir; 47 47 48 - #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ 48 + #define DEFINE_DEBUGFS_ATTRIBUTE_XSIGNED(__fops, __get, __set, __fmt, __is_signed) \ 49 49 static int __fops ## _open(struct inode *inode, struct file *file) \ 50 50 { \ 51 51 __simple_attr_check_format(__fmt, 0ull); \ ··· 56 56 .open = __fops ## _open, \ 57 57 .release = simple_attr_release, \ 58 58 .read = debugfs_attr_read, \ 59 - .write = debugfs_attr_write, \ 59 + .write = (__is_signed) ? debugfs_attr_write_signed : debugfs_attr_write, \ 60 60 .llseek = no_llseek, \ 61 61 } 62 + 63 + #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ 64 + DEFINE_DEBUGFS_ATTRIBUTE_XSIGNED(__fops, __get, __set, __fmt, false) 65 + 66 + #define DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(__fops, __get, __set, __fmt) \ 67 + DEFINE_DEBUGFS_ATTRIBUTE_XSIGNED(__fops, __get, __set, __fmt, true) 62 68 63 69 typedef struct vfsmount *(*debugfs_automount_t)(struct dentry *, void *); 64 70 ··· 107 101 ssize_t debugfs_attr_read(struct file *file, char __user *buf, 108 102 size_t len, loff_t *ppos); 109 103 ssize_t debugfs_attr_write(struct file *file, const char __user *buf, 104 + size_t len, loff_t *ppos); 105 + ssize_t debugfs_attr_write_signed(struct file *file, const char __user *buf, 110 106 size_t len, loff_t *ppos); 111 107 112 108 struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, ··· 256 248 } 257 249 258 250 static inline ssize_t debugfs_attr_write(struct file *file, 251 + const char __user *buf, 252 + size_t len, loff_t *ppos) 253 + { 254 + return -ENODEV; 255 + } 256 + 257 + static inline ssize_t debugfs_attr_write_signed(struct file *file, 259 258 const char __user *buf, 260 259 size_t len, loff_t *ppos) 261 260 {