-
Notifications
You must be signed in to change notification settings - Fork 103
fs: Enhance read-only filesystem error notification mechanism #1268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: linux-6.6.y
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4073,19 +4073,14 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, | |||||||||||||||||||||||
| goto retry; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| out1: | ||||||||||||||||||||||||
| #ifdef CONFIG_DEEPIN_ERR_NOTIFY | ||||||||||||||||||||||||
| if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { | ||||||||||||||||||||||||
| struct path file_path; | ||||||||||||||||||||||||
| int get_path_err; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| get_path_err = | ||||||||||||||||||||||||
| deepin_get_path_for_err_notify(dfd, name, &file_path); | ||||||||||||||||||||||||
| if (!get_path_err) { | ||||||||||||||||||||||||
| deepin_check_and_notify_ro_fs_err(&file_path, "mknod"); | ||||||||||||||||||||||||
| path_put(&file_path); | ||||||||||||||||||||||||
| if (deepin_should_notify_ro_fs_err(error)) { | ||||||||||||||||||||||||
| struct deepin_path_last path_last; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) { | ||||||||||||||||||||||||
| deepin_check_and_notify_ro_fs_err(&path_last, "mknod"); | ||||||||||||||||||||||||
| deepin_put_path_last(&path_last); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| #endif /* CONFIG_DEEPIN_ERR_NOTIFY */ | ||||||||||||||||||||||||
| putname(name); | ||||||||||||||||||||||||
| return error; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
@@ -4169,19 +4164,14 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode) | |||||||||||||||||||||||
| goto retry; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| out_putname: | ||||||||||||||||||||||||
| #ifdef CONFIG_DEEPIN_ERR_NOTIFY | ||||||||||||||||||||||||
| if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { | ||||||||||||||||||||||||
| struct path file_path; | ||||||||||||||||||||||||
| int get_path_err; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| get_path_err = | ||||||||||||||||||||||||
| deepin_get_path_for_err_notify(dfd, name, &file_path); | ||||||||||||||||||||||||
| if (!get_path_err) { | ||||||||||||||||||||||||
| deepin_check_and_notify_ro_fs_err(&file_path, "mkdir"); | ||||||||||||||||||||||||
| path_put(&file_path); | ||||||||||||||||||||||||
| if (deepin_should_notify_ro_fs_err(error)) { | ||||||||||||||||||||||||
| struct deepin_path_last path_last; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) { | ||||||||||||||||||||||||
| deepin_check_and_notify_ro_fs_err(&path_last, "mkdir"); | ||||||||||||||||||||||||
| deepin_put_path_last(&path_last); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| #endif /* CONFIG_DEEPIN_ERR_NOTIFY */ | ||||||||||||||||||||||||
| putname(name); | ||||||||||||||||||||||||
| return error; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
@@ -4299,21 +4289,18 @@ int do_rmdir(int dfd, struct filename *name) | |||||||||||||||||||||||
| inode_unlock(path.dentry->d_inode); | ||||||||||||||||||||||||
| mnt_drop_write(path.mnt); | ||||||||||||||||||||||||
| exit2: | ||||||||||||||||||||||||
| #ifdef CONFIG_DEEPIN_ERR_NOTIFY | ||||||||||||||||||||||||
| if (unlikely((error == -EROFS) && deepin_err_notify_enabled())) { | ||||||||||||||||||||||||
| dentry = lookup_one_qstr_excl(&last, path.dentry, 0); | ||||||||||||||||||||||||
| if (!IS_ERR(dentry)) { | ||||||||||||||||||||||||
| if (d_is_positive(dentry)) { | ||||||||||||||||||||||||
| // dentry is positive, so we can get the path | ||||||||||||||||||||||||
| struct path file_path = { .mnt = path.mnt, | ||||||||||||||||||||||||
| .dentry = dentry }; | ||||||||||||||||||||||||
| deepin_check_and_notify_ro_fs_err(&file_path, | ||||||||||||||||||||||||
| if (deepin_should_notify_ro_fs_err(error)) { | ||||||||||||||||||||||||
| struct deepin_path_last path_last; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (!deepin_lookup_path_or_parent(dfd, name, lookup_flags, &path_last)) { | ||||||||||||||||||||||||
| if (!path_last.last) { | ||||||||||||||||||||||||
| // File exists, notify error | ||||||||||||||||||||||||
| deepin_check_and_notify_ro_fs_err(&path_last, | ||||||||||||||||||||||||
| "rmdir"); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| dput(dentry); | ||||||||||||||||||||||||
| deepin_put_path_last(&path_last); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| deepin_put_path_last(&path_last); | |
| deepin_put_path_last(&path_last); | |
| } else { | |
| deepin_put_path_last(&path_last); |
Copilot
AI
Nov 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memory leak when path lookup fails in the middle. When deepin_lookup_path_or_parent succeeds but the subsequent check at line 4453 (if (!path_last.last)) is false, the function returns without calling deepin_put_path_last(&path_last). This means the path reference acquired in deepin_lookup_path_or_parent is never released when path_last.last is not NULL. The cleanup call should be moved outside the conditional block or a call should be added to the else branch.
Copilot
AI
Nov 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function deepin_notify_rename_ro_fs_err is called at line 5111 to handle rename errors, but it directly uses old_last->name and new_last->name pointers from qstr structures (see lines 671-672 in deepin_err_notify.c). These pointers are stack-allocated or reference dentry names that may not remain valid. Unlike other call sites that use deepin_lookup_path_or_parent which duplicates strings with kstrdup() (line 5381 in namei.c), this rename handler doesn't duplicate the strings, potentially causing use-after-free issues.
| if (deepin_should_notify_ro_fs_err(error)) | |
| deepin_notify_rename_ro_fs_err(&old_last, &new_last, &old_path, &new_path); | |
| if (deepin_should_notify_ro_fs_err(error)) { | |
| struct qstr old_last_dup = old_last; | |
| struct qstr new_last_dup = new_last; | |
| old_last_dup.name = kstrdup(old_last.name, GFP_KERNEL); | |
| new_last_dup.name = kstrdup(new_last.name, GFP_KERNEL); | |
| deepin_notify_rename_ro_fs_err(&old_last_dup, &new_last_dup, &old_path, &new_path); | |
| kfree(old_last_dup.name); | |
| kfree(new_last_dup.name); | |
| } |
Copilot
AI
Nov 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] When handling the -ENOENT case, if filename_parentat() returns an error at line 5374, the function returns early at line 5375 without calling path_put(&result_path). However, since filename_lookup() returned -ENOENT at line 5372, the result_path was never successfully initialized and doesn't hold a reference. This is correct behavior, but the code flow could be clearer. Consider adding a comment to clarify that result_path is not initialized when filename_lookup returns -ENOENT.
| if (error) | |
| if (error) | |
| /* No need to call path_put(&result_path) here: | |
| * result_path was never initialized when filename_lookup() | |
| * returns -ENOENT. | |
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk):
deepin_lookup_path_or_parenttreatsqstr.nameas a C-string, butqstrnames are not guaranteed to be NUL-terminated.In
deepin_lookup_path_or_parent()you do:and later
combine_path_and_last()callsstrlen(last)on that buffer. Sincestruct qstronly guaranteesname+len, this can read past the allocated memory and lead to corruption.Use:
kstrndup(last.name, last.len, GFP_KERNEL)when duplicating, andlast.len(or an equivalent length) instead of callingstrlen(last).Apply the same pattern anywhere
last.nameis handled as a C string.