scsi: target: fix recursive locking in __configfs_open_file()
Subsystems (3)
PATCH v4 1/1 scsi: target: fix recursive locking in __configfs_open_file()
In flush_write_buffer, &p->frag_sem is acquired and then the loaded store function is called, which, here, is target_core_item_dbroot_store(). This function called filp_open(), following which these functions were called (in reverse order), according to the call trace:
down_read__configfs_open_filedo_dentry_openvfs_opendo_openpath_openatdo_filp_openfile_open_namefilp_opentarget_core_item_dbroot_storeflush_write_bufferconfigfs_write_iter
target_core_item_dbroot_store() tries to validate the new file path by trying to open the file path provided to it; however, in this case, the bug report shows:
db_root: not a directory: /sys/kernel/config/target/dbroot
indicating that the same configfs file was tried to be opened, on which it is currently working on. Thus, it is trying to acquire frag_sem semaphore of the same file of which it already holds the semaphore obtained in flush_write_buffer(), leading to acquiring the semaphore in a nested manner and a possibility of recursive locking.
Fix this by modifying target_core_item_dbroot_store() to use kern_path() instead of filp_open() to avoid opening the file using filesystem-specific function __configfs_open_file(), and further modifying it to make this fix compatible.
---
Changes since v3:
- Add LOOKUP_DIRECTORY flag in call to kern_path() so as to check presence of directory checks more efficiently
v3 link: https://lore.kernel.org/all/[email protected]/T/#m175d152067817dd6e9dc1821b6fbf626e47a4007
Note:
I checked out and found that when I try to test on commit 3a8660878839faadb4f1a6dd72c3179c1df56787 (latest commit on which bug dashboard reports the bug on, in upstream repository) syzbot uses, in its kernel config:
CONFIG_CC_VERSION_TEXT="gcc (Debian 12.2.0-14+deb12u1) 12.2.0"
Ref: https://syzkaller.appspot.com/x/.config?x=e854293d7f44b5a5
Syzbot Reply: https://lore.kernel.org/all/[email protected]/T/#m62bc76de5549460ae98e843bb120712548489794
While when #syz test (i.e. on HEAD commit of upstream) is used, it uses, in its kernel config:
CONFIG_CC_VERSION_TEXT="gcc (Debian 14.2.0-19) 14.2.0"
Ref: https://syzkaller.appspot.com/x/.config?x=99ac58566e9eb044
Syzbot reply: https://lore.kernel.org/all/[email protected]/T/#me8b79610e4c18a8d8a7d8d6bc249d1c7cf2f8819
However in both cases it uses:
gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
Probably due to mismatch in compiler version which syzbot actually uses and whats present in kernel config, the build fails for the first case. However, the patch succeeds in fixing the bug in second case.
Earlier for v1 patch (sine v2 patch involved minor change to commit message and v3 involved adding a missed out Reviewed-by tag) patch the kernel builds as well as testing succeeded since syzbot used this in its kernel config:
CONFIG_CC_VERSION_TEXT="gcc (Debian 12.2.0-14+deb12u1) 12.2.0"
as well as used the compiler:
gcc (Debian 12.2.0-14+deb12u1) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
Changes since v2:
- Add Reviewed-by tag received from Dmitry Bogdanov, which was accidentally left to be added in v2 patch.
v2 link: https://lore.kernel.org/linux-scsi/[email protected]/T/#u
Reference for Reviewed-by Tag: https://lore.kernel.org/all/[email protected]/T/#mb22d0fc06e747e2b2df8320a15afd2a0670fd0e7
Changes since v1:
- Update commit message to reflect the fact that same file, which code was currently operating on, was tried to be opened again, leading to acquiring the same semaphore in nested manner & possibility of recursive locking.
v1 link: https://lore.kernel.org/all/[email protected]/T/
drivers/target/target_core_configfs.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)
drivers/target/target_core_configfs.c +6 -9
@@ -108,8 +108,8 @@ static ssize_t target_core_item_dbroot_store(struct config_item *item, const char *page, size_t count) { ssize_t read_bytes; -struct file *fp;ssize_t r = -EINVAL ; +struct path path = {};mutex_lock (&target_devices_lock);if (target_devices) {
@@ -131,17 +131,14 @@ static ssize_t target_core_item_dbroot_store(struct config_item *item, db_root_stage[read_bytes -1 ] ='\0' ;/* validate new db root before accepting it */ - fp =filp_open (db_root_stage,O_RDONLY ,0 ); -if (IS_ERR (fp)) { + r =kern_path (db_root_stage,LOOKUP_FOLLOW |LOOKUP_DIRECTORY , &path); +if (r) {pr_err ("db_root: cannot open: %s\n" , db_root_stage); +if (r == -ENOTDIR ) +pr_err ("db_root: not a directory: %s\n" , db_root_stage); goto unlock; } -if (!S_ISDIR (file_inode (fp)->i_mode)) { -filp_close (fp,NULL ); -pr_err ("db_root: not a directory: %s\n" , db_root_stage); - goto unlock; - } -filp_close (fp,NULL ); +path_put (&path);strscpy (db_root, db_root_stage);pr_debug ("Target_Core_ConfigFS: db_root set to %s\n" , db_root);
base-commit: 3a8660878839faadb4f1a6dd72c3179c1df56787
Signature · 1 line
2.34.1
Quoted 142 lines · Prithvi Tambewagh
In
flush_write_buffer,&p->frag_semis acquired and then the loaded store function is called, which, here, istarget_core_item_dbroot_store(). This function calledfilp_open(), following which these functions were called (in reverse order), according to the call trace:
down_read__configfs_open_filedo_dentry_openvfs_opendo_openpath_openatdo_filp_openfile_open_namefilp_opentarget_core_item_dbroot_storeflush_write_bufferconfigfs_write_iter
target_core_item_dbroot_store()tries to validate the new file path by trying to open the file path provided to it; however, in this case, the bug report shows:
db_root: not a directory:/sys/kernel/config/target/dbrootindicating that the same configfs file was tried to be opened, on which it is currently working on. Thus, it is trying to acquire
frag_semsemaphore of the same file of which it already holds the semaphore obtained inflush_write_buffer(), leading to acquiring the semaphore in a nested manner and a possibility of recursive locking.Fix this by modifying
target_core_item_dbroot_store()to usekern_path()instead offilp_open()to avoid opening the file using filesystem-specific function__configfs_open_file(), and further modifying it to make this fix compatible.Reported-by [email protected]Tested-by [email protected]Signed-off-by Prithvi Tambewagh <[email protected]>Reviewed-by Dmitry Bogdanov <[email protected]>---
Changes since v3:
- AddLOOKUP_DIRECTORYflag in call tokern_path()so as to check presence of directory checks more efficientlyv3 link: https://lore.kernel.org/all/[email protected]/T/#m175d152067817dd6e9dc1821b6fbf626e47a4007
Note:
I checked out and found that when I try to test on commit3a8660878839faadb4f1a6dd72c3179c1df56787(latest commit on which bug dashboard reports the bug on, in upstream repository) syzbot uses, in its kernel config:
CONFIG_CC_VERSION_TEXT="gcc(Debian 12.2.0-14+deb12u1) 12.2.0"Ref: https://syzkaller.appspot.com/x/.config?x=e854293d7f44b5a5
Syzbot Reply: https://lore.kernel.org/all/[email protected]/T/#m62bc76de5549460ae98e843bb120712548489794While when #syz test (i.e. on HEAD commit of upstream) is used, it uses, in its kernel config:
CONFIG_CC_VERSION_TEXT="gcc(Debian 14.2.0-19) 14.2.0"Ref: https://syzkaller.appspot.com/x/.config?x=99ac58566e9eb044
Syzbot reply: https://lore.kernel.org/all/[email protected]/T/#me8b79610e4c18a8d8a7d8d6bc249d1c7cf2f8819However in both cases it uses:
gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
Probably due to mismatch in compiler version which syzbot actually uses and whats present in kernel config, the build fails for the first case. However, the patch succeeds in fixing the bug in second case.
Earlier for v1 patch (sine v2 patch involved minor change to commit message and v3 involved adding a missed out Reviewed-by tag) patch the kernel builds as well as testing succeeded since syzbot used this in its kernel config:
CONFIG_CC_VERSION_TEXT="gcc(Debian 12.2.0-14+deb12u1) 12.2.0"as well as used the compiler:
gcc (Debian 12.2.0-14+deb12u1) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
Changes since v2:
- Add Reviewed-by tag received from Dmitry Bogdanov, which was accidentally left to be added in v2 patch.v2 link: https://lore.kernel.org/linux-scsi/[email protected]/T/#u
Reference for Reviewed-by Tag: https://lore.kernel.org/all/[email protected]/T/#mb22d0fc06e747e2b2df8320a15afd2a0670fd0e7Changes since v1:
- Update commit message to reflect the fact that same file, which code was currently operating on, was tried to be opened again, leading to acquiring the same semaphore in nested manner & possibility of recursive locking.v1 link: https://lore.kernel.org/all/[email protected]/T/
drivers/target/target_core_configfs.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)drivers/target/target_core_configfs.c +6
-9@@ -108,8 +108,8 @@ static ssize_t target_core_item_dbroot_store(struct config_item *item, const char *page, size_t count) { ssize_t read_bytes; -struct file *fp;ssize_t r = -EINVAL ; +struct path path = {};mutex_lock (&target_devices_lock);if (target_devices) {@@ -131,17 +131,14 @@ static ssize_t target_core_item_dbroot_store(struct config_item *item, db_root_stage[read_bytes -1 ] ='\0' ;/* validate new db root before accepting it */ - fp =filp_open (db_root_stage,O_RDONLY ,0 ); -if (IS_ERR (fp)) { + r =kern_path (db_root_stage,LOOKUP_FOLLOW |LOOKUP_DIRECTORY , &path); +if (r) {pr_err ("db_root: cannot open: %s\n" , db_root_stage); +if (r == -ENOTDIR ) +pr_err ("db_root: not a directory: %s\n" , db_root_stage); goto unlock; } -if (!S_ISDIR (file_inode (fp)->i_mode)) { -filp_close (fp,NULL ); -pr_err ("db_root: not a directory: %s\n" , db_root_stage); - goto unlock; - } -filp_close (fp,NULL ); +path_put (&path);strscpy (db_root, db_root_stage);pr_debug ("Target_Core_ConfigFS: db_root set to %s\n" , db_root);
Quoted 4 lines
base-commit:
3a8660878839faadb4f1a6dd72c3179c1df56787Signature · 1 line
2.34.1
Hello everyone,
Just a gentle ping on this v4 patch; it incorporates Al Viro's suggestion from v3 to use LOOKUP_DIRECTORY flag in kern_path(). Kindly let me know if anything else is needed from my side or any feedback is to be addressed.
Thanks,
Prithvi
Martin K. Petersen
AppliedQuoted 18 lines · Prithvi Tambewagh
In
flush_write_buffer,&p->frag_semis acquired and then the loaded store function is called, which, here, istarget_core_item_dbroot_store(). This function calledfilp_open(), following which these functions were called (in reverse order), according to the call trace:
down_read__configfs_open_filedo_dentry_openvfs_opendo_openpath_openatdo_filp_openfile_open_namefilp_opentarget_core_item_dbroot_storeflush_write_bufferconfigfs_write_iter
Quoted 1 line
[...]
Applied to 7.0/scsi-fixes, thanks!
[1/1] scsi: target: fix recursive locking in __configfs_open_file()
https://git.kernel.org/mkp/scsi/c/14d4ac19d189
Signature · 1 line
Martin K. Petersen