backing-file: inherit FMODE_NONOTIFY from the user file
Subsystems (3)
PATCH 1/1 backing-file: inherit FMODE_NONOTIFY from the user file
When fanotify reports a permission event, copy_event_to_user() creates the event fd with dentry_open_nonotify(), so that the listener's use of that fd does not generate further events. If the event's path is on a stacked filesystem, the stacked filesystem's ->open() opens the real file with backing_file_open(). Since commit ebe559609d78 ("fs: get rid of __FMODE_NONOTIFY kludge"), FMODE_NONOTIFY lives in f_mode only and the backing file does not inherit it: it is allocated with the default fsnotify mode, and fsnotify_open_perm_and_set_mode() raises FS_OPEN_PERM on the real path if the backing filesystem has permission event watchers.
Before that commit __FMODE_NONOTIFY travelled in f_flags, and overlayfs copies the user file's f_flags into the backing open (ovl_open_realfile()), so the backing file was exempt implicitly. Commit bc2473c90fca ("ovl: enable fsnotify events on underlying real files") relied on that when it dropped FMODE_NONOTIFY from OVL_OPEN_FLAGS.
The nested event is queued to the same group, from inside the listener's read(2), and can only be answered by another reader thread of that group. A listener holding filesystem marks on both the stacked and the backing filesystem therefore deadlocks as soon as all of its reader threads are inside such a read(2) at the same time, and every other task that touches a marked filesystem queues up behind it.
Observed with a commercial anti-malware agent (16 reader threads) on an ostree/composefs host: / is an overlayfs whose data layer is the ostree object store on the same btrfs that holds /etc and /home, and a systemd-sysext overlay sits on /usr. Every on-demand scan of /usr wedged the whole machine until the group was killed. Stack of a blocked reader thread on 7.2.7:
fanotify_get_response
fanotify_handle_event
send_to_group
fsnotify
__fsnotify_parent
do_dentry_open
vfs_open
backing_file_open
ovl_open_realfile [overlay]
ovl_open [overlay]
do_dentry_open
vfs_open
backing_file_open
ovl_open_realfile [overlay]
ovl_open [overlay]
do_dentry_open
vfs_open
dentry_open_nonotify
copy_event_to_user
fanotify_read
vfs_read
ksys_read
Userspace cannot avoid this: the backing open happens while the kernel builds the fd, before the event is reported, and fid reporting is not available for permission event classes.
Make a backing file inherit FMODE_NONOTIFY when the user file it is opened for must not generate events. This restores the pre-6.14 behaviour for the fds that fanotify creates (and for any other FMODE_NONOTIFY file), while ordinary opens through a stacked filesystem keep generating events on the real path, as intended by bc2473c90fca.
Verified with a self-contained reproducer (private tmpfs lower layers, an overlayfs over them, FAN_OPEN_PERM filesystem marks on the overlay and on one lower layer, a child opening a file through the overlay) in a qemu VM on this tree built with defconfig plus OVERLAY_FS, FANOTIFY and FANOTIFY_ACCESS_PERMISSIONS: without this patch the listener's read(2) never returns and the child's open is released only by the listener's death; with it read(2) returns, the event is answered and the open completes.
backing_tmpfile_open() is left alone: its only caller is ovl_create_tmpfile(), whose user file is a regular overlayfs file.
---
Reproducer: nested-perm-repro.c (inline below). It unshares a mount namespace, mounts two private tmpfs and an overlayfs over them, puts FAN_OPEN_PERM filesystem marks on the overlay and on one tmpfs, and lets a child open a file through the overlay. On an unpatched 6.14+ kernel the listener's read(2) never returns (the kernel raises a second permission event for the backing file while creating the event fd); with this patch it returns and the child's open completes. Verified in a qemu VM on a defconfig+overlayfs+fanotify build of this tree, before and after the patch (x86_64 defconfig plus OVERLAY_FS, FANOTIFY and FANOTIFY_ACCESS_PERMISSIONS). Also observed in production: Bitdefender BEST with filesystem marks on the btrfs that holds the ostree object store and on a systemd-sysext overlay over /usr, Fedora kernel 7.2.7, on a bootc/composefs host; every scan of /usr wedged the machine until the group was killed. Full SysRq-w dumps available on request.
Tool disclosure (Documentation/process/generated-content.rst): the stack analysis, the git-history search for the regressing commit, the patch text, the commit message and the reproducer were produced with an AI coding assistant (Anthropic Claude Code) under my direction over interactive sessions; I reviewed, built and tested the result. No static analysis tools beyond checkpatch were used.
nested-perm-repro.c:
/*
* nested-perm-repro.c — fanotify permission event raised by the kernel's own
* event-fd creation on a stacked filesystem (overlayfs), deadlocking a listener
* with a single reader thread. Self-contained: builds a private tmpfs + overlay
* stack inside a new mount namespace, touches nothing else on the system. *
* gcc -O2 -Wall -o nested-perm-repro nested-perm-repro.c
* sudo ./nested-perm-repro
*
* What it does:
* 1. unshare(CLONE_NEWNS); mount tmpfs A and tmpfs B; create A/file;
* mount overlay M with lowerdir=A:B (read-only overlay, no upper needed).
* 2. fanotify group (FAN_CLASS_CONTENT) with FAN_OPEN_PERM *filesystem* marks
* on M (the stacked fs) and on A (the backing fs) — the shape of an * anti-malware agent watching both the OS tree and the disk it lives on. * Filesystem marks, not mount marks: overlayfs opens its layers through * private clones of the lower mounts, which a mount mark never matches. * Each tmpfs is its own superblock, so the marks touch nothing else. * 3. fork: the child opens M/file. That raises an OPEN_PERM event on M. * 4. The parent read()s the event. The kernel builds the event fd with * dentry_open_nonotify() -> ovl_open() -> backing_file_open() on A/file. * On kernels >= 6.14 without the fix, that inner open raises a SECOND * OPEN_PERM event on A, queued to the same group, from inside read(2). * With one reader thread nobody can read it: read() never returns. * 5. The parent dies on alarm(TIMEOUT) (SIGALRM, default action; the sleep is * TASK_KILLABLE so the fatal signal gets through). Closing the group * answers everything FAN_ALLOW; the child's open completes and reports * how long it was blocked: >= TIMEOUT means REPRODUCED.
*
* Expected output on an affected kernel:
* parent: read() on the fanotify fd ...
* child : open took 3.0 s (unblocked only by the listener's death) -> BUG REPRODUCED
* On a fixed kernel the read returns at once, the parent answers FAN_ALLOW,
* and the child reports a few milliseconds -> not reproduced. */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <time.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/fanotify.h>
#define TIMEOUT 3
#define BASE "/tmp/nested-perm-repro"
static void die(const char *what)
{
fprintf(stderr, "%s: %s\n", what, strerror(errno));
exit(1);
}static double now(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + ts.tv_nsec / 1e9;
}int main(void)
{
char a[128], b[128], m[128], file[160], opts[512];
int fan, fd;
pid_t child;
snprintf(a, sizeof a, "%s/A", BASE);
snprintf(b, sizeof b, "%s/B", BASE);
snprintf(m, sizeof m, "%s/M", BASE);
snprintf(file, sizeof file, "%s/file", m);
/* 1. private mount namespace + the stack */
if (unshare(CLONE_NEWNS) < 0) die("unshare(CLONE_NEWNS)");
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) die("make-rprivate /");
mkdir(BASE, 0755);
mkdir(a, 0755); mkdir(b, 0755); mkdir(m, 0755);
if (mount("tmpfs", a, "tmpfs", 0, "size=1m") < 0) die("mount tmpfs A");
if (mount("tmpfs", b, "tmpfs", 0, "size=1m") < 0) die("mount tmpfs B");
{
char p[160];
snprintf(p, sizeof p, "%s/file", a);
fd = open(p, O_WRONLY | O_CREAT, 0644);
if (fd < 0) die("create A/file");
(void)!write(fd, "hello\n", 6);
close(fd);
}
snprintf(opts, sizeof opts, "lowerdir=%s:%s", a, b);
if (mount("overlay", m, "overlay", 0, opts) < 0) die("mount overlay M");
printf("stack : %s = overlay(lowerdir=A:B), A and B are private tmpfs\n", m);
/* 2. the listener: one group, permission marks on the stacked AND the backing mount */
fan = fanotify_init(FAN_CLASS_CONTENT | FAN_CLOEXEC, O_RDONLY | O_CLOEXEC);
if (fan < 0) die("fanotify_init");
if (fanotify_mark(fan, FAN_MARK_ADD | FAN_MARK_FILESYSTEM, FAN_OPEN_PERM, AT_FDCWD, m) < 0)
die("fanotify_mark M");
if (fanotify_mark(fan, FAN_MARK_ADD | FAN_MARK_FILESYSTEM, FAN_OPEN_PERM, AT_FDCWD, a) < 0)
die("fanotify_mark A");
printf("marks : FAN_OPEN_PERM filesystem marks on M (stacked) and A (backing)\n");
/* 3. the victim */
child = fork();
if (child < 0) die("fork");
if (child == 0) {
double t0, dt;
int rc;
close(fan);
usleep(200 * 1000);
t0 = now();
rc = open(file, O_RDONLY);
dt = now() - t0;
if (rc >= 0) close(rc);
if (dt >= TIMEOUT - 0.5)
printf("child : open took %.1f s (unblocked only by the listener's death) "
"-> BUG REPRODUCED: the listener's read() never returned\n", dt);
else
printf("child : open took %.3f s (rc=%d) -> not reproduced\n", dt, rc);
_exit(0);
}
/* 4. one read(). On an affected kernel this never returns. */
printf("parent: read() on the fanotify fd (dies on alarm after %d s if it hangs) ...\n",
TIMEOUT);
fflush(stdout);
alarm(TIMEOUT);
{
char buf[4096];
ssize_t n = read(fan, buf, sizeof buf);
struct fanotify_event_metadata *md = (struct fanotify_event_metadata *)buf;
struct fanotify_response resp;
alarm(0);
if (n <= 0) die("read");
printf("parent: got event mask=%#llx fd=%d pid=%d — read() returned, answering ALLOW\n",
(unsigned long long)md->mask, md->fd, md->pid);
resp.fd = md->fd;
resp.response = FAN_ALLOW;
(void)!write(fan, &resp, sizeof resp);
if (md->fd >= 0) close(md->fd);
}
close(fan);
waitpid(child, NULL, 0);
return 0;
}fs/backing-file.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
fs/backing-file.c +11 -0
@@ -43,6 +43,17 @@ struct file *backing_file_open(const struct file *user_file, int flags,if (IS_ERR (f))return f; +/* +* A user file that must not generate fsnotify events (e.g. the fd +* that fanotify creates for a permission event with +* dentry_open_nonotify()) must not generate them through its backing +* file either. Otherwise a listener watching the backing filesystem +* gets a nested permission event from its own read(2), which only +* another reader of the same group can answer. +*/ +if (FMODE_FSNOTIFY_NONE (user_file->f_mode)) +file_set_fsnotify_mode (f,FMODE_NONOTIFY ); +path_get (user_path);backing_file_set_user_path (f, user_path); error =vfs_open (real_path, f);
Signature · 1 line
2.56.0
Amir Goldstein
maintainerQuoted 46 lines · Edvard Conrad
When fanotify reports a permission event,
copy_event_to_user()creates the event fd withdentry_open_nonotify(), so that the listener's use of that fd does not generate further events. If the event's path is on a stacked filesystem, the stacked filesystem's ->open() opens the real file withbacking_file_open(). Since commitebe559609d78("fs: get rid of__FMODE_NONOTIFYkludge"),FMODE_NONOTIFYlives inf_modeonly and the backing file does not inherit it: it is allocated with the default fsnotify mode, andfsnotify_open_perm_and_set_mode()raisesFS_OPEN_PERMon the real path if the backing filesystem has permission event watchers.Before that commit
__FMODE_NONOTIFYtravelled inf_flags, and overlayfs copies the user file'sf_flagsinto the backing open (ovl_open_realfile()), so the backing file was exempt implicitly. Commitbc2473c90fca("ovl: enable fsnotify events on underlying real files") relied on that when it droppedFMODE_NONOTIFYfromOVL_OPEN_FLAGS.The nested event is queued to the same group, from inside the listener's read(2), and can only be answered by another reader thread of that group. A listener holding filesystem marks on both the stacked and the backing filesystem therefore deadlocks as soon as all of its reader threads are inside such a read(2) at the same time, and every other task that touches a marked filesystem queues up behind it.
Observed with a commercial anti-malware agent (16 reader threads) on an ostree/composefs host: / is an overlayfs whose data layer is the ostree object store on the same btrfs that holds /etc and /home, and a systemd-sysext overlay sits on /usr. Every on-demand scan of /usr wedged the whole machine until the group was killed. Stack of a blocked reader thread on 7.2.7:
fanotify_get_response
fanotify_handle_event
send_to_group
fsnotify
__fsnotify_parent
do_dentry_open
vfs_open
backing_file_open
ovl_open_realfile[overlay]
ovl_open[overlay]
do_dentry_open
vfs_open
backing_file_open
ovl_open_realfile[overlay]
Quoted 8 lines
ovl_open[overlay]
do_dentry_open
vfs_open
dentry_open_nonotify
copy_event_to_user
fanotify_read
vfs_read
ksys_read
Thanks for the report!
Quoted 195 lines
Userspace cannot avoid this: the backing open happens while the kernel builds the fd, before the event is reported, and fid reporting is not available for permission event classes.
Make a backing file inherit
FMODE_NONOTIFYwhen the user file it is opened for must not generate events. This restores the pre-6.14 behaviour for the fds that fanotify creates (and for any otherFMODE_NONOTIFYfile), while ordinary opens through a stacked filesystem keep generating events on the real path, as intended bybc2473c90fca.Verified with a self-contained reproducer (private tmpfs lower layers, an overlayfs over them,
FAN_OPEN_PERMfilesystem marks on the overlay and on one lower layer, a child opening a file through the overlay) in a qemu VM on this tree built with defconfig plusOVERLAY_FS, FANOTIFY andFANOTIFY_ACCESS_PERMISSIONS: without this patch the listener's read(2) never returns and the child's open is released only by the listener's death; with it read(2) returns, the event is answered and the open completes.
backing_tmpfile_open()is left alone: its only caller isovl_create_tmpfile(), whose user file is a regular overlayfs file.Fixes ebe559609d78 ("fs: get rid of __FMODE_NONOTIFY kludge")Assisted-by LLMSigned-off-by Edvard Conrad <[email protected]>---
Reproducer:nested-perm-repro.c(inline below). It unshares a mount namespace, mounts two private tmpfs and an overlayfs over them, putsFAN_OPEN_PERMfilesystem marks on the overlay and on one tmpfs, and lets a child open a file through the overlay. On an unpatched 6.14+ kernel the listener's read(2) never returns (the kernel raises a second permission event for the backing file while creating the event fd); with this patch it returns and the child's open completes. Verified in a qemu VM on a defconfig+overlayfs+fanotify build of this tree, before and after the patch (x86_64defconfig plusOVERLAY_FS, FANOTIFY andFANOTIFY_ACCESS_PERMISSIONS). Also observed in production: Bitdefender BEST with filesystem marks on the btrfs that holds the ostree object store and on a systemd-sysext overlay over /usr, Fedora kernel 7.2.7, on a bootc/composefs host; every scan of /usr wedged the machine until the group was killed. Full SysRq-w dumps available on request.Tool disclosure (
Documentation/process/generated-content.rst): the stack analysis, the git-history search for the regressing commit, the patch text, the commit message and the reproducer were produced with an AI coding assistant (Anthropic Claude Code) under my direction over interactive sessions; I reviewed, built and tested the result. No static analysis tools beyond checkpatch were used.
nested-perm-repro.c:/*
*nested-perm-repro.c— fanotify permission event raised by the kernel's own
* event-fd creation on a stacked filesystem (overlayfs), deadlocking a listener
* with a single reader thread. Self-contained: builds a private tmpfs + overlay
* stack inside a new mount namespace, touches nothing else on the system. *
* gcc -O2 -Wall -o nested-perm-repronested-perm-repro.c
* sudo ./nested-perm-repro
*
* What it does:
* 1.unshare(CLONE_NEWNS); mount tmpfs A and tmpfs B; create A/file;
* mount overlay M with lowerdir=A:B (read-only overlay, no upper needed).
* 2. fanotify group (FAN_CLASS_CONTENT) withFAN_OPEN_PERM*filesystem* marks* on M (the stacked fs) and on A (the backing fs) — the shape of an * anti-malware agent watching both the OS tree and the disk it lives on. * Filesystem marks, not mount marks: overlayfs opens its layers through * private clones of the lower mounts, which a mount mark never matches. * Each tmpfs is its own superblock, so the marks touch nothing else. * 3. fork: the child opens M/file. That raises an OPEN_PERM event on M. * 4. The parent read()s the event. The kernel builds the event fd with * dentry_open_nonotify() -> ovl_open() -> backing_file_open() on A/file. * On kernels >= 6.14 without the fix, that inner open raises a SECOND * OPEN_PERM event on A, queued to the same group, from inside read(2). * With one reader thread nobody can read it: read() never returns. * 5. The parent dies on alarm(TIMEOUT) (SIGALRM, default action; the sleep is * TASK_KILLABLE so the fatal signal gets through). Closing the group * answers everything FAN_ALLOW; the child's open completes and reports * how long it was blocked: >= TIMEOUT means REPRODUCED.*
* Expected output on an affected kernel:
* parent:read()on the fanotify fd ...
* child : open took 3.0 s (unblocked only by the listener's death) -> BUG REPRODUCED
* On a fixed kernel the read returns at once, the parent answersFAN_ALLOW,
* and the child reports a few milliseconds -> not reproduced. */
#define_GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <time.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/fanotify.h>#define TIMEOUT 3
#define BASE "/tmp/nested-perm-repro"static void
die(const char *what){ fprintf(stderr, "%s: %s\n", what, strerror(errno)); exit(1); }static double now(void)
{ struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec / 1e9; }int main(void)
{ char a[128], b[128], m[128], file[160], opts[512]; int fan, fd; pid_t child; snprintf(a, sizeof a, "%s/A", BASE); snprintf(b, sizeof b, "%s/B", BASE); snprintf(m, sizeof m, "%s/M", BASE); snprintf(file, sizeof file, "%s/file", m); /* 1. private mount namespace + the stack */ if (unshare(CLONE_NEWNS) < 0) die("unshare(CLONE_NEWNS)"); if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) die("make-rprivate /"); mkdir(BASE, 0755); mkdir(a, 0755); mkdir(b, 0755); mkdir(m, 0755); if (mount("tmpfs", a, "tmpfs", 0, "size=1m") < 0) die("mount tmpfs A"); if (mount("tmpfs", b, "tmpfs", 0, "size=1m") < 0) die("mount tmpfs B"); { char p[160]; snprintf(p, sizeof p, "%s/file", a); fd = open(p, O_WRONLY | O_CREAT, 0644); if (fd < 0) die("create A/file"); (void)!write(fd, "hello\n", 6); close(fd); } snprintf(opts, sizeof opts, "lowerdir=%s:%s", a, b); if (mount("overlay", m, "overlay", 0, opts) < 0) die("mount overlay M"); printf("stack : %s = overlay(lowerdir=A:B), A and B are private tmpfs\n", m); /* 2. the listener: one group, permission marks on the stacked AND the backing mount */ fan = fanotify_init(FAN_CLASS_CONTENT | FAN_CLOEXEC, O_RDONLY | O_CLOEXEC); if (fan < 0) die("fanotify_init"); if (fanotify_mark(fan, FAN_MARK_ADD | FAN_MARK_FILESYSTEM, FAN_OPEN_PERM, AT_FDCWD, m) < 0) die("fanotify_mark M"); if (fanotify_mark(fan, FAN_MARK_ADD | FAN_MARK_FILESYSTEM, FAN_OPEN_PERM, AT_FDCWD, a) < 0) die("fanotify_mark A"); printf("marks : FAN_OPEN_PERM filesystem marks on M (stacked) and A (backing)\n"); /* 3. the victim */ child = fork(); if (child < 0) die("fork"); if (child == 0) { double t0, dt; int rc; close(fan); usleep(200 * 1000); t0 = now(); rc = open(file, O_RDONLY); dt = now() - t0; if (rc >= 0) close(rc); if (dt >= TIMEOUT - 0.5) printf("child : open took %.1f s (unblocked only by the listener's death) " "-> BUG REPRODUCED: the listener's read() never returned\n", dt); else printf("child : open took %.3f s (rc=%d) -> not reproduced\n", dt, rc); _exit(0); } /* 4. one read(). On an affected kernel this never returns. */ printf("parent: read() on the fanotify fd (dies on alarm after %d s if it hangs) ...\n", TIMEOUT); fflush(stdout); alarm(TIMEOUT); { char buf[4096]; ssize_t n = read(fan, buf, sizeof buf); struct fanotify_event_metadata *md = (struct fanotify_event_metadata *)buf; struct fanotify_response resp; alarm(0); if (n <= 0) die("read"); printf("parent: got event mask=%#llx fd=%d pid=%d — read() returned, answering ALLOW\n", (unsigned long long)md->mask, md->fd, md->pid); resp.fd = md->fd;
Quoted 9 lines
resp.response = FAN_ALLOW;
(void)!write(fan, &resp, sizeof resp);
if (md->fd >= 0) close(md->fd);
}
close(fan);
waitpid(child, NULL, 0);
return 0;
}Any chance you can post this as an LTP test?
Quoted 26 lines
fs/backing-file.c | 11 +++++++++++ 1 file changed, 11 insertions(+)fs/backing-file.c +11
-1@@ -43,6 +43,17 @@ struct file *backing_file_open(const struct file *user_file, int flags,if (IS_ERR (f))return f; +/* +* A user file that must not generate fsnotify events (e.g. the fd +* that fanotify creates for a permission event with +* dentry_open_nonotify()) must not generate them through its backing +* file either. Otherwise a listener watching the backing filesystem +* gets a nested permission event from its own read(2), which only +* another reader of the same group can answer. +*/ +if (FMODE_FSNOTIFY_NONE (user_file->f_mode)) +file_set_fsnotify_mode (f,FMODE_NONOTIFY ); +path_get (user_path);backing_file_set_user_path (f, user_path); error =vfs_open (real_path, f); --
Quoted 2 lines
2.56.0
Urgh I hate how special case this is.
How about something like below (untested)
Thanks,
Amir.
fs/file_table.c +2 -1
@@ -176,7 +176,8 @@ static int __init init_fs_stat_sysctls(void)fs_initcall (init_fs_stat_sysctls);#endif -static int init_file (struct file *f,int flags,const struct cred *cred) +static int init_file (struct file *f,int flags,const struct cred *cred, +fmode_t fmode) {int error;
@@ -200,7 +201,7 @@ static int init_file(struct file *f, int flags,
const struct cred *cred)
memset(&f->f_ra, 0, sizeof(f->f_ra));
f->f_flags = flags;
- f->f_mode = OPEN_FMODE(flags);
+ f->f_mode = OPEN_FMODE(flags) | fmode; /*
* Disable permission and pre-content events for all files by default.
* They may be enabled later by fsnotify_open_perm_and_set_mode().
@@ -263,7 +264,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
if (unlikely(!f))
return ERR_PTR(-ENOMEM);- error = init_file(f, flags, cred);
+ error = init_file(f, flags, cred, 0);
if (unlikely(error)) {
kmem_cache_free(filp_cache, f);
return ERR_PTR(error);
@@ -297,14 +298,12 @@ struct file *alloc_empty_file_noaccount(int
flags, const struct cred *cred)
if (unlikely(!f))
return ERR_PTR(-ENOMEM);- error = init_file(f, flags, cred);
+ error = init_file(f, flags, cred, FMODE_NOACCOUNT);
if (unlikely(error)) {
kmem_cache_free(filp_cache, f);
return ERR_PTR(error);
}- f->f_mode |= FMODE_NOACCOUNT;
-
return f;
}
@@ -316,6 +315,16 @@ static int init_backing_file(struct backing_file *ff,
return security_backing_file_alloc(&ff->file, user_file);
}
+/*
+ * A user file that must not generate fsnotify events (e.g. the fd
+ * that fanotify creates for a permission event with
+ * dentry_open_nonotify()) must not generate them through its backing
+ * file either. Otherwise a listener watching the backing filesystem
+ * gets a nested permission event from its own read(2), which only
+ * another reader of the same group can answer.
+ */
+#define BACKING_FILE_FMODE_MASK (FMODE_NONOTIFY)
+
/*
* Variant of alloc_empty_file() that allocates a backing_file container
* and doesn't check and modify nr_files.
@@ -328,19 +337,19 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
{
struct backing_file *ff;
int error;+ fmode_t fmode = user_file->f_mode & BACKING_FILE_FMODE_MASK;
ff = kmem_cache_alloc(bfilp_cache, GFP_KERNEL);
if (unlikely(!ff))
return ERR_PTR(-ENOMEM);- error = init_file(&ff->file, flags, cred);
+ error = init_file(&ff->file, flags, cred, fmode |
+ FMODE_BACKING | FMODE_NOACCOUNT);
if (unlikely(error)) {
kmem_cache_free(bfilp_cache, ff);
return ERR_PTR(error);
}- /* The f_mode flags must be set before fput(). */
- ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
error = init_backing_file(ff, user_file);
if (unlikely(error)) {
fput(&ff->file);