From: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
To: "Mickaël Salaün" <mic@digikod.net>, willemdebruijn.kernel@gmail.com
Cc: <linux-security-module@vger.kernel.org>, <netdev@vger.kernel.org>,
<netfilter-devel@vger.kernel.org>, <yusongping@huawei.com>,
<artem.kuzin@huawei.com>, <anton.sirazetdinov@huawei.com>
Subject: Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
Date: Tue, 22 Mar 2022 15:33:17 +0300 [thread overview]
Message-ID: <cb30248d-a8ae-c366-2c9f-2ab0fe44cc9a@huawei.com> (raw)
In-Reply-To: <92d498f0-c598-7413-6b7c-d19c5aec6cab@digikod.net>
3/18/2022 9:33 PM, Mickaël Salaün пишет:
>
> On 17/03/2022 15:29, Konstantin Meskhidze wrote:
>>
>>
>> 3/16/2022 11:27 AM, Mickaël Salaün пишет:
>>>
>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>> A new object union added to support a socket port
>>>> rule type. To support it landlock_insert_rule() and
>>>> landlock_find_rule() were refactored. Now adding
>>>> or searching a rule in a ruleset depends on a
>>>> rule_type argument provided in refactored
>>>> functions mentioned above.
>>>>
>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>> ---
>>>>
>>>> Changes since v3:
>>>> * Split commit.
>>>> * Refactoring landlock_insert_rule and landlock_find_rule functions.
>>>> * Rename new_ruleset->root_inode.
>>>>
>>>> ---
>>>> security/landlock/fs.c | 5 +-
>>>> security/landlock/ruleset.c | 108
>>>> +++++++++++++++++++++++++-----------
>>>> security/landlock/ruleset.h | 26 +++++----
>>>> 3 files changed, 94 insertions(+), 45 deletions(-)
>>>>
>>>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>>>> index 97f5c455f5a7..1497948d754f 100644
>>>> --- a/security/landlock/fs.c
>>>> +++ b/security/landlock/fs.c
>>>> @@ -168,7 +168,7 @@ int landlock_append_fs_rule(struct
>>>> landlock_ruleset *const ruleset,
>>>> if (IS_ERR(object))
>>>> return PTR_ERR(object);
>>>> mutex_lock(&ruleset->lock);
>>>> - err = landlock_insert_rule(ruleset, object, access_rights);
>>>> + err = landlock_insert_rule(ruleset, object, 0, access_rights,
>>>> LANDLOCK_RULE_PATH_BENEATH);
>>>
>>> For consistency, please use 80 columns everywhere.
>>
>> Ok. I got it.
>>>
>>>> mutex_unlock(&ruleset->lock);
>>>> /*
>>>> * No need to check for an error because landlock_insert_rule()
>>>> @@ -195,7 +195,8 @@ static inline u64 unmask_layers(
>>>> inode = d_backing_inode(path->dentry);
>>>> rcu_read_lock();
>>>> rule = landlock_find_rule(domain,
>>>> - rcu_dereference(landlock_inode(inode)->object));
>>>> + (uintptr_t)rcu_dereference(landlock_inode(inode)->object),
>>>> + LANDLOCK_RULE_PATH_BENEATH);
>>>> rcu_read_unlock();
>>>> if (!rule)
>>>> return layer_mask;
>>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>>> index a6212b752549..971685c48641 100644
>>>> --- a/security/landlock/ruleset.c
>>>> +++ b/security/landlock/ruleset.c
>>>> @@ -34,7 +34,7 @@ static struct landlock_ruleset
>>>> *create_ruleset(const u32 num_layers)
>>>> return ERR_PTR(-ENOMEM);
>>>> refcount_set(&new_ruleset->usage, 1);
>>>> mutex_init(&new_ruleset->lock);
>>>> - new_ruleset->root = RB_ROOT;
>>>> + new_ruleset->root_inode = RB_ROOT;
>>>> new_ruleset->num_layers = num_layers;
>>>> /*
>>>> * hierarchy = NULL
>>>> @@ -81,10 +81,12 @@ static void build_check_rule(void)
>>>> }
>>>>
>>>> static struct landlock_rule *create_rule(
>>>> - struct landlock_object *const object,
>>>> + struct landlock_object *const object_ptr,
>>>> + const uintptr_t object_data,
>>>> const struct landlock_layer (*const layers)[],
>>>> const u32 num_layers,
>>>> - const struct landlock_layer *const new_layer)
>>>> + const struct landlock_layer *const new_layer,
>>>> + const u16 rule_type)
>>>> {
>>>> struct landlock_rule *new_rule;
>>>> u32 new_num_layers;
>>>> @@ -103,8 +105,16 @@ static struct landlock_rule *create_rule(
>>>> if (!new_rule)
>>>> return ERR_PTR(-ENOMEM);
>>>> RB_CLEAR_NODE(&new_rule->node);
>>>> - landlock_get_object(object);
>>>> - new_rule->object = object;
>>>> +
>>>> + switch (rule_type) {
>>>> + case LANDLOCK_RULE_PATH_BENEATH:
>>>> + landlock_get_object(object_ptr);
>>>> + new_rule->object.ptr = object_ptr;
>>>> + break;
>>>> + default:
>>>> + return ERR_PTR(-EINVAL);
>>>
>>> This would lead to memory leak. You should at least add a
>>> WARN_ON_ONCE(1) here, but a proper solution would be to remove the
>>> use of rule_type and only rely on object_ptr and object_data values.
>>> You can also add a WARN_ON_ONCE(object_ptr && object_data).
>>>
>>>
>> But rule_type is needed here in coming commits to support network
>> rules. For LANDLOCK_RULE_PATH_BENEATH rule type
>> landlock_get_object() is used but for LANDLOCK_RULE_NET_SERVICE is
>> not. Using rule type is convenient for distinguising between fs and
>> network rules.
>
> rule_type is not required to infer if the rule use a pointer or raw
> data, even with the following commits, because you can rely on
> object_ptr being NULL or not. This would make create_rule() generic for
> pointer-based and data-based object, even if not-yet-existing rule
> types. It is less error-prone to only be able to infer something from
> one source (i.e. object_ptr and not rule_type).
>
Ok. I got you. Will be refactored.
>
>>>> + }
>>>> +
>>>> new_rule->num_layers = new_num_layers;
>>>> /* Copies the original layer stack. */
>>>> memcpy(new_rule->layers, layers,
>>>> @@ -120,7 +130,7 @@ static void free_rule(struct landlock_rule
>>>> *const rule)
>>>> might_sleep();
>>>> if (!rule)
>>>> return;
>>>> - landlock_put_object(rule->object);
>>>> + landlock_put_object(rule->object.ptr);
>>>> kfree(rule);
>>>> }
>>>>
>>>> @@ -156,26 +166,38 @@ static void build_check_ruleset(void)
>>>> * access rights.
>>>> */
>>>> static int insert_rule(struct landlock_ruleset *const ruleset,
>>>> - struct landlock_object *const object,
>>>> + struct landlock_object *const object_ptr,
>>>> + const uintptr_t object_data,
>
> Can you move rule_type here for this function and similar ones? It makes
> sense to group object-related arguments.
Just to group them together, not putting rule_type in the end?
>
>
>>>> const struct landlock_layer (*const layers)[],
>>>> - size_t num_layers)
>>>> + size_t num_layers, u16 rule_type)
>>>> {
>>>> struct rb_node **walker_node;
>>>> struct rb_node *parent_node = NULL;
>>>> struct landlock_rule *new_rule;
>>>> + uintptr_t object;
>>>> + struct rb_root *root;
>>>>
>>>> might_sleep();
>>>> lockdep_assert_held(&ruleset->lock);
>>>> - if (WARN_ON_ONCE(!object || !layers))
>>>> - return -ENOENT;
>>>
>>> You can leave this code here.
>>
>> But anyway in coming commits with network rules this code will be
>> moved into case LANDLOCK_RULE_PATH_BENEATH: ....
>
> Yes, but without rule_type you don't need to duplicate this check, just
> to remove object_ptr from WARN_ON_ONCE() and replace the rule_type
> switch/case with if (object_ptr).
>
> You can change to this:
>
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -194,43 +194,49 @@ static void build_check_ruleset(void)
> */
> static int insert_rule(struct landlock_ruleset *const ruleset,
> struct landlock_object *const object_ptr,
> - const uintptr_t object_data,
> + uintptr_t object_data, /* move @rule_type here */
> const struct landlock_layer (*const layers)[],
> - size_t num_layers, u16 rule_type)
> + size_t num_layers, const enum landlock_rule_type rule_type)
> {
> struct rb_node **walker_node;
> struct rb_node *parent_node = NULL;
> struct landlock_rule *new_rule;
> - uintptr_t object;
> struct rb_root *root;
>
> might_sleep();
> lockdep_assert_held(&ruleset->lock);
> - /* Choose rb_tree structure depending on a rule type */
> +
> + if (WARN_ON_ONCE(!layers))
> + return -ENOENT;
> + if (WARN_ON_ONCE(object_ptr && object_data))
> + return -EINVAL;
> +
> + /* Chooses the rb_tree according to the rule type. */
> switch (rule_type) {
> case LANDLOCK_RULE_PATH_BENEATH:
> - if (WARN_ON_ONCE(!object_ptr || !layers))
> + if (WARN_ON_ONCE(!object_ptr))
> return -ENOENT;
> - object = (uintptr_t)object_ptr;
> + object_data = (uintptr_t)object_ptr;
> root = &ruleset->root_inode;
> break;
> case LANDLOCK_RULE_NET_SERVICE:
> - if (WARN_ON_ONCE(!object_data || !layers))
> - return -ENOENT;
> - object = object_data;
> + if (WARN_ON_ONCE(object_ptr))
> + return -EINVAL;
> root = &ruleset->root_net_port;
> break;
> default:
> + WARN_ON_ONCE(1);
> return -EINVAL;
> }
> +
> walker_node = &root->rb_node;
> while (*walker_node) {
> struct landlock_rule *const this = rb_entry(*walker_node,
> struct landlock_rule, node);
>
> - if (this->object.data != object) {
> + if (this->object.data != object_data) {
> parent_node = *walker_node;
> - if (this->object.data < object)
> + if (this->object.data < object_data)
> walker_node = &((*walker_node)->rb_right);
> else
> walker_node = &((*walker_node)->rb_left);
>
>
> This highlight an implicit error handling for a port value of 0. I'm not
> sure if this should be allowed or not though. If not, it should be an
> explicit service_port check in add_rule_net_service(). A data value of
> zero might be legitimate for this use case or not-yet-existing
> data-based rule types. Anyway, this kind of check is specific to the use
> case and should not be part of insert_rule().
>
Ok. I got it.
>
>
>>>
>>>> - walker_node = &(ruleset->root.rb_node);
>>>> + /* Choose rb_tree structure depending on a rule type */
>>>> + switch (rule_type) {
>>>> + case LANDLOCK_RULE_PATH_BENEATH:
>>>> + if (WARN_ON_ONCE(!object_ptr || !layers))
>>>> + return -ENOENT;
>>>> + object = (uintptr_t)object_ptr;
>>>> + root = &ruleset->root_inode;
>>>> + break;
>>>> + default:
>>>> + return -EINVAL;
>>>> + }
>>>> + walker_node = &root->rb_node;
>>>> while (*walker_node) {
>>>> struct landlock_rule *const this = rb_entry(*walker_node,
>>>> struct landlock_rule, node);
>>>>
>>>> - if (this->object != object) {
>>>> + if (this->object.data != object) {
>>>> parent_node = *walker_node;
>>>> - if (this->object < object)
>>>> + if (this->object.data < object)
>>>> walker_node = &((*walker_node)->rb_right);
>>>> else
>>>> walker_node = &((*walker_node)->rb_left);
>>>> @@ -207,11 +229,15 @@ static int insert_rule(struct landlock_ruleset
>>>> *const ruleset,
>>>> * Intersects access rights when it is a merge between a
>>>> * ruleset and a domain.
>>>> */
>>>> - new_rule = create_rule(object, &this->layers,
>>>> this->num_layers,
>>>> - &(*layers)[0]);
>>>> + switch (rule_type) {
>>>> + case LANDLOCK_RULE_PATH_BENEATH:
>>>
>>> Same here and for the following code, you should replace such
>>> switch/case with an if (object_ptr).
>>> What about coming commits with network rule_type support?
>
> This will still works.
>
Yep. Ok.
>
>>>
>>>> + new_rule = create_rule(object_ptr, 0, &this->layers,
>>>> this->num_layers,
>>>> + &(*layers)[0], rule_type);
>>>> + break;
>>>> + }
>>>> if (IS_ERR(new_rule))
>>>> return PTR_ERR(new_rule);
>>>> - rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
>>>> + rb_replace_node(&this->node, &new_rule->node,
>>>> &ruleset->root_inode);
>>>
>>> Use the root variable here. Same for the following code and patches.
>>
>> What about your suggestion to use 2 rb_tress to support different
>> rule_types:
>> 1. root_inode - for filesystem objects
>> 2. root_net_port - for network port objects
>> ????
>
> I was talking about the root variable you declared a few line before.
> The conversion from ruleset->root to ruleset->root_inode is fine.
>
Sorry. It was a misunderstanding. Got your point.
>
> [...]
>
>>>> @@ -465,20 +501,28 @@ struct landlock_ruleset *landlock_merge_ruleset(
>>>> */
>>>> const struct landlock_rule *landlock_find_rule(
>>>> const struct landlock_ruleset *const ruleset,
>>>> - const struct landlock_object *const object)
>>>> + const uintptr_t object_data, const u16 rule_type)
>>>> {
>>>> const struct rb_node *node;
>>>>
>>>> - if (!object)
>>>> + if (!object_data)
>>>
>>> object_data can be 0. You need to add a test with such value.
>>>
>>> We need to be sure that this change cannot affect the current FS code.
>>
>> I got it. I will refactor it.
>
> Well, 0 means a port 0, which might not be correct, but this check
> should not be performed by landlock_merge_ruleset().
>
Do you mean landlock_find_rule()?? Cause this check is not
performed in landlock_merge_ruleset().
>
>>>
>>>
>>>> return NULL;
>>>> - node = ruleset->root.rb_node;
>>>> +
>>>> + switch (rule_type) {
>>>> + case LANDLOCK_RULE_PATH_BENEATH:
>>>> + node = ruleset->root_inode.rb_node;
>>>> + break;
>>>> + default:
>>>> + return ERR_PTR(-EINVAL);
>>>
>>> This is a bug. There is no check for such value. You need to check
>>> and update all call sites to catch such errors. Same for all new use
>>> of ERR_PTR().
>>
>> Sorry, I did not get your point.
>> Do you mean I should check the correctness of rule_type in above
>> function which calls landlock_find_rule() ??? Why can't I add such
>> check here?
>
> landlock_find_rule() only returns NULL or a valid pointer, not an error.
What about incorrect rule_type?? Return NULL? Or final rule_checl
must be in upper function?
>
> [...]
> .
next prev parent reply other threads:[~2022-03-22 12:33 UTC|newest]
Thread overview: 63+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 01/15] landlock: access mask renaming Konstantin Meskhidze
2022-04-01 16:47 ` Mickaël Salaün
2022-04-04 8:17 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 02/15] landlock: filesystem access mask helpers Konstantin Meskhidze
2022-03-15 17:48 ` Mickaël Salaün
2022-03-17 13:25 ` Konstantin Meskhidze
2022-03-17 18:03 ` Mickaël Salaün
2022-03-18 11:36 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring Konstantin Meskhidze
2022-03-16 8:27 ` Mickaël Salaün
2022-03-17 14:29 ` Konstantin Meskhidze
2022-03-18 18:33 ` Mickaël Salaün
2022-03-22 12:33 ` Konstantin Meskhidze [this message]
2022-03-22 13:24 ` Mickaël Salaün
2022-03-23 8:41 ` Konstantin Meskhidze
2022-04-12 11:07 ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring (TCP port 0) Mickaël Salaün
2022-04-26 9:15 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 04/15] landlock: merge and inherit function refactoring Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 05/15] landlock: unmask_layers() " Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring Konstantin Meskhidze
2022-04-12 11:12 ` Mickaël Salaün
2022-04-26 8:30 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 07/15] landlock: user space API network support Konstantin Meskhidze
2022-04-12 11:21 ` Mickaël Salaün
2022-04-12 13:48 ` Mickaël Salaün
2022-04-12 14:05 ` Konstantin Meskhidze
2022-04-12 16:10 ` Mickaël Salaün
2022-04-26 10:17 ` Konstantin Meskhidze
2022-04-25 14:29 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 08/15] landlock: add support network rules Konstantin Meskhidze
2022-04-08 16:30 ` Mickaël Salaün
2022-04-11 13:44 ` Konstantin Meskhidze
2022-04-11 16:20 ` Mickaël Salaün
2022-04-12 8:38 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 09/15] landlock: TCP network hooks implementation Konstantin Meskhidze
2022-04-11 16:24 ` Mickaël Salaün
2022-04-26 8:36 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks Konstantin Meskhidze
2022-04-01 16:52 ` Mickaël Salaün
2022-04-04 8:28 ` Konstantin Meskhidze
2022-04-04 9:44 ` Mickaël Salaün
2022-04-06 14:12 ` Konstantin Meskhidze
2022-04-08 16:41 ` Mickaël Salaün
2022-04-26 9:35 ` Konstantin Meskhidze
2022-05-16 10:10 ` Mickaël Salaün
2022-05-16 10:22 ` Konstantin Meskhidze
2022-04-04 18:32 ` Mickaël Salaün
2022-04-06 14:17 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 11/15] seltest/landlock: add tests for connect() hooks Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 12/15] seltest/landlock: connect() with AF_UNSPEC tests Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 13/15] seltest/landlock: rules overlapping test Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 14/15] seltest/landlock: ruleset expanding test Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 15/15] seltest/landlock: invalid user input data test Konstantin Meskhidze
2022-03-15 17:02 ` [RFC PATCH v4 00/15] Landlock LSM Mickaël Salaün
2022-03-17 13:01 ` Konstantin Meskhidze
2022-03-17 17:26 ` Mickaël Salaün
2022-03-18 15:55 ` Konstantin Meskhidze
2022-03-23 16:30 ` Konstantin Meskhidze
2022-03-24 12:27 ` Mickaël Salaün
2022-03-24 13:34 ` Konstantin Meskhidze
2022-03-24 15:30 ` Mickaël Salaün
2022-03-24 16:19 ` Konstantin Meskhidze
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=cb30248d-a8ae-c366-2c9f-2ab0fe44cc9a@huawei.com \
--to=konstantin.meskhidze@huawei.com \
--cc=anton.sirazetdinov@huawei.com \
--cc=artem.kuzin@huawei.com \
--cc=linux-security-module@vger.kernel.org \
--cc=mic@digikod.net \
--cc=netdev@vger.kernel.org \
--cc=netfilter-devel@vger.kernel.org \
--cc=willemdebruijn.kernel@gmail.com \
--cc=yusongping@huawei.com \
/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;
as well as URLs for NNTP newsgroup(s).