From: "Mickaël Salaün" <mic@digikod.net>
To: Tingmao Wang <m@maowtm.org>
Cc: "Song Liu" <song@kernel.org>, "Al Viro" <viro@zeniv.linux.org.uk>,
"Günther Noack" <gnoack@google.com>, "Jan Kara" <jack@suse.cz>,
"Alexei Starovoitov" <alexei.starovoitov@gmail.com>,
"Christian Brauner" <brauner@kernel.org>,
linux-security-module@vger.kernel.org,
linux-fsdevel@vger.kernel.org
Subject: Re: [RFC PATCH 1/3] landlock: walk parent dir without taking references
Date: Wed, 4 Jun 2025 19:15:26 +0200 [thread overview]
Message-ID: <20250604.ciecheo7EeNg@digikod.net> (raw)
In-Reply-To: <8cf726883f6dae564559e4aacdb2c09bf532fcc5.1748997840.git.m@maowtm.org>
On Wed, Jun 04, 2025 at 01:45:43AM +0100, Tingmao Wang wrote:
> This commit replaces dget_parent with a direct read of d_parent. By
> holding rcu read lock we should be safe in terms of not reading freed
> memory, but this is still problematic due to move+unlink, as will be shown
> with the test in the next commit.
>
> Note that follow_up is still used when walking up a mountpoint.
>
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
> security/landlock/fs.c | 40 ++++++++++++++++++++++------------------
> 1 file changed, 22 insertions(+), 18 deletions(-)
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 6fee7c20f64d..923737412cfa 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -361,7 +361,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
> * Returns NULL if no rule is found or if @dentry is negative.
> */
> static const struct landlock_rule *
> -find_rule(const struct landlock_ruleset *const domain,
> +find_rule_rcu(const struct landlock_ruleset *const domain,
> const struct dentry *const dentry)
> {
> const struct landlock_rule *rule;
> @@ -375,10 +375,10 @@ find_rule(const struct landlock_ruleset *const domain,
> return NULL;
>
> inode = d_backing_inode(dentry);
> - rcu_read_lock();
> + if (unlikely(!inode))
> + return NULL;
> id.key.object = rcu_dereference(landlock_inode(inode)->object);
> rule = landlock_find_rule(domain, id);
> - rcu_read_unlock();
> return rule;
> }
>
> @@ -809,9 +809,11 @@ static bool is_access_to_paths_allowed(
> is_dom_check = false;
> }
>
> + rcu_read_lock();
> +
> if (unlikely(dentry_child1)) {
> landlock_unmask_layers(
> - find_rule(domain, dentry_child1),
> + find_rule_rcu(domain, dentry_child1),
> landlock_init_layer_masks(
> domain, LANDLOCK_MASK_ACCESS_FS,
> &_layer_masks_child1, LANDLOCK_KEY_INODE),
> @@ -821,7 +823,7 @@ static bool is_access_to_paths_allowed(
> }
> if (unlikely(dentry_child2)) {
> landlock_unmask_layers(
> - find_rule(domain, dentry_child2),
> + find_rule_rcu(domain, dentry_child2),
> landlock_init_layer_masks(
> domain, LANDLOCK_MASK_ACCESS_FS,
> &_layer_masks_child2, LANDLOCK_KEY_INODE),
> @@ -831,7 +833,6 @@ static bool is_access_to_paths_allowed(
> }
>
> walker_path = *path;
> - path_get(&walker_path);
> /*
> * We need to walk through all the hierarchy to not miss any relevant
> * restriction.
> @@ -880,7 +881,7 @@ static bool is_access_to_paths_allowed(
> break;
> }
>
> - rule = find_rule(domain, walker_path.dentry);
> + rule = find_rule_rcu(domain, walker_path.dentry);
> allowed_parent1 = allowed_parent1 ||
> landlock_unmask_layers(
> rule, access_masked_parent1,
> @@ -897,10 +898,14 @@ static bool is_access_to_paths_allowed(
> break;
> jump_up:
> if (walker_path.dentry == walker_path.mnt->mnt_root) {
> + /* follow_up gets the parent and puts the passed in path */
> + path_get(&walker_path);
> if (follow_up(&walker_path)) {
> + path_put(&walker_path);
path_put() cannot be safely called in a RCU read-side critical section
because it can free memory which can sleep, and also because it can wait
for a lock. However, we can call rcu_read_unlock() before and
rcu_read_lock() after (if we hold a reference).
> /* Ignores hidden mount points. */
> goto jump_up;
> } else {
> + path_put(&walker_path);
> /*
> * Stops at the real root. Denies access
> * because not all layers have granted access.
> @@ -920,11 +925,11 @@ static bool is_access_to_paths_allowed(
> }
> break;
> }
> - parent_dentry = dget_parent(walker_path.dentry);
> - dput(walker_path.dentry);
> + parent_dentry = walker_path.dentry->d_parent;
> walker_path.dentry = parent_dentry;
> }
> - path_put(&walker_path);
> +
> + rcu_read_unlock();
>
> if (!allowed_parent1) {
> log_request_parent1->type = LANDLOCK_REQUEST_FS_ACCESS;
> @@ -1045,12 +1050,11 @@ static bool collect_domain_accesses(
> layer_masks_dom,
> LANDLOCK_KEY_INODE);
>
> - dget(dir);
> - while (true) {
> - struct dentry *parent_dentry;
> + rcu_read_lock();
>
> + while (true) {
> /* Gets all layers allowing all domain accesses. */
> - if (landlock_unmask_layers(find_rule(domain, dir), access_dom,
> + if (landlock_unmask_layers(find_rule_rcu(domain, dir), access_dom,
> layer_masks_dom,
> ARRAY_SIZE(*layer_masks_dom))) {
> /*
> @@ -1065,11 +1069,11 @@ static bool collect_domain_accesses(
> if (dir == mnt_root || WARN_ON_ONCE(IS_ROOT(dir)))
> break;
>
> - parent_dentry = dget_parent(dir);
> - dput(dir);
> - dir = parent_dentry;
> + dir = dir->d_parent;
> }
> - dput(dir);
> +
> + rcu_read_unlock();
> +
> return ret;
> }
>
> --
> 2.49.0
>
>
next prev parent reply other threads:[~2025-06-04 17:15 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-04 0:45 [RFC PATCH 0/3] landlock: walk parent dir with RCU, without taking references Tingmao Wang
2025-06-04 0:45 ` [RFC PATCH 1/3] landlock: walk parent dir " Tingmao Wang
2025-06-04 17:15 ` Mickaël Salaün [this message]
2025-06-04 21:05 ` Tingmao Wang
2025-06-06 10:25 ` Christian Brauner
2025-06-04 0:45 ` [RFC PATCH 2/3] selftests/landlock: Add fs_race_test Tingmao Wang
2025-06-04 0:45 ` [RFC PATCH 3/3] Restart pathwalk on rename seqcount change Tingmao Wang
2025-06-04 0:55 ` Al Viro
2025-06-04 1:12 ` Tingmao Wang
2025-06-04 2:21 ` Al Viro
2025-06-04 18:56 ` Tingmao Wang
2025-06-04 19:17 ` Tingmao Wang
2025-06-04 1:09 ` Tingmao Wang
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=20250604.ciecheo7EeNg@digikod.net \
--to=mic@digikod.net \
--cc=alexei.starovoitov@gmail.com \
--cc=brauner@kernel.org \
--cc=gnoack@google.com \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=song@kernel.org \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.