Re: [RFC PATCH 2/9] Refactor per-layer information in rulesets and rules

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Mar 04, 2025 at 01:12:58AM +0000, Tingmao Wang wrote:
> We need a place to store the supervisor pointer for each layer in
> a domain.  Currently, the domain has a trailing flexible array
> for handled access masks of each layer.  This patch extends it by
> creating a separate landlock_ruleset_layer structure that will
> hold this access mask, and make the ruleset's flexible array use
> this structure instead.
> 
> An alternative is to use landlock_hierarchy, but I have chosen to
> extend the FAM as this is makes it more clear the supervisor
> pointer is tied to layers, just like access masks.

We could indeed have a pointer in the  landlock_hierarchy and have a
dedicated bit in each layer's access_masks to indicate that this layer
is supervised.  This should simplify the whole patch series.

> 
> This patch doesn't make any functional changes nor add any
> supervise specific stuff.  It is purely to pave the way for
> future patches.
> 
> Signed-off-by: Tingmao Wang <m@xxxxxxxxxx>
> ---
>  security/landlock/ruleset.c  | 29 +++++++++---------
>  security/landlock/ruleset.h  | 59 ++++++++++++++++++++++--------------
>  security/landlock/syscalls.c |  2 +-
>  3 files changed, 52 insertions(+), 38 deletions(-)
> 
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 69742467a0cf..2cc6f7c5eb1b 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -31,9 +31,8 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>  {
>  	struct landlock_ruleset *new_ruleset;
>  
> -	new_ruleset =
> -		kzalloc(struct_size(new_ruleset, access_masks, num_layers),
> -			GFP_KERNEL_ACCOUNT);
> +	new_ruleset = kzalloc(struct_size(new_ruleset, layer_stack, num_layers),
> +			      GFP_KERNEL_ACCOUNT);
>  	if (!new_ruleset)
>  		return ERR_PTR(-ENOMEM);
>  	refcount_set(&new_ruleset->usage, 1);
> @@ -104,8 +103,9 @@ static bool is_object_pointer(const enum landlock_key_type key_type)
>  
>  static struct landlock_rule *
>  create_rule(const struct landlock_id id,
> -	    const struct landlock_layer (*const layers)[], const u32 num_layers,
> -	    const struct landlock_layer *const new_layer)
> +	    const struct landlock_rule_layer (*const layers)[],
> +	    const u32 num_layers,
> +	    const struct landlock_rule_layer *const new_layer)
>  {
>  	struct landlock_rule *new_rule;
>  	u32 new_num_layers;
> @@ -201,7 +201,7 @@ static void build_check_ruleset(void)
>   */
>  static int insert_rule(struct landlock_ruleset *const ruleset,
>  		       const struct landlock_id id,
> -		       const struct landlock_layer (*const layers)[],
> +		       const struct landlock_rule_layer (*const layers)[],
>  		       const size_t num_layers)
>  {
>  	struct rb_node **walker_node;
> @@ -284,7 +284,7 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
>  
>  static void build_check_layer(void)
>  {
> -	const struct landlock_layer layer = {
> +	const struct landlock_rule_layer layer = {

It's not useful to rename this struct.

>  		.level = ~0,
>  		.access = ~0,
>  	};
> @@ -299,7 +299,7 @@ int landlock_insert_rule(struct landlock_ruleset *const ruleset,
>  			 const struct landlock_id id,
>  			 const access_mask_t access)
>  {
> -	struct landlock_layer layers[] = { {
> +	struct landlock_rule_layer layers[] = { {
>  		.access = access,
>  		/* When @level is zero, insert_rule() extends @ruleset. */
>  		.level = 0,
> @@ -344,7 +344,7 @@ static int merge_tree(struct landlock_ruleset *const dst,
>  	/* Merges the @src tree. */
>  	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, src_root,
>  					     node) {
> -		struct landlock_layer layers[] = { {
> +		struct landlock_rule_layer layers[] = { {
>  			.level = dst->num_layers,
>  		} };
>  		const struct landlock_id id = {
> @@ -389,8 +389,9 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>  		err = -EINVAL;
>  		goto out_unlock;
>  	}
> -	dst->access_masks[dst->num_layers - 1] =
> -		landlock_upgrade_handled_access_masks(src->access_masks[0]);
> +	dst->layer_stack[dst->num_layers - 1].access_masks =
> +		landlock_upgrade_handled_access_masks(
> +			src->layer_stack[0].access_masks);
>  
>  	/* Merges the @src inode tree. */
>  	err = merge_tree(dst, src, LANDLOCK_KEY_INODE);
> @@ -472,8 +473,8 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>  		goto out_unlock;
>  	}
>  	/* Copies the parent layer stack and leaves a space for the new layer. */
> -	memcpy(child->access_masks, parent->access_masks,
> -	       flex_array_size(parent, access_masks, parent->num_layers));
> +	memcpy(child->layer_stack, parent->layer_stack,
> +	       flex_array_size(parent, layer_stack, parent->num_layers));
>  
>  	if (WARN_ON_ONCE(!parent->hierarchy)) {
>  		err = -EINVAL;
> @@ -644,7 +645,7 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
>  	 * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
>  	 */
>  	for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
> -		const struct landlock_layer *const layer =
> +		const struct landlock_rule_layer *const layer =
>  			&rule->layers[layer_level];
>  		const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
>  		const unsigned long access_req = access_request;
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 52f4f0af6ab0..a2605959f733 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -21,9 +21,10 @@
>  #include "object.h"
>  
>  /**
> - * struct landlock_layer - Access rights for a given layer
> + * struct landlock_rule_layer - Stores the access rights for a
> + * given layer in a rule.
>   */
> -struct landlock_layer {
> +struct landlock_rule_layer {
>  	/**
>  	 * @level: Position of this layer in the layer stack.
>  	 */
> @@ -102,10 +103,11 @@ struct landlock_rule {
>  	 */
>  	u32 num_layers;
>  	/**
> -	 * @layers: Stack of layers, from the latest to the newest, implemented
> -	 * as a flexible array member (FAM).
> +	 * @layers: Stack of layers, from the latest to the newest,
> +	 * implemented as a flexible array member (FAM). Only
> +	 * contains layers that has a rule for this object.
>  	 */
> -	struct landlock_layer layers[] __counted_by(num_layers);
> +	struct landlock_rule_layer layers[] __counted_by(num_layers);
>  };
>  
>  /**
> @@ -124,6 +126,18 @@ struct landlock_hierarchy {
>  	refcount_t usage;
>  };
>  
> +/**
> + * struct landlock_ruleset_layer - Store per-layer information
> + * within a domain (or a non-merged ruleset)
> + */
> +struct landlock_ruleset_layer {
> +	/**
> +	 * @access_masks: Contains the subset of filesystem and
> +	 * network actions that are restricted by a layer.
> +	 */
> +	struct access_masks access_masks;
> +};
> +
>  /**
>   * struct landlock_ruleset - Landlock ruleset
>   *
> @@ -187,18 +201,17 @@ struct landlock_ruleset {
>  			 */
>  			u32 num_layers;
>  			/**
> -			 * @access_masks: Contains the subset of filesystem and
> -			 * network actions that are restricted by a ruleset.
> -			 * A domain saves all layers of merged rulesets in a
> -			 * stack (FAM), starting from the first layer to the
> -			 * last one.  These layers are used when merging
> -			 * rulesets, for user space backward compatibility
> -			 * (i.e. future-proof), and to properly handle merged
> -			 * rulesets without overlapping access rights.  These
> -			 * layers are set once and never changed for the
> -			 * lifetime of the ruleset.
> +			 * @layer_stack: A domain saves all layers of merged
> +			 * rulesets in a stack (FAM), starting from the first
> +			 * layer to the last one.  These layers are used when
> +			 * merging rulesets, for user space backward
> +			 * compatibility (i.e. future-proof), and to properly
> +			 * handle merged rulesets without overlapping access
> +			 * rights.  These layers are set once and never
> +			 * changed for the lifetime of the ruleset.
>  			 */
> -			struct access_masks access_masks[];
> +			struct landlock_ruleset_layer
> +				layer_stack[] __counted_by(num_layers);
>  		};
>  	};
>  };
> @@ -248,7 +261,7 @@ landlock_union_access_masks(const struct landlock_ruleset *const domain)
>  
>  	for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
>  		union access_masks_all layer = {
> -			.masks = domain->access_masks[layer_level],
> +			.masks = domain->layer_stack[layer_level].access_masks,
>  		};
>  
>  		matches.all |= layer.all;
> @@ -296,7 +309,7 @@ landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
>  
>  	/* Should already be checked in sys_landlock_create_ruleset(). */
>  	WARN_ON_ONCE(fs_access_mask != fs_mask);
> -	ruleset->access_masks[layer_level].fs |= fs_mask;
> +	ruleset->layer_stack[layer_level].access_masks.fs |= fs_mask;
>  }
>  
>  static inline void
> @@ -308,7 +321,7 @@ landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
>  
>  	/* Should already be checked in sys_landlock_create_ruleset(). */
>  	WARN_ON_ONCE(net_access_mask != net_mask);
> -	ruleset->access_masks[layer_level].net |= net_mask;
> +	ruleset->layer_stack[layer_level].access_masks.net |= net_mask;
>  }
>  
>  static inline void
> @@ -319,7 +332,7 @@ landlock_add_scope_mask(struct landlock_ruleset *const ruleset,
>  
>  	/* Should already be checked in sys_landlock_create_ruleset(). */
>  	WARN_ON_ONCE(scope_mask != mask);
> -	ruleset->access_masks[layer_level].scope |= mask;
> +	ruleset->layer_stack[layer_level].access_masks.scope |= mask;
>  }
>  
>  static inline access_mask_t
> @@ -327,7 +340,7 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>  			    const u16 layer_level)
>  {
>  	/* Handles all initially denied by default access rights. */
> -	return ruleset->access_masks[layer_level].fs |
> +	return ruleset->layer_stack[layer_level].access_masks.fs |
>  	       _LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
>  }
>  
> @@ -335,14 +348,14 @@ static inline access_mask_t
>  landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
>  			     const u16 layer_level)
>  {
> -	return ruleset->access_masks[layer_level].net;
> +	return ruleset->layer_stack[layer_level].access_masks.net;
>  }
>  
>  static inline access_mask_t
>  landlock_get_scope_mask(const struct landlock_ruleset *const ruleset,
>  			const u16 layer_level)
>  {
> -	return ruleset->access_masks[layer_level].scope;
> +	return ruleset->layer_stack[layer_level].access_masks.scope;
>  }
>  
>  bool landlock_unmask_layers(const struct landlock_rule *const rule,
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index a9760d252fc2..ead9b68168ad 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -313,7 +313,7 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>  		return -ENOMSG;
>  
>  	/* Checks that allowed_access matches the @ruleset constraints. */
> -	mask = ruleset->access_masks[0].fs;
> +	mask = landlock_get_fs_access_mask(ruleset, 0);
>  	if ((path_beneath_attr.allowed_access | mask) != mask)
>  		return -EINVAL;
>  
> -- 
> 2.39.5
> 
> 




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux