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

Mickaël Salaün mic at digikod.net
Sun May 24 20:35:05 UTC 2026


On Sun, May 24, 2026 at 02:29:40AM +0100, Tingmao Wang wrote:
> On 5/23/26 21:48, Mickaël Salaün wrote:
> > This patch doesn't build.
> 
> Missed a hunk in this patch (ended up in the next one), will add.
> 
> >> @@ -797,20 +803,28 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
> >>  	}
> >>  
> >>  	if (unlikely(dentry_child1)) {
> >> +		/*
> >> +		 * Get the layer masks for the child dentries for use by domain
> >> +		 * check later.  The rule_flags for child1 should have been
> >> +		 * included in rule_flags_parent1 already (cf.
> >> +		 * collect_domain_accesses), and is not relevant for domain check,
> >> +		 * so we don't have to pass it to landlock_unmask_layers.
> >> +		 */
> >>  		if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
> >>  					      &_layer_masks_child1,
> >>  					      LANDLOCK_KEY_INODE))
> >>  			landlock_unmask_layers(find_rule(domain, dentry_child1),
> >> -					       &_layer_masks_child1);
> >> +					       &_layer_masks_child1, NULL);
> >>  		layer_masks_child1 = &_layer_masks_child1;
> >>  		child1_is_directory = d_is_dir(dentry_child1);
> >>  	}
> >>  	if (unlikely(dentry_child2)) {
> >> +		/* See above comment for why NULL is passed as rule_flags_masks. */
> > 
> > rule_flags_masks doesn't exist.
> 
> I guess I was probably referring to the rule_flags argument - will fix.
> 
> >> [...]
> >> @@ -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).

Most of these places are tests.

> 
> 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.
> 
> 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;
> };

Yes, like Justin, I prefer this approach too.  Some improvements:

// In limits.h:
#define LANDLOCK_MAX_NUM_ACCESSES \
	MAX(LANDLOCK_NUM_ACCESS_FS, LANDLOCK_NUM_ACCESS_NET)

// In access.h:
struct layer_mask {
	access_mask_t access:LANDLOCK_MAX_NUM_ACCESSES;
#ifdef CONFIG_AUDIT
	bool quiet:1; // I'm not sure if using bool would work for all
	// architectures though, but we can make sure with the following
	// assert.
#endif /* CONFIG_AUDIT */
};
static_assert(sizeof(struct layer_mask), sizeof(access_mask_t));

> struct layer_masks {
> 	struct layer_mask layer[LANDLOCK_MAX_NUM_LAYERS];
> };
> 
>    (Maybe we can just make struct layer_masks a typedef to
>    layer_mask[LANDLOCK_MAX_NUM_LAYERS] instead?  But currently not sure if
>    there are any gotchas with a typedef like that)

The only typedefs used in Landlock are for potentially growing types. So
no need for typedef here.

> 
> 3. Mirror layer_access_masks::access[] - add a
>    rule_flags[LANDLOCK_MAX_NUM_LAYERS] too.  struct layer_masks becomes 80
>    bytes (+16).
> 
> struct rule_flags {
> 	bool quiet:1;
> };
> struct layer_masks {
> 	/**
> 	 * @access: The unfulfilled access rights for each layer.
> 	 */
> 	access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> 	struct rule_flags rule_flags[LANDLOCK_MAX_NUM_LAYERS];
> };
> 
> (3 seems very wasteful to me)
> 



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