From 6a320935fa4293e9e599ec9f85dc9eb3be7029f8 Mon Sep 17 00:00:00 2001 From: Chia-Ming Chang Date: Tue, 24 Feb 2026 17:34:42 +0800 Subject: [PATCH 1/5] inotify: fix watch count leak when fsnotify_add_inode_mark_locked() fails When fsnotify_add_inode_mark_locked() fails in inotify_new_watch(), the error path calls inotify_remove_from_idr() but does not call dec_inotify_watches() to undo the preceding inc_inotify_watches(). This leaks a watch count, and repeated failures can exhaust the max_user_watches limit with -ENOSPC even when no watches are active. Prior to commit 1cce1eea0aff ("inotify: Convert to using per-namespace limits"), the watch count was incremented after fsnotify_add_mark_locked() succeeded, so this path was not affected. The conversion moved inc_inotify_watches() before the mark insertion without adding the corresponding rollback. Add the missing dec_inotify_watches() call in the error path. Fixes: 1cce1eea0aff ("inotify: Convert to using per-namespace limits") Cc: stable@vger.kernel.org Signed-off-by: Chia-Ming Chang Signed-off-by: robbieko Reviewed-by: Nikolay Borisov Link: https://patch.msgid.link/20260224093442.3076294-1-chiamingc@synology.com Signed-off-by: Jan Kara --- fs/notify/inotify/inotify_user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 5e1845f2c25d..2edac3b39178 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -621,6 +621,7 @@ static int inotify_new_watch(struct fsnotify_group *group, if (ret) { /* we failed to get on the inode, get off the idr */ inotify_remove_from_idr(group, tmp_i_mark); + dec_inotify_watches(group->inotify_data.ucounts); goto out_err; } From 0d5ee3373426395478c355f3e93ba4b1118a04e9 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Mon, 16 Feb 2026 16:06:24 +0100 Subject: [PATCH 2/5] fanotify: avoid/silence premature LSM capability checks Make sure calling capable()/ns_capable() actually leads to access denied when false is returned, because these functions emit an audit record when a Linux Security Module denies the capability, which makes it difficult to avoid allowing/silencing unnecessary permissions in security policies (namely with SELinux). Where the return value just used to set a flag, use the non-auditing ns_capable_noaudit() instead. Fixes: 7cea2a3c505e ("fanotify: support limited functionality for unprivileged users") Signed-off-by: Ondrej Mosnacek Reviewed-by: Paul Moore Reviewed-by: Amir Goldstein Link: https://patch.msgid.link/20260216150625.793013-2-omosnace@redhat.com Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify_user.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index c2dcb25151de..5d030fbb2dff 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1615,17 +1615,18 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) pr_debug("%s: flags=%x event_f_flags=%x\n", __func__, flags, event_f_flags); - if (!capable(CAP_SYS_ADMIN)) { - /* - * An unprivileged user can setup an fanotify group with - * limited functionality - an unprivileged group is limited to - * notification events with file handles or mount ids and it - * cannot use unlimited queue/marks. - */ - if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) || - !(flags & (FANOTIFY_FID_BITS | FAN_REPORT_MNT))) - return -EPERM; + /* + * An unprivileged user can setup an fanotify group with limited + * functionality - an unprivileged group is limited to notification + * events with file handles or mount ids and it cannot use unlimited + * queue/marks. + */ + if (((flags & FANOTIFY_ADMIN_INIT_FLAGS) || + !(flags & (FANOTIFY_FID_BITS | FAN_REPORT_MNT))) && + !capable(CAP_SYS_ADMIN)) + return -EPERM; + if (!ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN)) { /* * Setting the internal flag FANOTIFY_UNPRIV on the group * prevents setting mount/filesystem marks on this group and @@ -1990,8 +1991,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, * A user is allowed to setup sb/mount/mntns marks only if it is * capable in the user ns where the group was created. */ - if (!ns_capable(group->user_ns, CAP_SYS_ADMIN) && - mark_type != FAN_MARK_INODE) + if (mark_type != FAN_MARK_INODE && + !ns_capable(group->user_ns, CAP_SYS_ADMIN)) return -EPERM; /* From 66052a768d4726a31e939b5ac902f2b0b452c8d5 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Mon, 16 Feb 2026 16:06:25 +0100 Subject: [PATCH 3/5] fanotify: call fanotify_events_supported() before path_permission() and security_path_notify() The latter trigger LSM (e.g. SELinux) checks, which will log a denial when permission is denied, so it's better to do them after validity checks to avoid logging a denial when the operation would fail anyway. Fixes: 0b3b094ac9a7 ("fanotify: Disallow permission events for proc filesystem") Signed-off-by: Ondrej Mosnacek Reviewed-by: Amir Goldstein Reviewed-by: Paul Moore Link: https://patch.msgid.link/20260216150625.793013-3-omosnace@redhat.com Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify_user.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 5d030fbb2dff..ae904451dfc0 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1210,6 +1210,7 @@ static int fanotify_find_path(int dfd, const char __user *filename, *path = fd_file(f)->f_path; path_get(path); + ret = 0; } else { unsigned int lookup_flags = 0; @@ -1219,22 +1220,7 @@ static int fanotify_find_path(int dfd, const char __user *filename, lookup_flags |= LOOKUP_DIRECTORY; ret = user_path_at(dfd, filename, lookup_flags, path); - if (ret) - goto out; } - - /* you can only watch an inode if you have read permissions on it */ - ret = path_permission(path, MAY_READ); - if (ret) { - path_put(path); - goto out; - } - - ret = security_path_notify(path, mask, obj_type); - if (ret) - path_put(path); - -out: return ret; } @@ -2058,6 +2044,15 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, goto path_put_and_out; } + /* you can only watch an inode if you have read permissions on it */ + ret = path_permission(&path, MAY_READ); + if (ret) + goto path_put_and_out; + + ret = security_path_notify(&path, mask, obj_type); + if (ret) + goto path_put_and_out; + if (fid_mode) { ret = fanotify_test_fsid(path.dentry, flags, &__fsid); if (ret) From 4520b96b8136ba2465a3f4dc5c3fb8bdf3d92e4e Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Sat, 14 Feb 2026 13:12:17 +0800 Subject: [PATCH 4/5] fsnotify: inotify: pass mark connector to fsnotify_recalc_mask() fsnotify_recalc_mask() expects a plain struct fsnotify_mark_connector *, but inode->i_fsnotify_marks is an __rcu pointer. Use fsn_mark->connector instead to avoid sparse "different address spaces" warnings. Signed-off-by: Sun Jian Link: https://patch.msgid.link/20260214051217.1381363-1-sun.jian.kdev@gmail.com Signed-off-by: Jan Kara --- fs/notify/inotify/inotify_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 2edac3b39178..ed37491c1618 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -573,7 +573,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, /* update the inode with this new fsn_mark */ if (dropped || do_inode) - fsnotify_recalc_mask(inode->i_fsnotify_marks); + fsnotify_recalc_mask(fsn_mark->connector); } From 0fdbe845534f4e0b7243b7a44562c4486ce25fb4 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 21 Mar 2026 22:05:47 +0100 Subject: [PATCH 5/5] fanotify: replace deprecated strcpy in fanotify_info_copy_{name,name2} strcpy() has been deprecated [1] because it performs no bounds checking on the destination buffer, which can lead to buffer overflows. Replace it with the safer strscpy(). Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy [1] Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260321210544.519259-4-thorsten.blum@linux.dev Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 39e60218df7c..a0619e7694d5 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -218,7 +219,7 @@ static inline void fanotify_info_copy_name(struct fanotify_info *info, return; info->name_len = name->len; - strcpy(fanotify_info_name(info), name->name); + strscpy(fanotify_info_name(info), name->name, name->len + 1); } static inline void fanotify_info_copy_name2(struct fanotify_info *info, @@ -228,7 +229,7 @@ static inline void fanotify_info_copy_name2(struct fanotify_info *info, return; info->name2_len = name->len; - strcpy(fanotify_info_name2(info), name->name); + strscpy(fanotify_info_name2(info), name->name, name->len + 1); } /*