[PATCH v1 06/11] landlock: Add support for file reparenting with LANDLOCK_ACCESS_FS_REFER

Mickaël Salaün mic at digikod.net
Tue Feb 22 10:18:17 UTC 2022


This error is because clang does not behave like GCC: 
check_access_path_dual() should be marked as __always_inline, or I 
should change from BUILD_BUG_ON() to WARN_ON_ONCE() if needed. I'll fix 
that in the next series.

On 22/02/2022 04:16, kernel test robot wrote:
> Hi "Mickaël,
> 
> I love your patch! Yet something to improve:
> 
> [auto build test ERROR on cfb92440ee71adcc2105b0890bb01ac3cddb8507]
> 
> url:    https://github.com/0day-ci/linux/commits/Micka-l-Sala-n/Landlock-file-linking-and-renaming-support/20220222-051842
> base:   cfb92440ee71adcc2105b0890bb01ac3cddb8507
> config: hexagon-randconfig-r002-20220221 (https://download.01.org/0day-ci/archive/20220222/202202221149.qLO9DEqo-lkp@intel.com/config)
> compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project d271fc04d5b97b12e6b797c6067d3c96a8d7470e)
> reproduce (this is a W=1 build):
>          wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>          chmod +x ~/bin/make.cross
>          # https://github.com/0day-ci/linux/commit/c68b879f54d6262963d435a18cedbc238b7faeaf
>          git remote add linux-review https://github.com/0day-ci/linux
>          git fetch --no-tags linux-review Micka-l-Sala-n/Landlock-file-linking-and-renaming-support/20220222-051842
>          git checkout c68b879f54d6262963d435a18cedbc238b7faeaf
>          # save the config file to linux build tree
>          mkdir build_dir
>          COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp at intel.com>
> 
> All errors (new ones prefixed by >>):
> 
>>> security/landlock/fs.c:463:2: error: call to __compiletime_assert_228 declared with 'error' attribute: BUILD_BUG_ON failed: !layer_masks_dst_parent
>             BUILD_BUG_ON(!layer_masks_dst_parent);
>             ^
>     include/linux/build_bug.h:50:2: note: expanded from macro 'BUILD_BUG_ON'
>             BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
>             ^
>     include/linux/build_bug.h:39:37: note: expanded from macro 'BUILD_BUG_ON_MSG'
>     #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
>                                         ^
>     include/linux/compiler_types.h:346:2: note: expanded from macro 'compiletime_assert'
>             _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
>             ^
>     include/linux/compiler_types.h:334:2: note: expanded from macro '_compiletime_assert'
>             __compiletime_assert(condition, msg, prefix, suffix)
>             ^
>     include/linux/compiler_types.h:327:4: note: expanded from macro '__compiletime_assert'
>                             prefix ## suffix();                             \
>                             ^
>     <scratch space>:170:1: note: expanded from here
>     __compiletime_assert_228
>     ^
>>> security/landlock/fs.c:670:2: error: call to __compiletime_assert_229 declared with 'error' attribute: BUILD_BUG_ON failed: !layer_masks_dom
>             BUILD_BUG_ON(!layer_masks_dom);
>             ^
>     include/linux/build_bug.h:50:2: note: expanded from macro 'BUILD_BUG_ON'
>             BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
>             ^
>     include/linux/build_bug.h:39:37: note: expanded from macro 'BUILD_BUG_ON_MSG'
>     #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
>                                         ^
>     include/linux/compiler_types.h:346:2: note: expanded from macro 'compiletime_assert'
>             _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
>             ^
>     include/linux/compiler_types.h:334:2: note: expanded from macro '_compiletime_assert'
>             __compiletime_assert(condition, msg, prefix, suffix)
>             ^
>     include/linux/compiler_types.h:327:4: note: expanded from macro '__compiletime_assert'
>                             prefix ## suffix();                             \
>                             ^
>     <scratch space>:174:1: note: expanded from here
>     __compiletime_assert_229
>     ^
>     2 errors generated.
> 
> 
> vim +/error +463 security/landlock/fs.c
> 
>     401	
>     402	/**
>     403	 * check_access_path_dual - Check a source and a destination accesses
>     404	 *
>     405	 * @domain: Domain to check against.
>     406	 * @path: File hierarchy to walk through.
>     407	 * @child_is_directory: Must be set to true if the (original) leaf is a
>     408	 *     directory, false otherwise.
>     409	 * @access_request_dst_parent: Accesses to check, once @layer_masks_dst_parent
>     410	 *     is equal to @layer_masks_src_parent (if any).
>     411	 * @layer_masks_dst_parent: Pointer to a matrix of layer masks per access
>     412	 *     masks, identifying the layers that forbid a specific access.  Bits from
>     413	 *     this matrix can be unset according to the @path walk.  An empty matrix
>     414	 *     means that @domain allows all possible Landlock accesses (i.e. not only
>     415	 *     those identified by @access_request_dst_parent).  This matrix can
>     416	 *     initially refer to domain layer masks and, when the accesses for the
>     417	 *     destination and source are the same, to request layer masks.
>     418	 * @access_request_src_parent: Similar to @access_request_dst_parent but for an
>     419	 *     initial source path request.  Only taken into account if
>     420	 *     @layer_masks_src_parent is not NULL.
>     421	 * @layer_masks_src_parent: Similar to @layer_masks_dst_parent but for an
>     422	 *     initial source path walk.  This can be NULL if only dealing with a
>     423	 *     destination access request (i.e. not a rename nor a link action).
>     424	 * @layer_masks_child: Similar to @layer_masks_src_parent but only for the
>     425	 *     linked or renamed inode (without hierarchy).  This is only used if
>     426	 *     @layer_masks_src_parent is not NULL.
>     427	 *
>     428	 * This helper first checks that the destination has a superset of restrictions
>     429	 * compared to the source (if any) for a common path.  It then checks that the
>     430	 * collected accesses and the remaining ones are enough to allow the request.
>     431	 *
>     432	 * Returns:
>     433	 * - 0 if the access request is granted;
>     434	 * - -EACCES if it is denied because of access right other than
>     435	 *   LANDLOCK_ACCESS_FS_REFER;
>     436	 * - -EXDEV if the renaming or linking would be a privileged escalation
>     437	 *   (according to each layered policies), or if LANDLOCK_ACCESS_FS_REFER is
>     438	 *   not allowed by the source or the destination.
>     439	 */
>     440	static int check_access_path_dual(const struct landlock_ruleset *const domain,
>     441			const struct path *const path,
>     442			bool child_is_directory,
>     443			const access_mask_t access_request_dst_parent,
>     444			layer_mask_t (*const
>     445				layer_masks_dst_parent)[LANDLOCK_NUM_ACCESS_FS],
>     446			const access_mask_t access_request_src_parent,
>     447			layer_mask_t (*layer_masks_src_parent)[LANDLOCK_NUM_ACCESS_FS],
>     448			layer_mask_t (*layer_masks_child)[LANDLOCK_NUM_ACCESS_FS])
>     449	{
>     450		bool allowed_dst_parent = false, allowed_src_parent = false, is_dom_check;
>     451		struct path walker_path;
>     452		access_mask_t access_masked_dst_parent, access_masked_src_parent;
>     453	
>     454		if (!access_request_dst_parent && !access_request_src_parent)
>     455			return 0;
>     456		if (WARN_ON_ONCE(!domain || !path))
>     457			return 0;
>     458		if (is_nouser_or_private(path->dentry))
>     459			return 0;
>     460		if (WARN_ON_ONCE(domain->num_layers < 1))
>     461			return -EACCES;
>     462	
>   > 463		BUILD_BUG_ON(!layer_masks_dst_parent);
>     464		if (layer_masks_src_parent) {
>     465			if (WARN_ON_ONCE(!layer_masks_child))
>     466				return -EACCES;
>     467			access_masked_dst_parent = access_masked_src_parent =
>     468				get_handled_accesses(domain);
>     469			is_dom_check = true;
>     470		} else {
>     471			if (WARN_ON_ONCE(layer_masks_child))
>     472				return -EACCES;
>     473			access_masked_dst_parent = access_request_dst_parent;
>     474			access_masked_src_parent = access_request_src_parent;
>     475			is_dom_check = false;
>     476		}
>     477	
>     478		walker_path = *path;
>     479		path_get(&walker_path);
>     480		/*
>     481		 * We need to walk through all the hierarchy to not miss any relevant
>     482		 * restriction.
>     483		 */
>     484		while (true) {
>     485			struct dentry *parent_dentry;
>     486			const struct landlock_rule *rule;
>     487	
>     488			/*
>     489			 * If at least all accesses allowed on the destination are
>     490			 * already allowed on the source, respectively if there is at
>     491			 * least as much as restrictions on the destination than on the
>     492			 * source, then we can safely refer files from the source to
>     493			 * the destination without risking a privilege escalation.
>     494			 * This is crucial for standalone multilayered security
>     495			 * policies.  Furthermore, this helps avoid policy writers to
>     496			 * shoot themselves in the foot.
>     497			 */
>     498			if (is_dom_check && is_superset(child_is_directory,
>     499						layer_masks_dst_parent,
>     500						layer_masks_src_parent,
>     501						layer_masks_child)) {
>     502				allowed_dst_parent =
>     503					scope_to_request(access_request_dst_parent,
>     504							layer_masks_dst_parent);
>     505				allowed_src_parent =
>     506					scope_to_request(access_request_src_parent,
>     507							layer_masks_src_parent);
>     508	
>     509				/* Stops when all accesses are granted. */
>     510				if (allowed_dst_parent && allowed_src_parent)
>     511					break;
>     512	
>     513				/*
>     514				 * Downgrades checks from domain handled accesses to
>     515				 * requested accesses.
>     516				 */
>     517				is_dom_check = false;
>     518				access_masked_dst_parent = access_request_dst_parent;
>     519				access_masked_src_parent = access_request_src_parent;
>     520			}
>     521	
>     522			rule = find_rule(domain, walker_path.dentry);
>     523			allowed_dst_parent = unmask_layers(rule, access_masked_dst_parent,
>     524					layer_masks_dst_parent);
>     525			allowed_src_parent = unmask_layers(rule, access_masked_src_parent,
>     526					layer_masks_src_parent);
>     527	
>     528			/* Stops when a rule from each layer grants access. */
>     529			if (allowed_dst_parent && allowed_src_parent)
>     530				break;
>     531	
>     532	jump_up:
>     533			if (walker_path.dentry == walker_path.mnt->mnt_root) {
>     534				if (follow_up(&walker_path)) {
>     535					/* Ignores hidden mount points. */
>     536					goto jump_up;
>     537				} else {
>     538					/*
>     539					 * Stops at the real root.  Denies access
>     540					 * because not all layers have granted access.
>     541					 */
>     542					allowed_dst_parent = false;
>     543					break;
>     544				}
>     545			}
>     546			if (unlikely(IS_ROOT(walker_path.dentry))) {
>     547				/*
>     548				 * Stops at disconnected root directories.  Only allows
>     549				 * access to internal filesystems (e.g. nsfs, which is
>     550				 * reachable through /proc/<pid>/ns/<namespace>).
>     551				 */
>     552				allowed_dst_parent = !!(walker_path.mnt->mnt_flags &
>     553						MNT_INTERNAL);
>     554				break;
>     555			}
>     556			parent_dentry = dget_parent(walker_path.dentry);
>     557			dput(walker_path.dentry);
>     558			walker_path.dentry = parent_dentry;
>     559		}
>     560		path_put(&walker_path);
>     561	
>     562		if (allowed_dst_parent && allowed_src_parent)
>     563			return 0;
>     564	
>     565		/*
>     566		 * Unfortunately, we cannot prioritize EACCES over EXDEV for all
>     567		 * RENAME_EXCHANGE cases because it depends on the source and
>     568		 * destination order.  This could be changed with a new
>     569		 * security_path_rename hook implementation.
>     570		 */
>     571		if (likely(is_eacces(layer_masks_dst_parent, access_request_dst_parent)
>     572					|| is_eacces(layer_masks_src_parent,
>     573						access_request_src_parent)))
>     574			return -EACCES;
>     575	
>     576		/*
>     577		 * Gracefully forbids reparenting if the destination directory
>     578		 * hierarchy is not a superset of restrictions of the source directory
>     579		 * hierarchy, or if LANDLOCK_ACCESS_FS_REFER is not allowed by the
>     580		 * source or the destination.
>     581		 */
>     582		return -EXDEV;
>     583	}
>     584	
> 
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org



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