[PATCH v8 1/9] landlock: Add a place for flags to layer rules
Tingmao Wang
m at maowtm.org
Sun May 24 18:20:19 UTC 2026
On 5/24/26 15:46, Justin Suess wrote:
> On Sun, May 24, 2026 at 02:29:40AM +0100, Tingmao Wang wrote:
>> On 5/23/26 21:48, Mickaël Salaün wrote:
>>> [...]
>>>> @@ -647,9 +648,14 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
>>>> */
>>>> for (size_t i = 0; i < rule->num_layers; i++) {
>>>> const struct landlock_layer *const layer = &rule->layers[i];
>>>> + const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
>>>>
>>>
>>>> /* Clear the bits where the layer in the rule grants access. */
>>>> masks->access[layer->level - 1] &= ~layer->access;
>>>> +
>>>> + /* Collect rule flags for each layer. */
>>>> + if (rule_flags && layer->flags.quiet)
>>>> + rule_flags->quiet_masks |= layer_bit;
>>>
>>> Why not store the quiet bit in masks? That would not only be "access"
>>> bits anymore but it makes sense to store all this bits it the same
>>> place.
>>>
>>> We should then probably rename struct layer_access_masks to just struct
>>> layer_masks.
>>>
>>> We need to be careful to not increase too much the size of this struct
>>> though while keeping the [LANDLOCK_MAX_NUM_LAYERS] approach if possible
>>> (see Günther's commit that added it).
>>
>> Most uses of struct layer_access_masks do not actually care about the rule
>> flags tho, e.g. in unmask_scoped_access, scope_to_request, or may_refer.
>> Such a rename would touch 31 places (and only a few of them would actually
>> touch the quiet flag).
>>
>> If we want to refactor to make this be in the layer_access_masks (then
>> rename it), I guess there are 3 options, which do you prefer?
>>
>> 1. Add a u16 bitfield for which layers are quieted. Future rule flags
>> will be additional bitfields. struct layer_masks becomes 68 bytes (+4).
>>
>> struct layer_masks {
>> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
>> layer_mask_t quiet_layers;
>> };
>>
>> 2. Make the [LANDLOCK_MAX_NUM_LAYERS] array store both the access mask and
>> the quiet bit (or more bits for future rule flags). Size of struct stays
>> the same.
>>
> This approach seems best.
>> static_assert(LANDLOCK_NUM_ACCESS_NET <= LANDLOCK_NUM_ACCESS_FS);
>> static_assert(LANDLOCK_NUM_SCOPE <= LANDLOCK_NUM_ACCESS_FS);
>> struct layer_mask {
>> access_mask_t access:LANDLOCK_NUM_ACCESS_FS;
>> bool quiet:1;
>> };
>
> Other way to do it could be an (anonymous?) union.
>
> union {
> access_mask_t fs_access:LANDLOCK_NUM_ACCESS_FS;
> access_mask_t net_access:LANDLOCK_NUM_ACCESS_NET;
> access_mask_t scope_access:LANDLOCK_NUM_SCOPE;
> }
>
> The union should be sized to fit the largest field automatically.
>
> That way you don't have to change this when adding new access rights
> and avoid the brittle static_asserts.
>
> Not sure about the alignment implications here though.
Unfortunately this forces struct layer_mask to be 2x as large:
https://godbolt.org/z/5P9b4rrMW
But it turns out I could have just used MAX, seems to compile for me:
struct layer_mask {
access_mask_t access
: MAX(LANDLOCK_NUM_ACCESS_FS,
MAX(LANDLOCK_NUM_ACCESS_NET, LANDLOCK_NUM_SCOPE));
bool quiet : 1;
};
struct layer_masks {
struct layer_mask layer[LANDLOCK_MAX_NUM_LAYERS];
};
Maybe we could #define LANDLOCK_NUM_ACCESS_MAX to be MAX(...) then use it
here.
I'm still not sure if putting the collected rule flags in struct
layer_(access_)masks is a good idea tho. Passing a separate struct
collected_rule_flags to the functions that needs to deal with rule flags
(quiet, and later, no inherit / has no inherit descendant) seems quite
practical to me.
More information about the Linux-security-module-archive
mailing list