[PATCH v8 1/9] landlock: Add a place for flags to layer rules

Mickaël Salaün mic at digikod.net
Mon May 25 20:39:16 UTC 2026


On Sun, May 24, 2026 at 06:08:00PM -0400, Justin Suess wrote:
> On Sun, May 24, 2026 at 07:20:19PM +0100, Tingmao Wang wrote:
> > 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
> > 
> Yeah I guess the compiler can't pack the fields with differing types.
> 
> *In theory* you could make everything a _BitInt or something but it
> seems better to do what you had below.
> > 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;
> > };
> This works perfectly.
> 
> Mickaël's suggestion (except w/ all three access right classes like
> you have here, think he missed LANDLOCK_NUM_SCOPE) is very close
> to this.
> > 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.

LANDLOCK_NUM_ACCESS_MAX looks like a better name (even if it also
include scopes).

> > 
> > 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.
> 
> (Not sure how stingy we gotta be with stack space)
> 
> There's a *slight* stack space advantage to keeping them together.
> 
> If you pass by value, (separate layer_access_masks, collected_rule_flags),
> those structs must be individually padded and aligned. Which may or may not
> make a difference, it's dependent on alignment and architecture.
> 
> Whereas if we keep them all together, we only pad once.
> 
> If you pass by pointer, you have to allocate stack space for each
> pointer, so passing it all at once saves sizeof(collected_rule_flags*)
> bytes in the pass by pointer case.
> 
> Either way it's probably a couple bytes at worst, so probably nothing to
> worry about.
> 
> The more compelling argument is that we don't know how future paths
> will use rule flags, so keeping it all together reduces churn later
> if a function ends up needing to access flags. Moreover, it makes those
> messy function signatures in fs.h/c a little less hairy, and easy to
> refactor later.

Agreed



More information about the Linux-security-module-archive mailing list