Re: [PATCH v3 07/12] landlock: Enforce namespace use restrictions

From: Tingmao Wang

Date: Sun Aug 09 2026 - 12:03:28 EST


On 7/26/26 17:13, Mickaël Salaün wrote:
> Add Landlock enforcement for namespace use via the LSM namespace_init
> and namespace_install hooks. This lets a sandboxed process restrict
> which namespace types it can acquire, using LANDLOCK_PERM_NAMESPACE_USE
> and per-type rules.
>
> Introduce the handled_perm field in struct landlock_ruleset_attr for
> per-category permissions: each permission gates all uses of a
> kernel-defined category (CLONE_NEW* for namespace types, CAP_* for
> capabilities) and provides complete deny-by-default coverage of category
> members. Rule values reference constants from other kernel subsystems
> (CLONE_NEW* for namespaces); unknown values are silently accepted
> because the allow-list denies them by default. See the "Ruleset
> restriction models" section in the kernel documentation for the full
> design rationale.
>
> The new permissions extend the UAPI, so this bumps the Landlock ABI
> version to 11.
>
> Both hooks share check_ns_type(): if the namespace's CLONE_NEW* type is
> not in the layer's allowed set, the operation is denied. No domain
> ancestry bypass, no namespace creator tracking, just a flat per-layer
> allowed-types bitmask.
>
> - hook_namespace_init() fires during unshare(CLONE_NEW*) and
> clone(CLONE_NEW*) via __ns_common_init().
>
> - hook_namespace_install() fires during setns() via validate_ns().
>
> Both record namespace_type and ns_id in the audit data; ns_id is zero at
> namespace creation.
>
> struct perm_masks is __packed __aligned(sizeof(u64)) because on m68k GCC
> packs bitfields at byte granularity, so without it a u64 bitfield struct
> can be smaller than sizeof(u64).

(The reason for forcing u64 is not obvious just from this patch alone, but
it makes sense after adding 40 capability bits in the patch following)

>
> The rule's perm selector must be LANDLOCK_PERM_NAMESPACE_USE and
> reserves room for future per-rule-type sub-permissions.
> allowed_namespace_types lists the allowed CLONE_NEW* flags; unknown bits
> are accepted for forward compatibility and have no effect since the
> allow-list denies by default.
>
> The rule also carries a quiet_namespace_types bitmask that silences the
> audit records of specific denied members without granting them. A
> sandbox that knowingly runs a caller probing a namespace type or
> capability it will never be granted (e.g. an old runtime kept for
> compatibility) would otherwise flood the audit log and drown the
> surprising denials that matter. Quiet is per-member rather than a
> coarse per-category ruleset bit so a sandbox can silence one member
> (CLONE_NEWNET) while still auditing another (CLONE_NEWUTS). Making
> quiet the complement of the allowed set would be broad, could not audit
> a member that is neither allowed nor explicitly quieted, and would
> auto-hide members added by future kernels. For the same reason a
> sandbox should quiet only specific members known to be requested but
> expected to be denied, never a blanket set, which follows the running
> kernel's known members and hides surprising or future denials. A bit
> set in both allowed_namespace_types and quiet_namespace_types has no
> effect, since an allowed member is never denied and its layer can never
> become the youngest denying layer for that member.

I agree this uAPI makes more sense, having a quiet_perm is a bit wasteful
given that the quiet configuration for perms can be determined solely via
the struct landlock_namespace_attr.

>
> The merged filesystem and network quiet feature marks unbounded rb-tree
> objects (paths, ports) quiet through the LANDLOCK_ADD_RULE_QUIET flag
> and the ruleset quiet_access_* masks. Capabilities and namespace types
> are instead a small, bounded, kernel-defined member set, so their quiet
> list is a per-rule bitmask sibling to the allowed bitmask, and the
> shared flag is rejected for these rule types. A single add_rule call
> can allow some members and quiet others independently, and a quiet-only
> rule (empty allowed set) is valid. Only the youngest denying layer's
> quiet mask decides, so a parent cannot silence a denial made by a deeper
> layer.
>
> [...]
> @@ -268,6 +281,40 @@ struct landlock_net_port_attr {
> __u64 port;
> };
>
> +/**
> + * struct landlock_namespace_attr - Namespace type definition
> + *
> + * Argument of sys_landlock_add_rule() with %LANDLOCK_RULE_NAMESPACE.
> + */
> +struct landlock_namespace_attr {
> + /**
> + * @perm: Must be set to %LANDLOCK_PERM_NAMESPACE_USE.
> + */
> + __u64 perm;
> + /**
> + * @allowed_namespace_types: Bitmask of namespace types (``CLONE_NEW*``
> + * flags) to allow under this rule. Unknown bits are silently ignored
> + * for forward compatibility.
> + */
> + __u64 allowed_namespace_types;
> + /**
> + * @quiet_namespace_types: Bitmask of namespace types (``CLONE_NEW*``
> + * flags) whose denial by this layer should not be logged, even if
> + * logging would normally take place per landlock_restrict_self() flags.
> + * Only denials attributed to this layer are suppressed (see `permission
> + * flags`_). Bits also set in @allowed_namespace_types have no effect,
> + * since an allowed type is never denied. Unknown bits are silently
> + * ignored.
> + *
> + * At least one of @allowed_namespace_types or @quiet_namespace_types
> + * must be non-zero, otherwise the call returns ``-ENOMSG``. The

Worth noting here that if a layer does not want to allow nor quiet any
bits it should just not do the landlock_add_rule.

> + * non-zero check runs on the raw input before unknown-bit masking, so a
> + * rule that sets only bits unknown to the running kernel succeeds but
> + * has no runtime effect.
> + */
> + __u64 quiet_namespace_types;
> +};
> +
> /**
> * DOC: fs_access
> *
> [...]
> diff --git a/security/landlock/domain.h b/security/landlock/domain.h
> index 56cceed5f50c..c2b72795b0d6 100644
> --- a/security/landlock/domain.h
> +++ b/security/landlock/domain.h
> @@ -116,6 +116,12 @@ struct landlock_hierarchy {
> * logged) if the related object is marked as quiet.
> */
> struct access_masks quiet_access;

I'm not sure if this is worth adjusting or mentioning in a comment but
basically quiet_access.perm is unused.

> + /**
> + * @quiet_perm: Per-member quiet bitmasks for permission types
> + * (capabilities and namespace types). A member denied by this layer is
> + * not logged when its bit is set here.
> + */
> + struct perm_masks quiet_perm;
> #endif /* CONFIG_AUDIT */
> };
>
> [...]

Reviewed-by: Tingmao Wang <m@xxxxxxxxxx>