From: James Bottomley <James.Bottomley@HansenPartnership.com>
To: Christian Brauner <brauner@kernel.org>
Cc: "Ard Biesheuvel" <ardb@kernel.org>,
"Al Viro" <viro@zeniv.linux.org.uk>,
"Ryan Lee" <ryan.lee@canonical.com>,
"Malte Schröder" <malte.schroeder@tnxip.de>,
linux-security-module@vger.kernel.org,
apparmor <apparmor@lists.ubuntu.com>,
linux-efi@vger.kernel.org,
"John Johansen" <john.johansen@canonical.com>,
"jk@ozlabs.org" <jk@ozlabs.org>,
linux-fsdevel@vger.kernel.org
Subject: Re: [RFC 1/1] fix NULL mnt [was Re: apparmor NULL pointer dereference on resume [efivarfs]]
Date: Sat, 15 Mar 2025 14:41:43 -0400 [thread overview]
Message-ID: <bad92b18f389256d26a886b2b0706d04c8c6c336.camel@HansenPartnership.com> (raw)
In-Reply-To: <20250315-allemal-fahrbahn-9afc7bc0008d@brauner>
On Sat, 2025-03-15 at 11:04 +0100, Christian Brauner wrote:
[...]
> Since efivars uses a single global superblock and we know that sfi-
> >sb is still alive (After all we've just pinned it above.)
> vfs_kern_mount() will reuse the same superblock.
>
> There's two cases to consider:
>
> (1) vfs_kern_mount() was successful. In this case path->mnt will hold
> an active superblock reference that will be released asynchronously
> via __fput(). That is safe and correct.
>
> (2) vfs_kern_mount() fails. That's an issue because you need to call
> deactivate_super() which will have a similar deadlock problem.
> If efivarfs_pm_notify() now holds the last reference to the
> superblock then deactivate_super() super will put that last
> reference and call efivarfs_kill_super() which in turn will wait for
> efivarfs_pm_notify() to finish. => deadlock
>
> So in the error case you need to offload the call to
> deactivate_super() to a workqueue.
OK, got that (although it did make my head explode a bit ... this is
certainly subtle stuff). To do the delayed work for the
deactivate_super(), I hijacked the superblock destroy_work structure
which I think is safe because by the time the work structure is
executed, we own it and so it can be reused for the actual destroy_work
in deactivate_super().
However, there's another problem: the mntput after kernel_file_open may
synchronously call cleanup_mnt() (and thus deactivate_super()) if the
open fails because it's marked MNT_INTERNAL, which is caused by
SB_KERNMOUNT. I fixed this just by not passing the SB_KERNMOUNT flag,
which feels a bit hacky.
I've put together everything at the bottom, however, I can't test the
error legs of this because trying to trigger and unmount and hybernate
at exactly the right point is pretty much impossible. The rest seems
to work as advertised, although I would like a tested-by from the
apparmour people because I do run apparmour in my debian test rig but
don't see the problem.
Regards,
James
---
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 6eae8cf655c1..2d826e98066b 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -474,12 +474,25 @@ static int efivarfs_check_missing(efi_char16_t *name16, efi_guid_t vendor,
return err;
}
+static void efivarfs_deactivate_super_work(struct work_struct *work)
+{
+ struct super_block *s = container_of(work, struct super_block,
+ destroy_work);
+ /*
+ * note: here s->destroy_work is free for reuse (which
+ * will happen in deactivate_super)
+ */
+ deactivate_super(s);
+}
+
+static struct file_system_type efivarfs_type;
+
static int efivarfs_pm_notify(struct notifier_block *nb, unsigned long action,
void *ptr)
{
struct efivarfs_fs_info *sfi = container_of(nb, struct efivarfs_fs_info,
pm_nb);
- struct path path = { .mnt = NULL, .dentry = sfi->sb->s_root, };
+ struct path path;
struct efivarfs_ctx ectx = {
.ctx = {
.actor = efivarfs_actor,
@@ -487,6 +500,7 @@ static int efivarfs_pm_notify(struct notifier_block *nb, unsigned long action,
.sb = sfi->sb,
};
struct file *file;
+ struct super_block *s = sfi->sb;
static bool rescan_done = true;
if (action == PM_HIBERNATION_PREPARE) {
@@ -499,11 +513,39 @@ static int efivarfs_pm_notify(struct notifier_block *nb, unsigned long action,
if (rescan_done)
return NOTIFY_DONE;
+ /* ensure single superblock is alive and pin it */
+ if (!atomic_inc_not_zero(&s->s_active))
+ return NOTIFY_DONE;
+
pr_info("efivarfs: resyncing variable state\n");
- /* O_NOATIME is required to prevent oops on NULL mnt */
+ path.dentry = sfi->sb->s_root;
+
+ /* do not add SB_KERNMOUNT which causes MNT_INTERNAL, see below */
+ path.mnt = vfs_kern_mount(&efivarfs_type, 0,
+ efivarfs_type.name, NULL);
+ if (IS_ERR(path.mnt)) {
+ pr_err("efivarfs: internal mount failed\n");
+ /*
+ * We may be the last pinner of the superblock but
+ * calling efivarfs_kill_sb from within the notifier
+ * here would deadlock trying to unregister it
+ */
+ INIT_WORK(&s->destroy_work, efivarfs_deactivate_super_work);
+ schedule_work(&s->destroy_work);
+ return PTR_ERR(path.mnt);
+ }
+
+ /* path.mnt now has pin on superblock, so this must be above one */
+ atomic_dec(&s->s_active);
+
file = kernel_file_open(&path, O_RDONLY | O_DIRECTORY | O_NOATIME,
current_cred());
+ /*
+ * safe even if last put because no MNT_INTERNAL means this
+ * will do delayed deactivate_super and not deadlock
+ */
+ mntput(path.mnt);
if (IS_ERR(file))
return NOTIFY_DONE;
next prev parent reply other threads:[~2025-03-15 18:41 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-05 19:04 apparmor NULL pointer dereference on resume Malte Schröder
2025-03-05 19:22 ` Ryan Lee
2025-03-05 21:47 ` Malte Schröder
2025-03-10 19:57 ` Ryan Lee
2025-03-10 21:49 ` apparmor NULL pointer dereference on resume [efivarfs] James Bottomley
2025-03-11 7:16 ` Ard Biesheuvel
2025-03-11 8:45 ` Christian Brauner
2025-03-11 13:01 ` James Bottomley
2025-03-11 15:55 ` Christian Brauner
2025-03-11 16:19 ` Christian Brauner
2025-03-11 16:20 ` James Bottomley
2025-03-11 17:15 ` Al Viro
2025-03-11 17:46 ` James Bottomley
2025-03-14 14:59 ` [RFC 1/1] fix NULL mnt [was Re: apparmor NULL pointer dereference on resume [efivarfs]] James Bottomley
2025-03-15 10:04 ` Christian Brauner
2025-03-15 18:41 ` James Bottomley [this message]
2025-03-16 6:46 ` Christian Brauner
2025-03-16 14:26 ` James Bottomley
2025-03-16 19:19 ` Ard Biesheuvel
2025-03-17 8:56 ` Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=bad92b18f389256d26a886b2b0706d04c8c6c336.camel@HansenPartnership.com \
--to=james.bottomley@hansenpartnership.com \
--cc=apparmor@lists.ubuntu.com \
--cc=ardb@kernel.org \
--cc=brauner@kernel.org \
--cc=jk@ozlabs.org \
--cc=john.johansen@canonical.com \
--cc=linux-efi@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=malte.schroeder@tnxip.de \
--cc=ryan.lee@canonical.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox