* Re: [PATCH v9 01/11] KEYS: trusted: Use get_random-fallback for TPM
From: Chris Fenner @ 2026-02-20 18:30 UTC (permalink / raw)
To: Mimi Zohar
Cc: Jarkko Sakkinen, linux-integrity, Jonathan McDowell, Eric Biggers,
James Bottomley, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list, Roberto Sassu
In-Reply-To: <06a08cbbe47111a1795e5dcd42fb8cc4be643a72.camel@linux.ibm.com>
My conclusion about TCG_TPM2_HMAC after [1] and [2] was that
TPM2_TCG_HMAC doesn't (or didn't at the time) actually solve the
threat model it claims to (active interposer adversaries), while
dramatically increasing the cost of many kernel TPM activities beyond
the amount that would have been required to just solve for
passive/bus-sniffer interposer adversaries. The added symmetric crypto
required to secure a TPM transaction is almost not noticeable; the big
performance problem is the re-bootstrapping of the session with ECDH
for every command.
My primary concern at that time was, essentially, that TCG_TPM2_HMAC
punts on checking that the key that was used to secure the session was
actually resident in a real TPM and not just an interposer adversary.
I wrote up my understanding at
https://www.dlp.rip/decorative-cryptography, for anyone who wants a
long-form opinionated take :).
Unless I'm wrong, or TCG_TPM2_HMAC has changed dramatically since
August, I don't think "TPM2_TCG_HMAC makes this too costly" is a
compelling reason to make a security decision. (There could be other
reasons to make choices about whether to use the TPM as a source of
randomness in the kernel! This just isn't one IMHO.)
The version of TCG_TPM2_HMAC that I'd like to see someday would be one
that fully admits that its threat model is only passive interposers,
and sets up one session upon startup and ContextSaves/ContextLoads it
back into the TPM as needed in order to secure parameter encryption
for e.g., GetRandom() and Unseal() calls.
[1]: https://lore.kernel.org/linux-integrity/CAMigqh2nwuRRxaLyOJ+QaTJ+XGmkQj=rMj5K9GP1bCcXp2OsBQ@mail.gmail.com/
[2]: https://lore.kernel.org/linux-integrity/20250825203223.629515-1-jarkko@kernel.org/
Thanks
Chris
On Fri, Feb 20, 2026 at 10:04 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
>
> [Cc: Chris Fenner, Jonathan McDowell, Roberto]
>
> On Sun, 2026-01-25 at 21:25 +0200, Jarkko Sakkinen wrote:
> > 1. tpm2_get_random() is costly when TCG_TPM2_HMAC is enabled and thus its
> > use should be pooled rather than directly used. This both reduces
> > latency and improves its predictability.
>
> If the concern is the latency of encrypting the bus session, please remember
> that:
>
> - Not all environments expose the TPM bus to sniffing.
> - The current TPM trusted keys design is based on TPM RNG, but already allows it
> to be replaced with the kernel RNG via the "trusted_rng=kernel" boot command
> line option.
> - The proposed patch removes that possibility for no reason.
>
> Mimi & Elaine
>
>
^ permalink raw reply
* [PATCH v2 0/2] move TPM-specific fields into trusted_tpm_options
From: Srish Srinivasan @ 2026-02-20 18:34 UTC (permalink / raw)
To: linux-integrity, keyrings
Cc: James.Bottomley, jarkko, zohar, stefanb, nayna, linux-kernel,
linux-security-module, ssrish
The backend-agnostic trusted_key_options structure carries TPM-specific
fields. With the recent addition of a backend-private pointer, these fields
can be moved out of the generic options structure.
This patch series intends to reloacte all TPM-spcific fields into a newly
defined trusted_tpm_options structure. A pointer to the trusted_tpm_options
struct is stored in trusted_key_option's private.
Along with the migration of TPM-specific fields, this patch series includes
a preparatory clean-up patch: replacing pr_info() with pr_debug() and using
KERN_DEBUG for print_hex_dump() for logging debug information.
Testing covered both TPM 1.2 and TPM 2.0 backends (virtual environment),
including trusted key creation, revocation, unlinking, invalidation, and
loading keys from encrypted blobs. I would welcome any additional testing
from upstream to further strengthen the validation.
Changelog:
v2:
* Exclude the bug-fix patch as it has already been applied to 6.19-rc7
* Rename instances of trusted_tpm_options from tpm_opts to private
* Use pr_debug and KERN_DEBUG for logging debug messages (preparatory
clean up patch)
* Address comments from Jarkko
Srish Srinivasan (2):
keys/trusted_keys: clean up debug message logging in the tpm backend
keys/trusted_keys: move TPM-specific fields into trusted_tpm_options
include/keys/trusted-type.h | 11 --
include/keys/trusted_tpm.h | 14 +++
security/keys/trusted-keys/trusted_tpm1.c | 129 ++++++++++++----------
security/keys/trusted-keys/trusted_tpm2.c | 51 +++++----
4 files changed, 111 insertions(+), 94 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v2 1/2] keys/trusted_keys: clean up debug message logging in the tpm backend
From: Srish Srinivasan @ 2026-02-20 18:34 UTC (permalink / raw)
To: linux-integrity, keyrings
Cc: James.Bottomley, jarkko, zohar, stefanb, nayna, linux-kernel,
linux-security-module, ssrish
In-Reply-To: <20260220183426.80446-1-ssrish@linux.ibm.com>
The TPM trusted-keys backend uses a local TPM_DEBUG guard and pr_info()
for logging debug information.
Replace pr_info() with pr_debug(), and use KERN_DEBUG for print_hex_dump().
Remove TPM_DEBUG.
No functional change intended.
Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
security/keys/trusted-keys/trusted_tpm1.c | 40 +++++++----------------
1 file changed, 12 insertions(+), 28 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index c865c97aa1b4..216caef97ffc 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -46,28 +46,25 @@ enum {
SRK_keytype = 4
};
-#define TPM_DEBUG 0
-
-#if TPM_DEBUG
static inline void dump_options(struct trusted_key_options *o)
{
- pr_info("sealing key type %d\n", o->keytype);
- pr_info("sealing key handle %0X\n", o->keyhandle);
- pr_info("pcrlock %d\n", o->pcrlock);
- pr_info("pcrinfo %d\n", o->pcrinfo_len);
- print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
+ pr_debug("sealing key type %d\n", o->keytype);
+ pr_debug("sealing key handle %0X\n", o->keyhandle);
+ pr_debug("pcrlock %d\n", o->pcrlock);
+ pr_debug("pcrinfo %d\n", o->pcrinfo_len);
+ print_hex_dump(KERN_DEBUG, "pcrinfo ", DUMP_PREFIX_NONE,
16, 1, o->pcrinfo, o->pcrinfo_len, 0);
}
static inline void dump_sess(struct osapsess *s)
{
- print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE,
+ print_hex_dump(KERN_DEBUG, "trusted-key: handle ", DUMP_PREFIX_NONE,
16, 1, &s->handle, 4, 0);
- pr_info("secret:\n");
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ pr_debug("secret:\n");
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE,
16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
- pr_info("trusted-key: enonce:\n");
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ pr_debug("trusted-key: enonce:\n");
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE,
16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
}
@@ -75,23 +72,10 @@ static inline void dump_tpm_buf(unsigned char *buf)
{
int len;
- pr_info("\ntpm buffer\n");
+ pr_debug("\ntpm buffer\n");
len = LOAD32(buf, TPM_SIZE_OFFSET);
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
-}
-#else
-static inline void dump_options(struct trusted_key_options *o)
-{
-}
-
-static inline void dump_sess(struct osapsess *s)
-{
-}
-
-static inline void dump_tpm_buf(unsigned char *buf)
-{
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
}
-#endif
static int TSS_rawhmac(unsigned char *digest, const unsigned char *key,
unsigned int keylen, ...)
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/2] keys/trusted_keys: move TPM-specific fields into trusted_tpm_options
From: Srish Srinivasan @ 2026-02-20 18:34 UTC (permalink / raw)
To: linux-integrity, keyrings
Cc: James.Bottomley, jarkko, zohar, stefanb, nayna, linux-kernel,
linux-security-module, ssrish
In-Reply-To: <20260220183426.80446-1-ssrish@linux.ibm.com>
The trusted_key_options struct contains TPM-specific fields (keyhandle,
keyauth, blobauth_len, blobauth, pcrinfo_len, pcrinfo, pcrlock, hash,
policydigest_len, policydigest, and policyhandle). This leads to the
accumulation of backend-specific fields in the generic options structure.
Define trusted_tpm_options structure and move the TPM-specific fields
there. Store a pointer to trusted_tpm_options in trusted_key_options's
private.
No functional change intended.
Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
include/keys/trusted-type.h | 11 ---
include/keys/trusted_tpm.h | 14 ++++
security/keys/trusted-keys/trusted_tpm1.c | 95 ++++++++++++++---------
security/keys/trusted-keys/trusted_tpm2.c | 51 ++++++------
4 files changed, 102 insertions(+), 69 deletions(-)
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index 03527162613f..b80f250305b8 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -39,17 +39,6 @@ struct trusted_key_payload {
struct trusted_key_options {
uint16_t keytype;
- uint32_t keyhandle;
- unsigned char keyauth[TPM_DIGEST_SIZE];
- uint32_t blobauth_len;
- unsigned char blobauth[TPM_DIGEST_SIZE];
- uint32_t pcrinfo_len;
- unsigned char pcrinfo[MAX_PCRINFO_SIZE];
- int pcrlock;
- uint32_t hash;
- uint32_t policydigest_len;
- unsigned char policydigest[MAX_DIGEST_SIZE];
- uint32_t policyhandle;
void *private;
};
diff --git a/include/keys/trusted_tpm.h b/include/keys/trusted_tpm.h
index 0fadc6a4f166..355ebd36cbfd 100644
--- a/include/keys/trusted_tpm.h
+++ b/include/keys/trusted_tpm.h
@@ -7,6 +7,20 @@
extern struct trusted_key_ops trusted_key_tpm_ops;
+struct trusted_tpm_options {
+ uint32_t keyhandle;
+ unsigned char keyauth[TPM_DIGEST_SIZE];
+ uint32_t blobauth_len;
+ unsigned char blobauth[TPM_DIGEST_SIZE];
+ uint32_t pcrinfo_len;
+ unsigned char pcrinfo[MAX_PCRINFO_SIZE];
+ int pcrlock;
+ uint32_t hash;
+ uint32_t policydigest_len;
+ unsigned char policydigest[MAX_DIGEST_SIZE];
+ uint32_t policyhandle;
+};
+
int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 216caef97ffc..741b1d47d9f8 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -48,12 +48,14 @@ enum {
static inline void dump_options(struct trusted_key_options *o)
{
+ struct trusted_tpm_options *private = o->private;
+
pr_debug("sealing key type %d\n", o->keytype);
- pr_debug("sealing key handle %0X\n", o->keyhandle);
- pr_debug("pcrlock %d\n", o->pcrlock);
- pr_debug("pcrinfo %d\n", o->pcrinfo_len);
+ pr_debug("sealing key handle %0X\n", private->keyhandle);
+ pr_debug("pcrlock %d\n", private->pcrlock);
+ pr_debug("pcrinfo %d\n", private->pcrinfo_len);
print_hex_dump(KERN_DEBUG, "pcrinfo ", DUMP_PREFIX_NONE,
- 16, 1, o->pcrinfo, o->pcrinfo_len, 0);
+ 16, 1, private->pcrinfo, private->pcrinfo_len, 0);
}
static inline void dump_sess(struct osapsess *s)
@@ -609,6 +611,7 @@ static int tpm_unseal(struct tpm_buf *tb,
static int key_seal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
+ struct trusted_tpm_options *private = o->private;
struct tpm_buf tb;
int ret;
@@ -619,9 +622,10 @@ static int key_seal(struct trusted_key_payload *p,
/* include migratable flag at end of sealed key */
p->key[p->key_len] = p->migratable;
- ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
+ ret = tpm_seal(&tb, o->keytype, private->keyhandle, private->keyauth,
p->key, p->key_len + 1, p->blob, &p->blob_len,
- o->blobauth, o->pcrinfo, o->pcrinfo_len);
+ private->blobauth, private->pcrinfo,
+ private->pcrinfo_len);
if (ret < 0)
pr_info("srkseal failed (%d)\n", ret);
@@ -635,6 +639,7 @@ static int key_seal(struct trusted_key_payload *p,
static int key_unseal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
+ struct trusted_tpm_options *private = o->private;
struct tpm_buf tb;
int ret;
@@ -642,8 +647,8 @@ static int key_unseal(struct trusted_key_payload *p,
if (ret)
return ret;
- ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
- o->blobauth, p->key, &p->key_len);
+ ret = tpm_unseal(&tb, private->keyhandle, private->keyauth, p->blob,
+ p->blob_len, private->blobauth, p->key, &p->key_len);
if (ret < 0)
pr_info("srkunseal failed (%d)\n", ret);
else
@@ -680,6 +685,7 @@ static const match_table_t key_tokens = {
static int getoptions(char *c, struct trusted_key_payload *pay,
struct trusted_key_options *opt)
{
+ struct trusted_tpm_options *private = opt->private;
substring_t args[MAX_OPT_ARGS];
char *p = c;
int token;
@@ -695,7 +701,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
if (tpm2 < 0)
return tpm2;
- opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
+ private->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
if (!c)
return 0;
@@ -709,11 +715,11 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
switch (token) {
case Opt_pcrinfo:
- opt->pcrinfo_len = strlen(args[0].from) / 2;
- if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
+ private->pcrinfo_len = strlen(args[0].from) / 2;
+ if (private->pcrinfo_len > MAX_PCRINFO_SIZE)
return -EINVAL;
- res = hex2bin(opt->pcrinfo, args[0].from,
- opt->pcrinfo_len);
+ res = hex2bin(private->pcrinfo, args[0].from,
+ private->pcrinfo_len);
if (res < 0)
return -EINVAL;
break;
@@ -722,12 +728,12 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
if (res < 0)
return -EINVAL;
opt->keytype = SEAL_keytype;
- opt->keyhandle = handle;
+ private->keyhandle = handle;
break;
case Opt_keyauth:
if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
return -EINVAL;
- res = hex2bin(opt->keyauth, args[0].from,
+ res = hex2bin(private->keyauth, args[0].from,
SHA1_DIGEST_SIZE);
if (res < 0)
return -EINVAL;
@@ -738,21 +744,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
* hex strings. TPM 2.0 authorizations are simple
* passwords (although it can take a hash as well)
*/
- opt->blobauth_len = strlen(args[0].from);
+ private->blobauth_len = strlen(args[0].from);
- if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) {
- res = hex2bin(opt->blobauth, args[0].from,
+ if (private->blobauth_len == 2 * TPM_DIGEST_SIZE) {
+ res = hex2bin(private->blobauth, args[0].from,
TPM_DIGEST_SIZE);
if (res < 0)
return -EINVAL;
- opt->blobauth_len = TPM_DIGEST_SIZE;
+ private->blobauth_len = TPM_DIGEST_SIZE;
break;
}
- if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
- memcpy(opt->blobauth, args[0].from,
- opt->blobauth_len);
+ if (tpm2 &&
+ private->blobauth_len <=
+ sizeof(private->blobauth)) {
+ memcpy(private->blobauth, args[0].from,
+ private->blobauth_len);
break;
}
@@ -770,14 +778,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
res = kstrtoul(args[0].from, 10, &lock);
if (res < 0)
return -EINVAL;
- opt->pcrlock = lock;
+ private->pcrlock = lock;
break;
case Opt_hash:
if (test_bit(Opt_policydigest, &token_mask))
return -EINVAL;
for (i = 0; i < HASH_ALGO__LAST; i++) {
if (!strcmp(args[0].from, hash_algo_name[i])) {
- opt->hash = i;
+ private->hash = i;
break;
}
}
@@ -789,14 +797,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
}
break;
case Opt_policydigest:
- digest_len = hash_digest_size[opt->hash];
+ digest_len = hash_digest_size[private->hash];
if (!tpm2 || strlen(args[0].from) != (2 * digest_len))
return -EINVAL;
- res = hex2bin(opt->policydigest, args[0].from,
+ res = hex2bin(private->policydigest, args[0].from,
digest_len);
if (res < 0)
return -EINVAL;
- opt->policydigest_len = digest_len;
+ private->policydigest_len = digest_len;
break;
case Opt_policyhandle:
if (!tpm2)
@@ -804,7 +812,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
res = kstrtoul(args[0].from, 16, &handle);
if (res < 0)
return -EINVAL;
- opt->policyhandle = handle;
+ private->policyhandle = handle;
break;
default:
return -EINVAL;
@@ -815,6 +823,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
static struct trusted_key_options *trusted_options_alloc(void)
{
+ struct trusted_tpm_options *private;
struct trusted_key_options *options;
int tpm2;
@@ -827,14 +836,23 @@ static struct trusted_key_options *trusted_options_alloc(void)
/* set any non-zero defaults */
options->keytype = SRK_keytype;
- if (!tpm2)
- options->keyhandle = SRKHANDLE;
+ private = kzalloc(sizeof(*private), GFP_KERNEL);
+ if (!private) {
+ kfree_sensitive(options);
+ options = NULL;
+ } else {
+ if (!tpm2)
+ private->keyhandle = SRKHANDLE;
+
+ options->private = private;
+ }
}
return options;
}
static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
{
+ struct trusted_tpm_options *private = NULL;
struct trusted_key_options *options = NULL;
int ret = 0;
int tpm2;
@@ -852,7 +870,8 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
goto out;
dump_options(options);
- if (!options->keyhandle && !tpm2) {
+ private = options->private;
+ if (!private->keyhandle && !tpm2) {
ret = -EINVAL;
goto out;
}
@@ -866,20 +885,22 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
goto out;
}
- if (options->pcrlock) {
- ret = pcrlock(options->pcrlock);
+ if (private->pcrlock) {
+ ret = pcrlock(private->pcrlock);
if (ret < 0) {
pr_info("pcrlock failed (%d)\n", ret);
goto out;
}
}
out:
+ kfree_sensitive(options->private);
kfree_sensitive(options);
return ret;
}
static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
{
+ struct trusted_tpm_options *private = NULL;
struct trusted_key_options *options = NULL;
int ret = 0;
int tpm2;
@@ -897,7 +918,8 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
goto out;
dump_options(options);
- if (!options->keyhandle && !tpm2) {
+ private = options->private;
+ if (!private->keyhandle && !tpm2) {
ret = -EINVAL;
goto out;
}
@@ -909,14 +931,15 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
if (ret < 0)
pr_info("key_unseal failed (%d)\n", ret);
- if (options->pcrlock) {
- ret = pcrlock(options->pcrlock);
+ if (private->pcrlock) {
+ ret = pcrlock(private->pcrlock);
if (ret < 0) {
pr_info("pcrlock failed (%d)\n", ret);
goto out;
}
}
out:
+ kfree_sensitive(options->private);
kfree_sensitive(options);
return ret;
}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 6340823f8b53..94e01249b921 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -24,6 +24,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
u8 *src, u32 len)
{
+ struct trusted_tpm_options *private = options->private;
const int SCRATCH_SIZE = PAGE_SIZE;
u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
u8 *work = scratch, *work1;
@@ -46,7 +47,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
work = asn1_encode_oid(work, end_work, tpm2key_oid,
asn1_oid_len(tpm2key_oid));
- if (options->blobauth_len == 0) {
+ if (private->blobauth_len == 0) {
unsigned char bool[3], *w = bool;
/* tag 0 is emptyAuth */
w = asn1_encode_boolean(w, w + sizeof(bool), true);
@@ -69,7 +70,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
goto err;
}
- work = asn1_encode_integer(work, end_work, options->keyhandle);
+ work = asn1_encode_integer(work, end_work, private->keyhandle);
work = asn1_encode_octet_string(work, end_work, pub, pub_len);
work = asn1_encode_octet_string(work, end_work, priv, priv_len);
@@ -102,6 +103,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
struct trusted_key_options *options,
u8 **buf)
{
+ struct trusted_tpm_options *private = options->private;
int ret;
struct tpm2_key_context ctx;
u8 *blob;
@@ -121,7 +123,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
return -ENOMEM;
*buf = blob;
- options->keyhandle = ctx.parent;
+ private->keyhandle = ctx.parent;
memcpy(blob, ctx.priv, ctx.priv_len);
blob += ctx.priv_len;
@@ -233,6 +235,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ struct trusted_tpm_options *private = options->private;
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
int blob_len = 0;
@@ -240,11 +243,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
u32 flags;
int rc;
- hash = tpm2_find_hash_alg(options->hash);
+ hash = tpm2_find_hash_alg(private->hash);
if (hash < 0)
return hash;
- if (!options->keyhandle)
+ if (!private->keyhandle)
return -EINVAL;
rc = tpm_try_get_ops(chip);
@@ -268,18 +271,19 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, private->keyhandle, NULL);
if (rc)
goto out;
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
- options->keyauth, TPM_DIGEST_SIZE);
+ private->keyauth, TPM_DIGEST_SIZE);
/* sensitive */
- tpm_buf_append_u16(&sized, options->blobauth_len);
+ tpm_buf_append_u16(&sized, private->blobauth_len);
- if (options->blobauth_len)
- tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
+ if (private->blobauth_len)
+ tpm_buf_append(&sized, private->blobauth,
+ private->blobauth_len);
tpm_buf_append_u16(&sized, payload->key_len);
tpm_buf_append(&sized, payload->key, payload->key_len);
@@ -292,14 +296,15 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
/* key properties */
flags = 0;
- flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
+ flags |= private->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
tpm_buf_append_u32(&sized, flags);
/* policy */
- tpm_buf_append_u16(&sized, options->policydigest_len);
- if (options->policydigest_len)
- tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
+ tpm_buf_append_u16(&sized, private->policydigest_len);
+ if (private->policydigest_len)
+ tpm_buf_append(&sized, private->policydigest,
+ private->policydigest_len);
/* public parameters */
tpm_buf_append_u16(&sized, TPM_ALG_NULL);
@@ -373,6 +378,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
+ struct trusted_tpm_options *private = options->private;
struct tpm_buf buf;
unsigned int private_len;
unsigned int public_len;
@@ -392,7 +398,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
}
/* new format carries keyhandle but old format doesn't */
- if (!options->keyhandle)
+ if (!private->keyhandle)
return -EINVAL;
/* must be big enough for at least the two be16 size counts */
@@ -433,11 +439,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, private->keyhandle, NULL);
if (rc)
goto out;
- tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
+ tpm_buf_append_hmac_session(chip, &buf, 0, private->keyauth,
TPM_DIGEST_SIZE);
tpm_buf_append(&buf, blob, blob_len);
@@ -481,6 +487,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 blob_handle)
{
+ struct trusted_tpm_options *private = options->private;
struct tpm_header *head;
struct tpm_buf buf;
u16 data_len;
@@ -502,10 +509,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
if (rc)
goto out;
- if (!options->policyhandle) {
+ if (!private->policyhandle) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
- options->blobauth,
- options->blobauth_len);
+ private->blobauth,
+ private->blobauth_len);
} else {
/*
* FIXME: The policy session was generated outside the
@@ -518,9 +525,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
* could repeat our actions with the exfiltrated
* password.
*/
- tpm2_buf_append_auth(&buf, options->policyhandle,
+ tpm2_buf_append_auth(&buf, private->policyhandle,
NULL /* nonce */, 0, 0,
- options->blobauth, options->blobauth_len);
+ private->blobauth, private->blobauth_len);
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
} else {
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] samples/landlock: Bump ABI version to 8
From: Mickaël Salaün @ 2026-02-20 19:30 UTC (permalink / raw)
To: Günther Noack; +Cc: linux-security-module
In-Reply-To: <20260220160627.53913-1-gnoack3000@gmail.com>
Thanks, applied!
On Fri, Feb 20, 2026 at 05:06:27PM +0100, Günther Noack wrote:
> The sample tool should print a warning if it is not running on a
> kernel that provides the newest Landlock ABI version.
>
> Link: https://lore.kernel.org/all/20260218.ufao5Vaefa2u@digikod.net/
> Suggested-by: Mickaël Salaün <mic@digikod.net>
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> samples/landlock/sandboxer.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
> index e7af02f98208..9f21088c0855 100644
> --- a/samples/landlock/sandboxer.c
> +++ b/samples/landlock/sandboxer.c
> @@ -299,7 +299,7 @@ static bool check_ruleset_scope(const char *const env_var,
>
> /* clang-format on */
>
> -#define LANDLOCK_ABI_LAST 7
> +#define LANDLOCK_ABI_LAST 8
>
> #define XSTR(s) #s
> #define STR(s) XSTR(s)
> @@ -436,7 +436,8 @@ int main(const int argc, char *const argv[], char *const *const envp)
> /* Removes LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON for ABI < 7 */
> supported_restrict_flags &=
> ~LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
> -
> + __attribute__((fallthrough));
> + case 7:
> /* Must be printed for any ABI < LANDLOCK_ABI_LAST. */
> fprintf(stderr,
> "Hint: You should update the running kernel "
> --
> 2.52.0
>
>
^ permalink raw reply
* [PATCH] lsm: move inode IS_PRIVATE checks to individual LSMs
From: danieldurning.work @ 2026-02-20 19:54 UTC (permalink / raw)
To: linux-security-module, selinux, linux-integrity
Cc: stephen.smalley.work, paul, jmorris, serge, john.johansen, zohar,
roberto.sassu, dmitry.kasatkin, mic, casey, takedakn,
penguin-kernel
From: Daniel Durning <danieldurning.work@gmail.com>
Move responsibility of bypassing S_PRIVATE inodes to the
individual LSMs. Originally the LSM framework would skip calling
the hooks on any inode that was marked S_PRIVATE. This would
prevent the LSMs from controlling access to any inodes marked as
such (ie. pidfds). We now perform the same IS_PRIVATE checks
within the LSMs instead. This is consistent with the general goal
of deferring as much as possible to the individual LSMs.
This reorganization enables the LSMs to eventually implement
checks or labeling for some specific S_PRIVATE inodes like pidfds.
Signed-off-by: Daniel Durning <danieldurning.work@gmail.com>
---
security/apparmor/lsm.c | 32 ++++++++
security/commoncap.c | 11 ++-
security/integrity/evm/evm_main.c | 33 +++++++++
security/integrity/ima/ima_appraise.c | 12 +++
security/integrity/ima/ima_main.c | 6 ++
security/landlock/fs.c | 23 ++++++
security/security.c | 101 ++------------------------
security/selinux/hooks.c | 77 ++++++++++++++++++++
security/smack/smack_lsm.c | 56 ++++++++++++++
security/tomoyo/tomoyo.c | 35 +++++++++
10 files changed, 290 insertions(+), 96 deletions(-)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index a87cd60ed206..5b3ced11bdbc 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -323,18 +323,27 @@ static int common_perm_create(const char *op, const struct path *dir,
static int apparmor_path_unlink(const struct path *dir, struct dentry *dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE);
}
static int apparmor_path_mkdir(const struct path *dir, struct dentry *dentry,
umode_t mode)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE,
S_IFDIR);
}
static int apparmor_path_rmdir(const struct path *dir, struct dentry *dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE);
}
@@ -346,6 +355,9 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry,
static int apparmor_path_truncate(const struct path *path)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR);
}
@@ -357,6 +369,9 @@ static int apparmor_file_truncate(struct file *file)
static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
const char *old_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE,
S_IFLNK);
}
@@ -367,6 +382,9 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
struct aa_label *label;
int error = 0;
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
+ return 0;
+
if (!path_mediated_fs(old_dentry))
return 0;
@@ -386,6 +404,11 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
struct aa_label *label;
int error = 0;
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
+ (d_is_positive(new_dentry) &&
+ IS_PRIVATE(d_backing_inode(new_dentry)))))
+ return 0;
+
if (!path_mediated_fs(old_dentry))
return 0;
if ((flags & RENAME_EXCHANGE) && !path_mediated_fs(new_dentry))
@@ -444,16 +467,25 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
static int apparmor_path_chmod(const struct path *path, umode_t mode)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD);
}
static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
}
static int apparmor_inode_getattr(const struct path *path)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return common_perm_cond(OP_GETATTR, path, AA_MAY_GETATTR);
}
diff --git a/security/commoncap.c b/security/commoncap.c
index 8a23dfab7fac..1b61d43529c2 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -88,7 +88,7 @@ static inline int cap_capable_helper(const struct cred *cred,
if (ns->level <= cred_ns->level)
return -EPERM;
- /*
+ /*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
@@ -432,6 +432,9 @@ int cap_inode_getsecurity(struct mnt_idmap *idmap,
struct dentry *dentry;
struct user_namespace *fs_ns;
+ if (unlikely(IS_PRIVATE(inode)))
+ return -EOPNOTSUPP;
+
if (strcmp(name, "capability") != 0)
return -EOPNOTSUPP;
@@ -1027,6 +1030,9 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
{
struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/* Ignore non-security xattrs */
if (strncmp(name, XATTR_SECURITY_PREFIX,
XATTR_SECURITY_PREFIX_LEN) != 0)
@@ -1068,6 +1074,9 @@ int cap_inode_removexattr(struct mnt_idmap *idmap,
{
struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/* Ignore non-security xattrs */
if (strncmp(name, XATTR_SECURITY_PREFIX,
XATTR_SECURITY_PREFIX_LEN) != 0)
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 73d500a375cb..0095712b8d75 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -590,6 +590,9 @@ static int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
{
const struct evm_ima_xattr_data *xattr_data = xattr_value;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/* Policy permits modification of the protected xattrs even though
* there's no HMAC key loaded
*/
@@ -675,6 +678,9 @@ static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
{
enum integrity_status evm_status;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/* Policy permits modification of the protected xattrs even though
* there's no HMAC key loaded
*/
@@ -725,6 +731,9 @@ static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
}
@@ -807,6 +816,9 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
size_t xattr_value_len,
int flags)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
if (!evm_revalidate_status(xattr_name))
return;
@@ -836,6 +848,9 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
}
@@ -852,6 +867,9 @@ static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
static void evm_inode_post_removexattr(struct dentry *dentry,
const char *xattr_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
if (!evm_revalidate_status(xattr_name))
return;
@@ -879,6 +897,9 @@ static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry,
const char *acl_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
evm_inode_post_removexattr(dentry, acl_name);
}
@@ -911,6 +932,9 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
unsigned int ia_valid = attr->ia_valid;
enum integrity_status evm_status;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/* Policy permits modification of the protected attrs even though
* there's no HMAC key loaded
*/
@@ -960,6 +984,9 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
static void evm_inode_post_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
if (!evm_revalidate_status(NULL))
return;
@@ -1019,6 +1046,9 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
bool evm_protected_xattrs = false;
int rc;
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs)
return 0;
@@ -1094,6 +1124,9 @@ static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
struct inode *inode = d_backing_inode(dentry);
struct evm_iint_cache *iint = evm_iint_inode(inode);
+ if (unlikely(IS_PRIVATE(inode)))
+ return;
+
if (!S_ISREG(inode->i_mode))
return;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 5149ff4fd50d..d705c908132f 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -665,6 +665,9 @@ static void ima_inode_post_setattr(struct mnt_idmap *idmap,
struct ima_iint_cache *iint;
int action;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|| !(inode->i_opflags & IOP_XATTR))
return;
@@ -790,6 +793,9 @@ static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
int result;
int err;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1) {
@@ -817,6 +823,9 @@ static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name, struct posix_acl *kacl)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
if (evm_revalidate_status(acl_name))
ima_reset_appraise_flags(d_backing_inode(dentry), -1);
@@ -842,6 +851,9 @@ static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
const char *acl_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 5770cf691912..5ddac6c15c7f 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -794,6 +794,9 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
struct ima_iint_cache *iint;
int must_appraise;
+ if (unlikely(IS_PRIVATE(inode)))
+ return;
+
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return;
@@ -826,6 +829,9 @@ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
struct inode *inode = dentry->d_inode;
int must_appraise;
+ if (unlikely(IS_PRIVATE(inode)))
+ return;
+
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return;
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index fe794875ad46..5fe15313a3f5 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1528,6 +1528,9 @@ static int hook_path_link(struct dentry *const old_dentry,
const struct path *const new_dir,
struct dentry *const new_dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
+ return 0;
+
return current_check_refer_path(old_dentry, new_dir, new_dentry, false,
false);
}
@@ -1538,6 +1541,11 @@ static int hook_path_rename(const struct path *const old_dir,
struct dentry *const new_dentry,
const unsigned int flags)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
+ (d_is_positive(new_dentry) &&
+ IS_PRIVATE(d_backing_inode(new_dentry)))))
+ return 0;
+
/* old_dir refers to old_dentry->d_parent and new_dir->mnt */
return current_check_refer_path(old_dentry, new_dir, new_dentry, true,
!!(flags & RENAME_EXCHANGE));
@@ -1546,6 +1554,9 @@ static int hook_path_rename(const struct path *const old_dir,
static int hook_path_mkdir(const struct path *const dir,
struct dentry *const dentry, const umode_t mode)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR);
}
@@ -1560,23 +1571,35 @@ static int hook_path_symlink(const struct path *const dir,
struct dentry *const dentry,
const char *const old_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM);
}
static int hook_path_unlink(const struct path *const dir,
struct dentry *const dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE);
}
static int hook_path_rmdir(const struct path *const dir,
struct dentry *const dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
+ return 0;
+
return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR);
}
static int hook_path_truncate(const struct path *const path)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
}
diff --git a/security/security.c b/security/security.c
index 31a688650601..658dbb10ea40 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1310,9 +1310,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
struct xattr *new_xattrs = NULL;
int ret = -EOPNOTSUPP, xattr_count = 0;
- if (unlikely(IS_PRIVATE(inode)))
- return 0;
-
if (!blob_sizes.lbs_xattr_count)
return 0;
@@ -1386,8 +1383,6 @@ int security_inode_init_security_anon(struct inode *inode,
int security_path_mknod(const struct path *dir, struct dentry *dentry,
umode_t mode, unsigned int dev)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
- return 0;
return call_int_hook(path_mknod, dir, dentry, mode, dev);
}
EXPORT_SYMBOL(security_path_mknod);
@@ -1401,8 +1396,6 @@ EXPORT_SYMBOL(security_path_mknod);
*/
void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return;
call_void_hook(path_post_mknod, idmap, dentry);
}
@@ -1419,8 +1412,6 @@ void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
int security_path_mkdir(const struct path *dir, struct dentry *dentry,
umode_t mode)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
- return 0;
return call_int_hook(path_mkdir, dir, dentry, mode);
}
EXPORT_SYMBOL(security_path_mkdir);
@@ -1436,8 +1427,6 @@ EXPORT_SYMBOL(security_path_mkdir);
*/
int security_path_rmdir(const struct path *dir, struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
- return 0;
return call_int_hook(path_rmdir, dir, dentry);
}
@@ -1452,8 +1441,6 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
*/
int security_path_unlink(const struct path *dir, struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
- return 0;
return call_int_hook(path_unlink, dir, dentry);
}
EXPORT_SYMBOL(security_path_unlink);
@@ -1471,8 +1458,6 @@ EXPORT_SYMBOL(security_path_unlink);
int security_path_symlink(const struct path *dir, struct dentry *dentry,
const char *old_name)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
- return 0;
return call_int_hook(path_symlink, dir, dentry, old_name);
}
@@ -1489,8 +1474,6 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
struct dentry *new_dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
- return 0;
return call_int_hook(path_link, old_dentry, new_dir, new_dentry);
}
@@ -1510,11 +1493,6 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry,
unsigned int flags)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
- (d_is_positive(new_dentry) &&
- IS_PRIVATE(d_backing_inode(new_dentry)))))
- return 0;
-
return call_int_hook(path_rename, old_dir, old_dentry, new_dir,
new_dentry, flags);
}
@@ -1532,8 +1510,6 @@ EXPORT_SYMBOL(security_path_rename);
*/
int security_path_truncate(const struct path *path)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
- return 0;
return call_int_hook(path_truncate, path);
}
@@ -1550,8 +1526,6 @@ int security_path_truncate(const struct path *path)
*/
int security_path_chmod(const struct path *path, umode_t mode)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
- return 0;
return call_int_hook(path_chmod, path, mode);
}
@@ -1567,8 +1541,6 @@ int security_path_chmod(const struct path *path, umode_t mode)
*/
int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
- return 0;
return call_int_hook(path_chown, path, uid, gid);
}
@@ -1599,8 +1571,6 @@ int security_path_chroot(const struct path *path)
int security_inode_create(struct inode *dir, struct dentry *dentry,
umode_t mode)
{
- if (unlikely(IS_PRIVATE(dir)))
- return 0;
return call_int_hook(inode_create, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_create);
@@ -1615,8 +1585,6 @@ EXPORT_SYMBOL_GPL(security_inode_create);
void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
struct inode *inode)
{
- if (unlikely(IS_PRIVATE(inode)))
- return;
call_void_hook(inode_post_create_tmpfile, idmap, inode);
}
@@ -1633,8 +1601,6 @@ void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
- return 0;
return call_int_hook(inode_link, old_dentry, dir, new_dentry);
}
@@ -1649,8 +1615,6 @@ int security_inode_link(struct dentry *old_dentry, struct inode *dir,
*/
int security_inode_unlink(struct inode *dir, struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_unlink, dir, dentry);
}
@@ -1667,8 +1631,6 @@ int security_inode_unlink(struct inode *dir, struct dentry *dentry)
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
const char *old_name)
{
- if (unlikely(IS_PRIVATE(dir)))
- return 0;
return call_int_hook(inode_symlink, dir, dentry, old_name);
}
@@ -1685,8 +1647,6 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry,
*/
int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
- if (unlikely(IS_PRIVATE(dir)))
- return 0;
return call_int_hook(inode_mkdir, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_mkdir);
@@ -1702,8 +1662,6 @@ EXPORT_SYMBOL_GPL(security_inode_mkdir);
*/
int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_rmdir, dir, dentry);
}
@@ -1724,8 +1682,6 @@ int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
int security_inode_mknod(struct inode *dir, struct dentry *dentry,
umode_t mode, dev_t dev)
{
- if (unlikely(IS_PRIVATE(dir)))
- return 0;
return call_int_hook(inode_mknod, dir, dentry, mode, dev);
}
@@ -1745,11 +1701,6 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
- (d_is_positive(new_dentry) &&
- IS_PRIVATE(d_backing_inode(new_dentry)))))
- return 0;
-
if (flags & RENAME_EXCHANGE) {
int err = call_int_hook(inode_rename, new_dir, new_dentry,
old_dir, old_dentry);
@@ -1771,8 +1722,6 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
*/
int security_inode_readlink(struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_readlink, dentry);
}
@@ -1790,8 +1739,6 @@ int security_inode_readlink(struct dentry *dentry)
int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
bool rcu)
{
- if (unlikely(IS_PRIVATE(inode)))
- return 0;
return call_int_hook(inode_follow_link, dentry, inode, rcu);
}
@@ -1811,8 +1758,6 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
*/
int security_inode_permission(struct inode *inode, int mask)
{
- if (unlikely(IS_PRIVATE(inode)))
- return 0;
return call_int_hook(inode_permission, inode, mask);
}
@@ -1832,8 +1777,6 @@ int security_inode_permission(struct inode *inode, int mask)
int security_inode_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_setattr, idmap, dentry, attr);
}
EXPORT_SYMBOL_GPL(security_inode_setattr);
@@ -1849,8 +1792,6 @@ EXPORT_SYMBOL_GPL(security_inode_setattr);
void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
int ia_valid)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return;
call_void_hook(inode_post_setattr, idmap, dentry, ia_valid);
}
@@ -1864,8 +1805,6 @@ void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
*/
int security_inode_getattr(const struct path *path)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
- return 0;
return call_int_hook(inode_getattr, path);
}
@@ -1901,11 +1840,11 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
{
int rc;
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
-
/* enforce the capability checks at the lsm layer, if needed */
if (!call_int_hook(inode_xattr_skipcap, name)) {
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
rc = cap_inode_setxattr(dentry, name, value, size, flags);
if (rc)
return rc;
@@ -1931,8 +1870,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_set_acl, idmap, dentry, acl_name, kacl);
}
@@ -1948,8 +1885,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return;
call_void_hook(inode_post_set_acl, dentry, acl_name, kacl);
}
@@ -1967,8 +1902,6 @@ void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
int security_inode_get_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_get_acl, idmap, dentry, acl_name);
}
@@ -1986,8 +1919,6 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
int security_inode_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_remove_acl, idmap, dentry, acl_name);
}
@@ -2003,8 +1934,6 @@ int security_inode_remove_acl(struct mnt_idmap *idmap,
void security_inode_post_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return;
call_void_hook(inode_post_remove_acl, idmap, dentry, acl_name);
}
@@ -2021,8 +1950,6 @@ void security_inode_post_remove_acl(struct mnt_idmap *idmap,
void security_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return;
call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
}
@@ -2038,8 +1965,6 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
*/
int security_inode_getxattr(struct dentry *dentry, const char *name)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_getxattr, dentry, name);
}
@@ -2054,8 +1979,6 @@ int security_inode_getxattr(struct dentry *dentry, const char *name)
*/
int security_inode_listxattr(struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
return call_int_hook(inode_listxattr, dentry);
}
@@ -2087,11 +2010,11 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
{
int rc;
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return 0;
-
/* enforce the capability checks at the lsm layer, if needed */
if (!call_int_hook(inode_xattr_skipcap, name)) {
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
rc = cap_inode_removexattr(idmap, dentry, name);
if (rc)
return rc;
@@ -2109,8 +2032,6 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
*/
void security_inode_post_removexattr(struct dentry *dentry, const char *name)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
- return;
call_void_hook(inode_post_removexattr, dentry, name);
}
@@ -2197,9 +2118,6 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
struct inode *inode, const char *name,
void **buffer, bool alloc)
{
- if (unlikely(IS_PRIVATE(inode)))
- return LSM_RET_DEFAULT(inode_getsecurity);
-
return call_int_hook(inode_getsecurity, idmap, inode, name, buffer,
alloc);
}
@@ -2222,9 +2140,6 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
int security_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
- if (unlikely(IS_PRIVATE(inode)))
- return LSM_RET_DEFAULT(inode_setsecurity);
-
return call_int_hook(inode_setsecurity, inode, name, value, size,
flags);
}
@@ -2245,8 +2160,6 @@ int security_inode_setsecurity(struct inode *inode, const char *name,
int security_inode_listsecurity(struct inode *inode,
char *buffer, size_t buffer_size)
{
- if (unlikely(IS_PRIVATE(inode)))
- return 0;
return call_int_hook(inode_listsecurity, inode, buffer, buffer_size);
}
EXPORT_SYMBOL(security_inode_listsecurity);
@@ -3596,8 +3509,6 @@ int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops,
*/
void security_d_instantiate(struct dentry *dentry, struct inode *inode)
{
- if (unlikely(inode && IS_PRIVATE(inode)))
- return;
call_void_hook(d_instantiate, dentry, inode);
}
EXPORT_SYMBOL(security_d_instantiate);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index feda34b18d83..e17d776fb159 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2948,6 +2948,9 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
int rc;
char *context;
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
sbsec = selinux_superblock(dir->i_sb);
newsid = crsec->create_sid;
@@ -3049,42 +3052,68 @@ static int selinux_inode_init_security_anon(struct inode *inode,
static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+
return may_create(dir, dentry, SECCLASS_FILE);
}
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
+ return 0;
+
return may_link(dir, old_dentry, MAY_LINK);
}
static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return may_link(dir, dentry, MAY_UNLINK);
}
static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+
return may_create(dir, dentry, SECCLASS_LNK_FILE);
}
static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mask)
{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+
return may_create(dir, dentry, SECCLASS_DIR);
}
static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return may_link(dir, dentry, MAY_RMDIR);
}
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
struct inode *new_inode, struct dentry *new_dentry)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
+ (d_is_positive(new_dentry) &&
+ IS_PRIVATE(d_backing_inode(new_dentry)))))
+ return 0;
+
return may_rename(old_inode, old_dentry, new_inode, new_dentry);
}
@@ -3092,6 +3121,9 @@ static int selinux_inode_readlink(struct dentry *dentry)
{
const struct cred *cred = current_cred();
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return dentry_has_perm(cred, dentry, FILE__READ);
}
@@ -3102,6 +3134,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
struct inode_security_struct *isec;
u32 sid = current_sid();
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
isec = inode_security_rcu(inode, rcu);
@@ -3230,6 +3265,9 @@ static int selinux_inode_permission(struct inode *inode, int requested)
int rc, rc2;
u32 audited, denied;
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
mask = requested & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
/* No permission to check. Existence test. */
@@ -3283,6 +3321,9 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
unsigned int ia_valid = iattr->ia_valid;
u32 av = FILE__WRITE;
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
/* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
if (ia_valid & ATTR_FORCE) {
ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE |
@@ -3308,6 +3349,9 @@ static int selinux_inode_getattr(const struct path *path)
{
struct task_security_struct *tsec;
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
tsec = selinux_task(current);
if (task_avdcache_permnoaudit(tsec, current_sid()))
@@ -3356,6 +3400,9 @@ static int selinux_inode_setxattr(struct mnt_idmap *idmap,
u32 newsid, sid = current_sid();
int rc = 0;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/* if not a selinux xattr, only check the ordinary setattr perm */
if (strcmp(name, XATTR_NAME_SELINUX))
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
@@ -3435,18 +3482,27 @@ static int selinux_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
}
static int selinux_inode_get_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
}
static int selinux_inode_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
}
@@ -3459,6 +3515,9 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
u32 newsid;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
if (strcmp(name, XATTR_NAME_SELINUX)) {
/* Not an attribute we recognize, so nothing to do. */
return;
@@ -3494,6 +3553,9 @@ static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
{
const struct cred *cred = current_cred();
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return dentry_has_perm(cred, dentry, FILE__GETATTR);
}
@@ -3501,6 +3563,9 @@ static int selinux_inode_listxattr(struct dentry *dentry)
{
const struct cred *cred = current_cred();
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
return dentry_has_perm(cred, dentry, FILE__GETATTR);
}
@@ -3593,6 +3658,9 @@ static int selinux_inode_getsecurity(struct mnt_idmap *idmap,
char *context = NULL;
struct inode_security_struct *isec;
+ if (unlikely(IS_PRIVATE(inode)))
+ return -EOPNOTSUPP;
+
/*
* If we're not initialized yet, then we can't validate contexts, so
* just let vfs_getxattr fall back to using the on-disk xattr.
@@ -3637,6 +3705,9 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
u32 newsid;
int rc;
+ if (unlikely(IS_PRIVATE(inode)))
+ return -EOPNOTSUPP;
+
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
@@ -3664,6 +3735,9 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
{
const int len = sizeof(XATTR_NAME_SELINUX);
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
if (!selinux_initialized())
return 0;
@@ -6546,6 +6620,9 @@ static void selinux_ipc_getlsmprop(struct kern_ipc_perm *ipcp,
static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
{
+ if (unlikely(inode && IS_PRIVATE(inode)))
+ return;
+
if (inode)
inode_doinit_with_dentry(inode, dentry);
}
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index a0bd4919a9d9..8f432348dfbd 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1020,6 +1020,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
bool trans_cred;
bool trans_rule;
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
/*
* UNIX domain sockets use lower level socket data. Let
* UDS inode have fixed * label to keep smack_inode_permission() calm
@@ -1093,6 +1096,9 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
@@ -1124,6 +1130,9 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
@@ -1157,6 +1166,9 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
@@ -1199,6 +1211,11 @@ static int smack_inode_rename(struct inode *old_inode,
struct smack_known *isp;
struct smk_audit_info ad;
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
+ (d_is_positive(new_dentry) &&
+ IS_PRIVATE(d_backing_inode(new_dentry)))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
@@ -1231,6 +1248,9 @@ static int smack_inode_permission(struct inode *inode, int mask)
int no_block = mask & MAY_NOT_BLOCK;
int rc;
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
/*
* No permission to check. Existence test. Yup, it's there.
@@ -1267,6 +1287,9 @@ static int smack_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/*
* Need to allow for clearing the setuid bit.
*/
@@ -1292,6 +1315,9 @@ static int smack_inode_getattr(const struct path *path)
struct inode *inode = d_backing_inode(path->dentry);
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, *path);
rc = smk_curacc(smk_of_inode(inode), MAY_READ, &ad);
@@ -1351,6 +1377,9 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
int rc = 0;
umode_t const i_mode = d_backing_inode(dentry)->i_mode;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
/*
* Check label validity here so import won't fail in post_setxattr
*/
@@ -1421,6 +1450,9 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
struct smack_known *skp;
struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+
if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
isp->smk_flags |= SMK_INODE_TRANSMUTE;
return;
@@ -1455,6 +1487,9 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
@@ -1541,6 +1576,9 @@ static int smack_inode_set_acl(struct mnt_idmap *idmap,
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
@@ -1563,6 +1601,9 @@ static int smack_inode_get_acl(struct mnt_idmap *idmap,
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
@@ -1585,6 +1626,9 @@ static int smack_inode_remove_acl(struct mnt_idmap *idmap,
struct smk_audit_info ad;
int rc;
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
@@ -1616,6 +1660,9 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
size_t label_len;
char *label = NULL;
+ if (unlikely(IS_PRIVATE(inode)))
+ return -EOPNOTSUPP;
+
if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
isp = smk_of_inode(inode);
} else if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
@@ -1672,6 +1719,9 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer,
{
int len = sizeof(XATTR_NAME_SMACK);
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
if (buffer != NULL && len <= buffer_size)
memcpy(buffer, XATTR_NAME_SMACK, len);
@@ -2918,6 +2968,9 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
struct socket *sock;
int rc = 0;
+ if (unlikely(IS_PRIVATE(inode)))
+ return -EOPNOTSUPP;
+
if (value == NULL || size > SMK_LONGLABEL || size == 0)
return -EINVAL;
@@ -3517,6 +3570,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
if (inode == NULL)
return;
+ if (unlikely(IS_PRIVATE(inode)))
+ return;
+
isp = smack_inode(inode);
/*
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index c66e02ed8ee3..98eb8cd67f78 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -120,6 +120,9 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
*/
static int tomoyo_inode_getattr(const struct path *path)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, path, NULL);
}
@@ -132,6 +135,9 @@ static int tomoyo_inode_getattr(const struct path *path)
*/
static int tomoyo_path_truncate(const struct path *path)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
}
@@ -159,6 +165,9 @@ static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry)
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+ if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
+ return 0;
+
return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
}
@@ -176,6 +185,9 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry,
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+ if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
+ return 0;
+
return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
mode & S_IALLUGO);
}
@@ -192,6 +204,9 @@ static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry)
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+ if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
+ return 0;
+
return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
}
@@ -209,6 +224,9 @@ static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry,
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+ if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
+ return 0;
+
return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
}
@@ -229,6 +247,9 @@ static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry,
int type = TOMOYO_TYPE_CREATE;
const unsigned int perm = mode & S_IALLUGO;
+ if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
+ return 0;
+
switch (mode & S_IFMT) {
case S_IFCHR:
type = TOMOYO_TYPE_MKCHAR;
@@ -267,6 +288,9 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di
struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry };
struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry };
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
+ return 0;
+
return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
}
@@ -290,6 +314,11 @@ static int tomoyo_path_rename(const struct path *old_parent,
struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
+ if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
+ (d_is_positive(new_dentry) &&
+ IS_PRIVATE(d_backing_inode(new_dentry)))))
+ return 0;
+
if (flags & RENAME_EXCHANGE) {
const int err = tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path2,
&path1);
@@ -360,6 +389,9 @@ static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
*/
static int tomoyo_path_chmod(const struct path *path, umode_t mode)
{
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, path,
mode & S_IALLUGO);
}
@@ -377,6 +409,9 @@ static int tomoyo_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{
int error = 0;
+ if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
+ return 0;
+
if (uid_valid(uid))
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path,
from_kuid(&init_user_ns, uid));
--
2.52.0
^ permalink raw reply related
* Re: [RFC PATCH] fs/pidfs: Add permission check to pidfd_info()
From: Daniel Durning @ 2026-02-20 20:45 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel, linux-security-module, selinux, viro, jack, paul,
stephen.smalley.work, omosnace, Oleg Nesterov
In-Reply-To: <20260217-abgedankt-eilte-2386c98b3ef7@brauner>
On Tue, Feb 17, 2026 at 7:01 AM Christian Brauner <brauner@kernel.org> wrote:
>
> On Wed, Feb 11, 2026 at 02:43:21PM -0500, Daniel Durning wrote:
> > On Mon, Feb 9, 2026 at 9:01 AM Christian Brauner <brauner@kernel.org> wrote:
> > >
> > > On Fri, Feb 06, 2026 at 06:02:48PM +0000, danieldurning.work@gmail.com wrote:
> > > > From: Daniel Durning <danieldurning.work@gmail.com>
> > > >
> > > > Added a permission check to pidfd_info(). Originally, process info
> > > > could be retrieved with a pidfd even if proc was mounted with hidepid
> > > > enabled, allowing pidfds to be used to bypass those protections. We
> > > > now call ptrace_may_access() to perform some DAC checking as well
> > > > as call the appropriate LSM hook.
> > > >
> > > > The downside to this approach is that there are now more restrictions
> > > > on accessing this info from a pidfd than when just using proc (without
> > > > hidepid). I am open to suggestions if anyone can think of a better way
> > > > to handle this.
> > >
> > > This isn't really workable since this would regress userspace quite a
> > > bit. I think we need a different approach. I've given it some thought
> > > and everything's kinda ugly but this might work.
> > >
> > > In struct pid_namespace record whether anyone ever mounted a procfs
> > > with hidepid turned on for this pidns. In pidfd_info() we check whether
> > > hidepid was ever turned on. If it wasn't we're done and can just return
> > > the info. This will be the common case. If hidepid was ever turned on
> > > use kern_path("/proc") to lookup procfs. If not found check
> > > ptrace_may_access() to decide whether to return the info or not. If
> > > /proc is found check it's hidepid settings and make a decision based on
> > > that.
> > >
> > > You can probably reorder this to call ptrace_may_access() first and then
> > > do the procfs lookup dance. Thoughts?
> >
> > Thanks for the feedback. I think your solution makes sense.
> >
> > Unfortunately, it seems like systemd mounts procfs with hidepid enabled on
> > boot for services with the ProtectProc option enabled. This means that
> > procfs will always have been mounted with hidepid in the init pid namespace.
> > Do you think it would be viable to record whether or not procfs was mounted
> > with hidepid enabled in the mount namespace instead?
>
> I guess we can see what it looks like.
Having looked into this some more I am not sure if the mount
namespace is viable either since a single proc instance could be in
multiple mount namespaces. In addition the mount namespace
does not seem to be easily accessible in the function where proc
mount options are applied. I also considered adding an option
similar to hidepid to pidfs, but since pidfs is not userspace-mounted
I do not think that is possible without some significant changes.
Doing a proc lookup with kern_path() does work, but it does not seem
practical in terms of performance unless we had some other way to
skip it in the common case.
Curious if anyone else has any ideas or suggestions on how this
could be implemented.
^ permalink raw reply
* Re: [PATCH] lsm: move inode IS_PRIVATE checks to individual LSMs
From: Casey Schaufler @ 2026-02-20 21:13 UTC (permalink / raw)
To: danieldurning.work, linux-security-module, selinux,
linux-integrity
Cc: stephen.smalley.work, paul, jmorris, serge, john.johansen, zohar,
roberto.sassu, dmitry.kasatkin, mic, takedakn, penguin-kernel,
Casey Schaufler
In-Reply-To: <20260220195405.30612-1-danieldurning.work@gmail.com>
On 2/20/2026 11:54 AM, danieldurning.work@gmail.com wrote:
> From: Daniel Durning <danieldurning.work@gmail.com>
>
> Move responsibility of bypassing S_PRIVATE inodes to the
> individual LSMs. Originally the LSM framework would skip calling
> the hooks on any inode that was marked S_PRIVATE. This would
> prevent the LSMs from controlling access to any inodes marked as
> such (ie. pidfds). We now perform the same IS_PRIVATE checks
> within the LSMs instead. This is consistent with the general goal
> of deferring as much as possible to the individual LSMs.
Um ... ick?
Sure, we generally want the LSMs to be responsible for their own
decisions, but that doesn't look like the point to me. What appears
to be the issue is that pidfs isn't using S_PRIVATE in a way that
conveys the necessary information to the LSMs.
>
> This reorganization enables the LSMs to eventually implement
> checks or labeling for some specific S_PRIVATE inodes like pidfds.
We could consider these or similar changes when that eventuality occurs.
I would strongly suggest that this is a pidfs issue, not an LSM
infrastructure issue.
>
> Signed-off-by: Daniel Durning <danieldurning.work@gmail.com>
> ---
> security/apparmor/lsm.c | 32 ++++++++
> security/commoncap.c | 11 ++-
> security/integrity/evm/evm_main.c | 33 +++++++++
> security/integrity/ima/ima_appraise.c | 12 +++
> security/integrity/ima/ima_main.c | 6 ++
> security/landlock/fs.c | 23 ++++++
> security/security.c | 101 ++------------------------
> security/selinux/hooks.c | 77 ++++++++++++++++++++
> security/smack/smack_lsm.c | 56 ++++++++++++++
> security/tomoyo/tomoyo.c | 35 +++++++++
> 10 files changed, 290 insertions(+), 96 deletions(-)
>
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index a87cd60ed206..5b3ced11bdbc 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -323,18 +323,27 @@ static int common_perm_create(const char *op, const struct path *dir,
>
> static int apparmor_path_unlink(const struct path *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE);
> }
>
> static int apparmor_path_mkdir(const struct path *dir, struct dentry *dentry,
> umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE,
> S_IFDIR);
> }
>
> static int apparmor_path_rmdir(const struct path *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE);
> }
>
> @@ -346,6 +355,9 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry,
>
> static int apparmor_path_truncate(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR);
> }
>
> @@ -357,6 +369,9 @@ static int apparmor_file_truncate(struct file *file)
> static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
> const char *old_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE,
> S_IFLNK);
> }
> @@ -367,6 +382,9 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
> struct aa_label *label;
> int error = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> if (!path_mediated_fs(old_dentry))
> return 0;
>
> @@ -386,6 +404,11 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
> struct aa_label *label;
> int error = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> if (!path_mediated_fs(old_dentry))
> return 0;
> if ((flags & RENAME_EXCHANGE) && !path_mediated_fs(new_dentry))
> @@ -444,16 +467,25 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
>
> static int apparmor_path_chmod(const struct path *path, umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD);
> }
>
> static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
> }
>
> static int apparmor_inode_getattr(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return common_perm_cond(OP_GETATTR, path, AA_MAY_GETATTR);
> }
>
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 8a23dfab7fac..1b61d43529c2 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -88,7 +88,7 @@ static inline int cap_capable_helper(const struct cred *cred,
> if (ns->level <= cred_ns->level)
> return -EPERM;
>
> - /*
> + /*
> * The owner of the user namespace in the parent of the
> * user namespace has all caps.
> */
> @@ -432,6 +432,9 @@ int cap_inode_getsecurity(struct mnt_idmap *idmap,
> struct dentry *dentry;
> struct user_namespace *fs_ns;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (strcmp(name, "capability") != 0)
> return -EOPNOTSUPP;
>
> @@ -1027,6 +1030,9 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
> {
> struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Ignore non-security xattrs */
> if (strncmp(name, XATTR_SECURITY_PREFIX,
> XATTR_SECURITY_PREFIX_LEN) != 0)
> @@ -1068,6 +1074,9 @@ int cap_inode_removexattr(struct mnt_idmap *idmap,
> {
> struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Ignore non-security xattrs */
> if (strncmp(name, XATTR_SECURITY_PREFIX,
> XATTR_SECURITY_PREFIX_LEN) != 0)
> diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
> index 73d500a375cb..0095712b8d75 100644
> --- a/security/integrity/evm/evm_main.c
> +++ b/security/integrity/evm/evm_main.c
> @@ -590,6 +590,9 @@ static int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> {
> const struct evm_ima_xattr_data *xattr_data = xattr_value;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Policy permits modification of the protected xattrs even though
> * there's no HMAC key loaded
> */
> @@ -675,6 +678,9 @@ static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> {
> enum integrity_status evm_status;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Policy permits modification of the protected xattrs even though
> * there's no HMAC key loaded
> */
> @@ -725,6 +731,9 @@ static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
> }
>
> @@ -807,6 +816,9 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
> size_t xattr_value_len,
> int flags)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!evm_revalidate_status(xattr_name))
> return;
>
> @@ -836,6 +848,9 @@ static void evm_inode_post_setxattr(struct dentry *dentry,
> static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
> }
>
> @@ -852,6 +867,9 @@ static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> static void evm_inode_post_removexattr(struct dentry *dentry,
> const char *xattr_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!evm_revalidate_status(xattr_name))
> return;
>
> @@ -879,6 +897,9 @@ static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry,
> const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> evm_inode_post_removexattr(dentry, acl_name);
> }
>
> @@ -911,6 +932,9 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> unsigned int ia_valid = attr->ia_valid;
> enum integrity_status evm_status;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* Policy permits modification of the protected attrs even though
> * there's no HMAC key loaded
> */
> @@ -960,6 +984,9 @@ static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> static void evm_inode_post_setattr(struct mnt_idmap *idmap,
> struct dentry *dentry, int ia_valid)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!evm_revalidate_status(NULL))
> return;
>
> @@ -1019,6 +1046,9 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
> bool evm_protected_xattrs = false;
> int rc;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs)
> return 0;
>
> @@ -1094,6 +1124,9 @@ static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> struct inode *inode = d_backing_inode(dentry);
> struct evm_iint_cache *iint = evm_iint_inode(inode);
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> if (!S_ISREG(inode->i_mode))
> return;
>
> diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
> index 5149ff4fd50d..d705c908132f 100644
> --- a/security/integrity/ima/ima_appraise.c
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -665,6 +665,9 @@ static void ima_inode_post_setattr(struct mnt_idmap *idmap,
> struct ima_iint_cache *iint;
> int action;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
> || !(inode->i_opflags & IOP_XATTR))
> return;
> @@ -790,6 +793,9 @@ static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> int result;
> int err;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> result = ima_protect_xattr(dentry, xattr_name, xattr_value,
> xattr_value_len);
> if (result == 1) {
> @@ -817,6 +823,9 @@ static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
> static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> const char *acl_name, struct posix_acl *kacl)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> if (evm_revalidate_status(acl_name))
> ima_reset_appraise_flags(d_backing_inode(dentry), -1);
>
> @@ -842,6 +851,9 @@ static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
> static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
> }
>
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 5770cf691912..5ddac6c15c7f 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -794,6 +794,9 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
> struct ima_iint_cache *iint;
> int must_appraise;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> if (!ima_policy_flag || !S_ISREG(inode->i_mode))
> return;
>
> @@ -826,6 +829,9 @@ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> struct inode *inode = dentry->d_inode;
> int must_appraise;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> if (!ima_policy_flag || !S_ISREG(inode->i_mode))
> return;
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index fe794875ad46..5fe15313a3f5 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -1528,6 +1528,9 @@ static int hook_path_link(struct dentry *const old_dentry,
> const struct path *const new_dir,
> struct dentry *const new_dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> return current_check_refer_path(old_dentry, new_dir, new_dentry, false,
> false);
> }
> @@ -1538,6 +1541,11 @@ static int hook_path_rename(const struct path *const old_dir,
> struct dentry *const new_dentry,
> const unsigned int flags)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> /* old_dir refers to old_dentry->d_parent and new_dir->mnt */
> return current_check_refer_path(old_dentry, new_dir, new_dentry, true,
> !!(flags & RENAME_EXCHANGE));
> @@ -1546,6 +1554,9 @@ static int hook_path_rename(const struct path *const old_dir,
> static int hook_path_mkdir(const struct path *const dir,
> struct dentry *const dentry, const umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_DIR);
> }
>
> @@ -1560,23 +1571,35 @@ static int hook_path_symlink(const struct path *const dir,
> struct dentry *const dentry,
> const char *const old_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_MAKE_SYM);
> }
>
> static int hook_path_unlink(const struct path *const dir,
> struct dentry *const dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_FILE);
> }
>
> static int hook_path_rmdir(const struct path *const dir,
> struct dentry *const dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> + return 0;
> +
> return current_check_access_path(dir, LANDLOCK_ACCESS_FS_REMOVE_DIR);
> }
>
> static int hook_path_truncate(const struct path *const path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return current_check_access_path(path, LANDLOCK_ACCESS_FS_TRUNCATE);
> }
>
> diff --git a/security/security.c b/security/security.c
> index 31a688650601..658dbb10ea40 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1310,9 +1310,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
> struct xattr *new_xattrs = NULL;
> int ret = -EOPNOTSUPP, xattr_count = 0;
>
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> -
> if (!blob_sizes.lbs_xattr_count)
> return 0;
>
> @@ -1386,8 +1383,6 @@ int security_inode_init_security_anon(struct inode *inode,
> int security_path_mknod(const struct path *dir, struct dentry *dentry,
> umode_t mode, unsigned int dev)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_mknod, dir, dentry, mode, dev);
> }
> EXPORT_SYMBOL(security_path_mknod);
> @@ -1401,8 +1396,6 @@ EXPORT_SYMBOL(security_path_mknod);
> */
> void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(path_post_mknod, idmap, dentry);
> }
>
> @@ -1419,8 +1412,6 @@ void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
> int security_path_mkdir(const struct path *dir, struct dentry *dentry,
> umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_mkdir, dir, dentry, mode);
> }
> EXPORT_SYMBOL(security_path_mkdir);
> @@ -1436,8 +1427,6 @@ EXPORT_SYMBOL(security_path_mkdir);
> */
> int security_path_rmdir(const struct path *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_rmdir, dir, dentry);
> }
>
> @@ -1452,8 +1441,6 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
> */
> int security_path_unlink(const struct path *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_unlink, dir, dentry);
> }
> EXPORT_SYMBOL(security_path_unlink);
> @@ -1471,8 +1458,6 @@ EXPORT_SYMBOL(security_path_unlink);
> int security_path_symlink(const struct path *dir, struct dentry *dentry,
> const char *old_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
> - return 0;
> return call_int_hook(path_symlink, dir, dentry, old_name);
> }
>
> @@ -1489,8 +1474,6 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
> int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
> struct dentry *new_dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> - return 0;
> return call_int_hook(path_link, old_dentry, new_dir, new_dentry);
> }
>
> @@ -1510,11 +1493,6 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
> const struct path *new_dir, struct dentry *new_dentry,
> unsigned int flags)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> - (d_is_positive(new_dentry) &&
> - IS_PRIVATE(d_backing_inode(new_dentry)))))
> - return 0;
> -
> return call_int_hook(path_rename, old_dir, old_dentry, new_dir,
> new_dentry, flags);
> }
> @@ -1532,8 +1510,6 @@ EXPORT_SYMBOL(security_path_rename);
> */
> int security_path_truncate(const struct path *path)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(path_truncate, path);
> }
>
> @@ -1550,8 +1526,6 @@ int security_path_truncate(const struct path *path)
> */
> int security_path_chmod(const struct path *path, umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(path_chmod, path, mode);
> }
>
> @@ -1567,8 +1541,6 @@ int security_path_chmod(const struct path *path, umode_t mode)
> */
> int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(path_chown, path, uid, gid);
> }
>
> @@ -1599,8 +1571,6 @@ int security_path_chroot(const struct path *path)
> int security_inode_create(struct inode *dir, struct dentry *dentry,
> umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_create, dir, dentry, mode);
> }
> EXPORT_SYMBOL_GPL(security_inode_create);
> @@ -1615,8 +1585,6 @@ EXPORT_SYMBOL_GPL(security_inode_create);
> void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
> struct inode *inode)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return;
> call_void_hook(inode_post_create_tmpfile, idmap, inode);
> }
>
> @@ -1633,8 +1601,6 @@ void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
> int security_inode_link(struct dentry *old_dentry, struct inode *dir,
> struct dentry *new_dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> - return 0;
> return call_int_hook(inode_link, old_dentry, dir, new_dentry);
> }
>
> @@ -1649,8 +1615,6 @@ int security_inode_link(struct dentry *old_dentry, struct inode *dir,
> */
> int security_inode_unlink(struct inode *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_unlink, dir, dentry);
> }
>
> @@ -1667,8 +1631,6 @@ int security_inode_unlink(struct inode *dir, struct dentry *dentry)
> int security_inode_symlink(struct inode *dir, struct dentry *dentry,
> const char *old_name)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_symlink, dir, dentry, old_name);
> }
>
> @@ -1685,8 +1647,6 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry,
> */
> int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_mkdir, dir, dentry, mode);
> }
> EXPORT_SYMBOL_GPL(security_inode_mkdir);
> @@ -1702,8 +1662,6 @@ EXPORT_SYMBOL_GPL(security_inode_mkdir);
> */
> int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_rmdir, dir, dentry);
> }
>
> @@ -1724,8 +1682,6 @@ int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
> int security_inode_mknod(struct inode *dir, struct dentry *dentry,
> umode_t mode, dev_t dev)
> {
> - if (unlikely(IS_PRIVATE(dir)))
> - return 0;
> return call_int_hook(inode_mknod, dir, dentry, mode, dev);
> }
>
> @@ -1745,11 +1701,6 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
> struct inode *new_dir, struct dentry *new_dentry,
> unsigned int flags)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> - (d_is_positive(new_dentry) &&
> - IS_PRIVATE(d_backing_inode(new_dentry)))))
> - return 0;
> -
> if (flags & RENAME_EXCHANGE) {
> int err = call_int_hook(inode_rename, new_dir, new_dentry,
> old_dir, old_dentry);
> @@ -1771,8 +1722,6 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
> */
> int security_inode_readlink(struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_readlink, dentry);
> }
>
> @@ -1790,8 +1739,6 @@ int security_inode_readlink(struct dentry *dentry)
> int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
> bool rcu)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> return call_int_hook(inode_follow_link, dentry, inode, rcu);
> }
>
> @@ -1811,8 +1758,6 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
> */
> int security_inode_permission(struct inode *inode, int mask)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> return call_int_hook(inode_permission, inode, mask);
> }
>
> @@ -1832,8 +1777,6 @@ int security_inode_permission(struct inode *inode, int mask)
> int security_inode_setattr(struct mnt_idmap *idmap,
> struct dentry *dentry, struct iattr *attr)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_setattr, idmap, dentry, attr);
> }
> EXPORT_SYMBOL_GPL(security_inode_setattr);
> @@ -1849,8 +1792,6 @@ EXPORT_SYMBOL_GPL(security_inode_setattr);
> void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> int ia_valid)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_setattr, idmap, dentry, ia_valid);
> }
>
> @@ -1864,8 +1805,6 @@ void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> */
> int security_inode_getattr(const struct path *path)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> - return 0;
> return call_int_hook(inode_getattr, path);
> }
>
> @@ -1901,11 +1840,11 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
> {
> int rc;
>
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> -
> /* enforce the capability checks at the lsm layer, if needed */
> if (!call_int_hook(inode_xattr_skipcap, name)) {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> rc = cap_inode_setxattr(dentry, name, value, size, flags);
> if (rc)
> return rc;
> @@ -1931,8 +1870,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_set_acl, idmap, dentry, acl_name, kacl);
> }
>
> @@ -1948,8 +1885,6 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
> void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_set_acl, dentry, acl_name, kacl);
> }
>
> @@ -1967,8 +1902,6 @@ void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
> int security_inode_get_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_get_acl, idmap, dentry, acl_name);
> }
>
> @@ -1986,8 +1919,6 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
> int security_inode_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_remove_acl, idmap, dentry, acl_name);
> }
>
> @@ -2003,8 +1934,6 @@ int security_inode_remove_acl(struct mnt_idmap *idmap,
> void security_inode_post_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_remove_acl, idmap, dentry, acl_name);
> }
>
> @@ -2021,8 +1950,6 @@ void security_inode_post_remove_acl(struct mnt_idmap *idmap,
> void security_inode_post_setxattr(struct dentry *dentry, const char *name,
> const void *value, size_t size, int flags)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
> }
>
> @@ -2038,8 +1965,6 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
> */
> int security_inode_getxattr(struct dentry *dentry, const char *name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_getxattr, dentry, name);
> }
>
> @@ -2054,8 +1979,6 @@ int security_inode_getxattr(struct dentry *dentry, const char *name)
> */
> int security_inode_listxattr(struct dentry *dentry)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> return call_int_hook(inode_listxattr, dentry);
> }
>
> @@ -2087,11 +2010,11 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
> {
> int rc;
>
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return 0;
> -
> /* enforce the capability checks at the lsm layer, if needed */
> if (!call_int_hook(inode_xattr_skipcap, name)) {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> rc = cap_inode_removexattr(idmap, dentry, name);
> if (rc)
> return rc;
> @@ -2109,8 +2032,6 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
> */
> void security_inode_post_removexattr(struct dentry *dentry, const char *name)
> {
> - if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> - return;
> call_void_hook(inode_post_removexattr, dentry, name);
> }
>
> @@ -2197,9 +2118,6 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
> struct inode *inode, const char *name,
> void **buffer, bool alloc)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return LSM_RET_DEFAULT(inode_getsecurity);
> -
> return call_int_hook(inode_getsecurity, idmap, inode, name, buffer,
> alloc);
> }
> @@ -2222,9 +2140,6 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
> int security_inode_setsecurity(struct inode *inode, const char *name,
> const void *value, size_t size, int flags)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return LSM_RET_DEFAULT(inode_setsecurity);
> -
> return call_int_hook(inode_setsecurity, inode, name, value, size,
> flags);
> }
> @@ -2245,8 +2160,6 @@ int security_inode_setsecurity(struct inode *inode, const char *name,
> int security_inode_listsecurity(struct inode *inode,
> char *buffer, size_t buffer_size)
> {
> - if (unlikely(IS_PRIVATE(inode)))
> - return 0;
> return call_int_hook(inode_listsecurity, inode, buffer, buffer_size);
> }
> EXPORT_SYMBOL(security_inode_listsecurity);
> @@ -3596,8 +3509,6 @@ int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops,
> */
> void security_d_instantiate(struct dentry *dentry, struct inode *inode)
> {
> - if (unlikely(inode && IS_PRIVATE(inode)))
> - return;
> call_void_hook(d_instantiate, dentry, inode);
> }
> EXPORT_SYMBOL(security_d_instantiate);
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index feda34b18d83..e17d776fb159 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -2948,6 +2948,9 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
> int rc;
> char *context;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> sbsec = selinux_superblock(dir->i_sb);
>
> newsid = crsec->create_sid;
> @@ -3049,42 +3052,68 @@ static int selinux_inode_init_security_anon(struct inode *inode,
>
> static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, SECCLASS_FILE);
> }
>
> static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> return may_link(dir, old_dentry, MAY_LINK);
> }
>
> static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return may_link(dir, dentry, MAY_UNLINK);
> }
>
> static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, SECCLASS_LNK_FILE);
> }
>
> static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mask)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, SECCLASS_DIR);
> }
>
> static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return may_link(dir, dentry, MAY_RMDIR);
> }
>
> static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
> {
> + if (unlikely(IS_PRIVATE(dir)))
> + return 0;
> +
> return may_create(dir, dentry, inode_mode_to_security_class(mode));
> }
>
> static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
> struct inode *new_inode, struct dentry *new_dentry)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> return may_rename(old_inode, old_dentry, new_inode, new_dentry);
> }
>
> @@ -3092,6 +3121,9 @@ static int selinux_inode_readlink(struct dentry *dentry)
> {
> const struct cred *cred = current_cred();
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(cred, dentry, FILE__READ);
> }
>
> @@ -3102,6 +3134,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
> struct inode_security_struct *isec;
> u32 sid = current_sid();
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> ad.type = LSM_AUDIT_DATA_DENTRY;
> ad.u.dentry = dentry;
> isec = inode_security_rcu(inode, rcu);
> @@ -3230,6 +3265,9 @@ static int selinux_inode_permission(struct inode *inode, int requested)
> int rc, rc2;
> u32 audited, denied;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> mask = requested & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
>
> /* No permission to check. Existence test. */
> @@ -3283,6 +3321,9 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> unsigned int ia_valid = iattr->ia_valid;
> u32 av = FILE__WRITE;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> /* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
> if (ia_valid & ATTR_FORCE) {
> ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE |
> @@ -3308,6 +3349,9 @@ static int selinux_inode_getattr(const struct path *path)
> {
> struct task_security_struct *tsec;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> tsec = selinux_task(current);
>
> if (task_avdcache_permnoaudit(tsec, current_sid()))
> @@ -3356,6 +3400,9 @@ static int selinux_inode_setxattr(struct mnt_idmap *idmap,
> u32 newsid, sid = current_sid();
> int rc = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /* if not a selinux xattr, only check the ordinary setattr perm */
> if (strcmp(name, XATTR_NAME_SELINUX))
> return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
> @@ -3435,18 +3482,27 @@ static int selinux_inode_set_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name,
> struct posix_acl *kacl)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
> }
>
> static int selinux_inode_get_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
> }
>
> static int selinux_inode_remove_acl(struct mnt_idmap *idmap,
> struct dentry *dentry, const char *acl_name)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
> }
>
> @@ -3459,6 +3515,9 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
> u32 newsid;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (strcmp(name, XATTR_NAME_SELINUX)) {
> /* Not an attribute we recognize, so nothing to do. */
> return;
> @@ -3494,6 +3553,9 @@ static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
> {
> const struct cred *cred = current_cred();
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(cred, dentry, FILE__GETATTR);
> }
>
> @@ -3501,6 +3563,9 @@ static int selinux_inode_listxattr(struct dentry *dentry)
> {
> const struct cred *cred = current_cred();
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> return dentry_has_perm(cred, dentry, FILE__GETATTR);
> }
>
> @@ -3593,6 +3658,9 @@ static int selinux_inode_getsecurity(struct mnt_idmap *idmap,
> char *context = NULL;
> struct inode_security_struct *isec;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> /*
> * If we're not initialized yet, then we can't validate contexts, so
> * just let vfs_getxattr fall back to using the on-disk xattr.
> @@ -3637,6 +3705,9 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
> u32 newsid;
> int rc;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (strcmp(name, XATTR_SELINUX_SUFFIX))
> return -EOPNOTSUPP;
>
> @@ -3664,6 +3735,9 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
> {
> const int len = sizeof(XATTR_NAME_SELINUX);
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> if (!selinux_initialized())
> return 0;
>
> @@ -6546,6 +6620,9 @@ static void selinux_ipc_getlsmprop(struct kern_ipc_perm *ipcp,
>
> static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
> {
> + if (unlikely(inode && IS_PRIVATE(inode)))
> + return;
> +
> if (inode)
> inode_doinit_with_dentry(inode, dentry);
> }
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index a0bd4919a9d9..8f432348dfbd 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1020,6 +1020,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
> bool trans_cred;
> bool trans_rule;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> /*
> * UNIX domain sockets use lower level socket data. Let
> * UDS inode have fixed * label to keep smack_inode_permission() calm
> @@ -1093,6 +1096,9 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
>
> @@ -1124,6 +1130,9 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1157,6 +1166,9 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1199,6 +1211,11 @@ static int smack_inode_rename(struct inode *old_inode,
> struct smack_known *isp;
> struct smk_audit_info ad;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
>
> @@ -1231,6 +1248,9 @@ static int smack_inode_permission(struct inode *inode, int mask)
> int no_block = mask & MAY_NOT_BLOCK;
> int rc;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
> /*
> * No permission to check. Existence test. Yup, it's there.
> @@ -1267,6 +1287,9 @@ static int smack_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /*
> * Need to allow for clearing the setuid bit.
> */
> @@ -1292,6 +1315,9 @@ static int smack_inode_getattr(const struct path *path)
> struct inode *inode = d_backing_inode(path->dentry);
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
> smk_ad_setfield_u_fs_path(&ad, *path);
> rc = smk_curacc(smk_of_inode(inode), MAY_READ, &ad);
> @@ -1351,6 +1377,9 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
> int rc = 0;
> umode_t const i_mode = d_backing_inode(dentry)->i_mode;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> /*
> * Check label validity here so import won't fail in post_setxattr
> */
> @@ -1421,6 +1450,9 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
> struct smack_known *skp;
> struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return;
> +
> if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
> isp->smk_flags |= SMK_INODE_TRANSMUTE;
> return;
> @@ -1455,6 +1487,9 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1541,6 +1576,9 @@ static int smack_inode_set_acl(struct mnt_idmap *idmap,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1563,6 +1601,9 @@ static int smack_inode_get_acl(struct mnt_idmap *idmap,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1585,6 +1626,9 @@ static int smack_inode_remove_acl(struct mnt_idmap *idmap,
> struct smk_audit_info ad;
> int rc;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
> + return 0;
> +
> smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
> smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
>
> @@ -1616,6 +1660,9 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
> size_t label_len;
> char *label = NULL;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
> isp = smk_of_inode(inode);
> } else if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
> @@ -1672,6 +1719,9 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer,
> {
> int len = sizeof(XATTR_NAME_SMACK);
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return 0;
> +
> if (buffer != NULL && len <= buffer_size)
> memcpy(buffer, XATTR_NAME_SMACK, len);
>
> @@ -2918,6 +2968,9 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
> struct socket *sock;
> int rc = 0;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return -EOPNOTSUPP;
> +
> if (value == NULL || size > SMK_LONGLABEL || size == 0)
> return -EINVAL;
>
> @@ -3517,6 +3570,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
> if (inode == NULL)
> return;
>
> + if (unlikely(IS_PRIVATE(inode)))
> + return;
> +
> isp = smack_inode(inode);
>
> /*
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index c66e02ed8ee3..98eb8cd67f78 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -120,6 +120,9 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
> */
> static int tomoyo_inode_getattr(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, path, NULL);
> }
>
> @@ -132,6 +135,9 @@ static int tomoyo_inode_getattr(const struct path *path)
> */
> static int tomoyo_path_truncate(const struct path *path)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
> }
>
> @@ -159,6 +165,9 @@ static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry)
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
> }
>
> @@ -176,6 +185,9 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry,
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
> mode & S_IALLUGO);
> }
> @@ -192,6 +204,9 @@ static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry)
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
> }
>
> @@ -209,6 +224,9 @@ static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry,
> {
> struct path path = { .mnt = parent->mnt, .dentry = dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
> }
>
> @@ -229,6 +247,9 @@ static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry,
> int type = TOMOYO_TYPE_CREATE;
> const unsigned int perm = mode & S_IALLUGO;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(parent->dentry))))
> + return 0;
> +
> switch (mode & S_IFMT) {
> case S_IFCHR:
> type = TOMOYO_TYPE_MKCHAR;
> @@ -267,6 +288,9 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di
> struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry };
> struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
> + return 0;
> +
> return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
> }
>
> @@ -290,6 +314,11 @@ static int tomoyo_path_rename(const struct path *old_parent,
> struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
> struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
> + (d_is_positive(new_dentry) &&
> + IS_PRIVATE(d_backing_inode(new_dentry)))))
> + return 0;
> +
> if (flags & RENAME_EXCHANGE) {
> const int err = tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path2,
> &path1);
> @@ -360,6 +389,9 @@ static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
> */
> static int tomoyo_path_chmod(const struct path *path, umode_t mode)
> {
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, path,
> mode & S_IALLUGO);
> }
> @@ -377,6 +409,9 @@ static int tomoyo_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
> {
> int error = 0;
>
> + if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
> + return 0;
> +
> if (uid_valid(uid))
> error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path,
> from_kuid(&init_user_ns, uid));
^ permalink raw reply
* Re: [PATCH 1/2] fanotify: avoid/silence premature LSM capability checks
From: Paul Moore @ 2026-02-20 22:15 UTC (permalink / raw)
To: Ondrej Mosnacek
Cc: Jan Kara, Amir Goldstein, Matthew Bobrowski, linux-fsdevel,
linux-security-module, selinux, linux-kernel
In-Reply-To: <20260216150625.793013-2-omosnace@redhat.com>
On Mon, Feb 16, 2026 at 10:13 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> Make sure calling capable()/ns_capable() actually leads to access denied
> when false is returned, because these functions emit an audit record
> when a Linux Security Module denies the capability, which makes it
> difficult to avoid allowing/silencing unnecessary permissions in
> security policies (namely with SELinux).
>
> Where the return value just used to set a flag, use the non-auditing
> ns_capable_noaudit() instead.
>
> Fixes: 7cea2a3c505e ("fanotify: support limited functionality for unprivileged users")
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> fs/notify/fanotify/fanotify_user.c | 25 +++++++++++++------------
> 1 file changed, 13 insertions(+), 12 deletions(-)
Reviewed-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH 2/2] fanotify: call fanotify_events_supported() before path_permission() and security_path_notify()
From: Paul Moore @ 2026-02-20 22:16 UTC (permalink / raw)
To: Ondrej Mosnacek
Cc: Jan Kara, Amir Goldstein, Matthew Bobrowski, linux-fsdevel,
linux-security-module, selinux, linux-kernel
In-Reply-To: <20260216150625.793013-3-omosnace@redhat.com>
On Mon, Feb 16, 2026 at 10:14 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> The latter trigger LSM (e.g. SELinux) checks, which will log a denial
> when permission is denied, so it's better to do them after validity
> checks to avoid logging a denial when the operation would fail anyway.
>
> Fixes: 0b3b094ac9a7 ("fanotify: Disallow permission events for proc filesystem")
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
> fs/notify/fanotify/fanotify_user.c | 25 ++++++++++---------------
> 1 file changed, 10 insertions(+), 15 deletions(-)
Reviewed-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH 05/13] selinux: Use simple_start_creating() / simple_done_creating()
From: Paul Moore @ 2026-02-20 22:47 UTC (permalink / raw)
To: NeilBrown
Cc: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, James Morris, Serge E. Hallyn, Stephen Smalley,
linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260204050726.177283-6-neilb@ownmail.net>
On Wed, Feb 4, 2026 at 12:08 AM NeilBrown <neilb@ownmail.net> wrote:
>
> From: NeilBrown <neil@brown.name>
>
> Instead of explicitly locking the parent and performing a lookup in
> selinux, use simple_start_creating(), and then use
> simple_done_creating() to unlock.
>
> This extends the region that the directory is locked for, and also
> performs a lookup.
> The lock extension is of no real consequence.
> The lookup uses simple_lookup() and so always succeeds. Thus when
> d_make_persistent() is called the dentry will already be hashed.
> d_make_persistent() handles this case.
>
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
> security/selinux/selinuxfs.c | 15 +++++++--------
> 1 file changed, 7 insertions(+), 8 deletions(-)
Unless I'm missing something, there is no reason why I couldn't take
just this patch into the SELinux tree once the merge window closes,
yes?
--
paul-moore.com
^ permalink raw reply
* Re: [GIT PULL] AppArmor updates for 7.0-rc1
From: pr-tracker-bot @ 2026-02-21 1:20 UTC (permalink / raw)
To: John Johansen; +Cc: Linus Torvalds, LKLM, open list:SECURITY SUBSYSTEM
In-Reply-To: <53c39cf4-d9e4-4ccf-ae16-dc5b143c9681@canonical.com>
The pull request you sent on Wed, 18 Feb 2026 16:19:34 -0800:
> git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor tags/apparmor-pr-2026-02-18
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/219d7660c57ec7457a6020f3d025cfe1139ffc64
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [syzbot] [kernel?] INFO: task hung in restrict_one_thread_callback
From: Ding Yihan @ 2026-02-21 7:28 UTC (permalink / raw)
To: syzbot, Mickaël Salaün
Cc: linux-security-module, Günther Noack
In-Reply-To: <69995a88.050a0220.340abe.0d25.GAE@google.com>
Hi all,
Thanks to syzbot for the testing and confirmation.
Since I am relatively new to the inner workings of this specific subsystem,
I would like to take a few days to thoroughly study the root cause
(the task_work and mutex interaction) and prepare a detailed and proper commit message.
I will send out the formal patch (v1) to the mailing list later.
Best regards,
Yihan Ding
在 2026/2/21 15:11, syzbot 写道:
> Hello,
>
> syzbot has tested the proposed patch and the reproducer did not trigger any issue:
>
> Reported-by: syzbot+7ea2f5e9dfd468201817@syzkaller.appspotmail.com
> Tested-by: syzbot+7ea2f5e9dfd468201817@syzkaller.appspotmail.com
>
> Tested on:
>
> commit: d4906ae1 Add linux-next specific files for 20260220
> git tree: linux-next
> console output: https://syzkaller.appspot.com/x/log.txt?x=13ea89e6580000
> kernel config: https://syzkaller.appspot.com/x/.config?x=51f859f3211496bc
> dashboard link: https://syzkaller.appspot.com/bug?extid=7ea2f5e9dfd468201817
> compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
> patch: https://syzkaller.appspot.com/x/patch.diff?x=15f0595a580000
>
> Note: testing is done by a robot and is best-effort only.
>
^ permalink raw reply
* Re: [syzbot] [kernel?] INFO: task hung in restrict_one_thread_callback
From: Günther Noack @ 2026-02-21 12:00 UTC (permalink / raw)
To: Ding Yihan
Cc: syzbot, Mickaël Salaün, linux-security-module,
Jann Horn
In-Reply-To: <00A9E53EDC82309F+7b1dfc69-95f8-4ffc-a67c-967de0e2dfee@uniontech.com>
Hello Ding!
On Sat, Feb 21, 2026 at 03:28:47PM +0800, Ding Yihan wrote:
> Since I am relatively new to the inner workings of this specific subsystem,
> I would like to take a few days to thoroughly study the root cause
> (the task_work and mutex interaction) and prepare a detailed and proper commit message.
>
> I will send out the formal patch (v1) to the mailing list later.
Thank you very much for preparing a patch, and especially also for
forwarding this to us. (The original syzkaller report was somehow not
addressed to Landlock or the LSM list. We should fix that.)
Timing wise, the feature was picked up for the 7.0 release, so we
still have some time to fix it before this is stable.
As an early review for the patch:
Background:
We had previously convinced ourselves that grabbing the
cred_guard_mutex was not necessary. To quote the comment in
landlock_restrict_sibling_threads():
Unlike seccomp, which modifies sibling tasks directly, we do not need to
acquire the cred_guard_mutex and sighand->siglock:
- As in our case, all threads are themselves exchanging their own struct
cred through the credentials API, no locks are needed for that.
- Our for_each_thread() loops are protected by RCU.
- We do not acquire a lock to keep the list of sibling threads stable
between our for_each_thread loops. If the list of available sibling
threads changes between these for_each_thread loops, we make up for
that by continuing to look for threads until they are all discovered
and have entered their task_work, where they are unable to spawn new
threads.
The question of locking cred_guard_mutex came up in the patch
discussion multiple times as well, the most recent discussion was:
https://lore.kernel.org/all/20251020.fohbo6Iecahz@digikod.net/
If it helps, I keep some of my own notes for this particular feature
on https://wiki.gnoack.org/LandlockMultithreadedEnforcement.
(Very) tentative investigation:
In the Syzkaller report [2], it seems that the reproducer [2.1] is
creating two rulesets and then enforcing them in parallel, a scenario
which we are exercising in the TEST(competing_enablement) in
tools/testing/selftests/landlock/tsync_test.c already, but which has
not failed in my own selftest runs.
In the crash report, there are four threads in total:
* Two are stuck in the line
wait_for_completion(&ctx->ready_to_commit);
in the per-thread task work (line 128 [4.1])
* Two are stuck in the line
wait_for_completion(&shared_ctx.all_prepared)
in the calling thread's coordination logic (line 539 [4.2])
In line 539, we are already on the code path where we detected that we
are getting interrupted by another thread and where we are attempting
to deal with the scenario where two landlock_restrict_self() calls
compete. This is detected on line 523 when
wait_for_completion_interruptible() is true. The approach to handle
this is to set the overall -ERESTARTNOINTR error and cancel the work
that has been ongoing so far, by canceling the task works that did not
start running yet and waiting for the ones that did start running
(that is the step where we are blocked!). The reasoning there was
that these task works will all hit the "all_prepared" stage now, but
as we can see in the stack trace, the task works that are actively
running are already on line 128 and have passed the "all_prepared"
stage).
Differences I can see between syzkaller and our own test:
* The reproducer also calls openat() and then twice socketpair().
These syscalls should be unrelated, but it's possible that the
"async" invocation of socketpair() contributes to adding more
threads. (Assuming that "async" means "in new thread" in syzkaller)
* Syzkaller gives it more attempts. ([2.2])
I do not understand yet what went wrong in our scheme and need to look
deeper.
Ding, do you have more insights into it from your debugging?
Thanks,
–Günther
For reference:
[1] Report Mail: https://lore.kernel.org/all/69984159.050a0220.21cd75.01bb.GAE@google.com/
[2] Report: https://syzkaller.appspot.com/bug?extid=7ea2f5e9dfd468201817
[2.1] Reproducer: https://syzkaller.appspot.com/text?tag=ReproSyz&x=16e41c02580000
[2.2] Reproducer (C): https://syzkaller.appspot.com/text?tag=ReproC&x=15813652580000
[3] Patch: https://lore.kernel.org/all/6999504d.a70a0220.2c38d7.0154.GAE@google.com/
[4.1] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/security/landlock/tsync.c?id=635c467cc14ebdffab3f77610217c1dacaf88e8c#n128
[4.2] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/security/landlock/tsync.c?id=635c467cc14ebdffab3f77610217c1dacaf88e8c#n539
^ permalink raw reply
* Re: [syzbot] [kernel?] INFO: task hung in restrict_one_thread_callback
From: Günther Noack @ 2026-02-21 13:19 UTC (permalink / raw)
To: Ding Yihan
Cc: syzbot, Mickaël Salaün, linux-security-module,
Jann Horn
In-Reply-To: <20260221.5d8a306bcaf1@gnoack.org>
On Sat, Feb 21, 2026 at 01:00:03PM +0100, Günther Noack wrote:
> (Very) tentative investigation:
>
> In the Syzkaller report [2], it seems that the reproducer [2.1] is
> creating two rulesets and then enforcing them in parallel, a scenario
> which we are exercising in the TEST(competing_enablement) in
> tools/testing/selftests/landlock/tsync_test.c already, but which has
> not failed in my own selftest runs.
>
> In the crash report, there are four threads in total:
>
> * Two are stuck in the line
> wait_for_completion(&ctx->ready_to_commit);
> in the per-thread task work (line 128 [4.1])
> * Two are stuck in the line
> wait_for_completion(&shared_ctx.all_prepared)
> in the calling thread's coordination logic (line 539 [4.2])
>
> In line 539, we are already on the code path where we detected that we
> are getting interrupted by another thread and where we are attempting
> to deal with the scenario where two landlock_restrict_self() calls
> compete. This is detected on line 523 when
> wait_for_completion_interruptible() is true. The approach to handle
> this is to set the overall -ERESTARTNOINTR error and cancel the work
> that has been ongoing so far, by canceling the task works that did not
> start running yet and waiting for the ones that did start running
> (that is the step where we are blocked!). The reasoning there was
> that these task works will all hit the "all_prepared" stage now, but
> as we can see in the stack trace, the task works that are actively
> running are already on line 128 and have passed the "all_prepared"
> stage).
>
> Differences I can see between syzkaller and our own test:
>
> * The reproducer also calls openat() and then twice socketpair().
> These syscalls should be unrelated, but it's possible that the
> "async" invocation of socketpair() contributes to adding more
> threads. (Assuming that "async" means "in new thread" in syzkaller)
> * Syzkaller gives it more attempts. ([2.2])
>
> I do not understand yet what went wrong in our scheme and need to look
> deeper.
OK, I think I understand now. Our existing recovery code for this
conflict is this:
/*
* Decrement num_preparing for current, to undo that we initialized it
* to 1 a few lines above.
*/
if (atomic_dec_return(&shared_ctx.num_preparing) > 0) {
if (wait_for_completion_interruptible(
&shared_ctx.all_prepared)) {
/* In case of interruption, we need to retry the system call. */
atomic_set(&shared_ctx.preparation_error,
-ERESTARTNOINTR);
/*
* Cancel task works for tasks that did not start running yet,
* and decrement all_prepared and num_unfinished accordingly.
*/
cancel_tsync_works(&works, &shared_ctx);
/*
* The remaining task works have started running, so waiting for
* their completion will finish.
*/
wait_for_completion(&shared_ctx.all_prepared);
}
}
When I wrote this, I assumed, as the last comment states, that the
task works which we could not cancel, are already running.
I was wrong there, because I had misunderstood task_work_run(). When
the task works get run there, it first *atomically dequeues the entire
queue of scheduled task works*, and then runs them sequentially.
That is why, if we have one task work that belongs to the first
landlock_restrict_self() call and one which belongs to the other, the
task work which is scheduled later can (a) not be dequeued with
cancel_tsync_works() any more, and (b) also has not started running
yet.
Now the only thing that is necessary to produce the deadlock is that
we have a pair of threads where the task works for the restriction
calls have been scheduled in different order. When the two
landlock_restrict_self() calls end up in the recovery path quoted
above, they will wait for one of their task works to run which is
blocked from running by another task work that is scheduled before and
does not finish either.
(Just pasting a brain dump here to save you some time hunting for the
root cause. I don't know the best solution yet either.)
–Günther
^ permalink raw reply
* Re: [PATCH v6] lsm: Add LSM hook security_unix_find
From: Justin Suess @ 2026-02-21 13:22 UTC (permalink / raw)
To: Günther Noack
Cc: brauner, demiobenour, fahimitahera, hi, horms, ivanov.mikhail1,
jannh, jmorris, john.johansen, konstantin.meskhidze,
linux-security-module, m, matthieu, mic, netdev, paul,
samasth.norway.ananda, serge, viro
In-Reply-To: <20260220.70d0460d1309@gnoack.org>
On Fri, Feb 20, 2026 at 04:49:34PM +0100, Günther Noack wrote:
> Hello!
>
> On Thu, Feb 19, 2026 at 03:04:59PM -0500, Justin Suess wrote:
> > diff --git a/security/security.c b/security/security.c
> > index 67af9228c4e9..c73196b8db4b 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -4731,6 +4731,26 @@ int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> >
> > #endif /* CONFIG_SECURITY_NETWORK */
> >
> > +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> > +/**
> > + * security_unix_find() - Check if a named AF_UNIX socket can connect
> > + * @path: path of the socket being connected to
> > + * @other: peer sock
> > + * @flags: flags associated with the socket
> > + *
> > + * This hook is called to check permissions before connecting to a named
> > + * AF_UNIX socket.
>
> Nit: Could we please insert a sentence about locking here?
>
> Something like:
>
> The caller holds no locks on @other.
>
> (Originally brought up by Mickaël in
> https://lore.kernel.org/all/20260217.lievaS8eeng8@digikod.net/)
>
> Thanks,
> –Günther
Sounds good. Would a "Link:" to the mentioned thread be appropriate in the commit
message?
I feel like the reasoning for this is subtle but important for hook
consumers.
Justin
^ permalink raw reply
* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
From: Nicolas Schier @ 2026-02-21 21:38 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
linux-modules, linux-security-module, linux-doc, linuxppc-dev,
linux-integrity
In-Reply-To: <20260113-module-hashes-v4-15-0b932db9b56b@weissschuh.net>
Hi Thomas,
On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> The current signature-based module integrity checking has some drawbacks
> in combination with reproducible builds. Either the module signing key
> is generated at build time, which makes the build unreproducible, or a
> static signing key is used, which precludes rebuilds by third parties
> and makes the whole build and packaging process much more complicated.
>
> The goal is to reach bit-for-bit reproducibility. Excluding certain
> parts of the build output from the reproducibility analysis would be
> error-prone and force each downstream consumer to introduce new tooling.
>
> Introduce a new mechanism to ensure only well-known modules are loaded
> by embedding a merkle tree root of all modules built as part of the full
> kernel build into vmlinux.
>
> Non-builtin modules can be validated as before through signatures.
>
> Normally the .ko module files depend on a fully built vmlinux to be
> available for modpost validation and BTF generation. With
> CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> to build a merkle tree. This introduces a dependency cycle which is
> impossible to satisfy. Work around this by building the modules during
> link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> but before the final module hashes are
>
> The PKCS7 format which is used for regular module signatures can not
> represent Merkle proofs, so a new kind of module signature is
> introduced. As this signature type is only ever used for builtin
> modules, no compatibility issues can arise.
>
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> .gitignore | 1 +
> Documentation/kbuild/reproducible-builds.rst | 5 +-
> Makefile | 8 +-
> include/asm-generic/vmlinux.lds.h | 11 +
> include/linux/module_hashes.h | 25 ++
> include/linux/module_signature.h | 1 +
> kernel/module/Kconfig | 21 +-
> kernel/module/Makefile | 1 +
> kernel/module/hashes.c | 92 ++++++
> kernel/module/hashes_root.c | 6 +
> kernel/module/internal.h | 1 +
> kernel/module/main.c | 4 +-
> scripts/.gitignore | 1 +
> scripts/Makefile | 3 +
> scripts/Makefile.modfinal | 11 +
> scripts/Makefile.modinst | 13 +
> scripts/Makefile.vmlinux | 5 +
> scripts/link-vmlinux.sh | 14 +-
> scripts/modules-merkle-tree.c | 467 +++++++++++++++++++++++++++
> security/lockdown/Kconfig | 2 +-
> 20 files changed, 685 insertions(+), 7 deletions(-)
>
[...]
> diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
> new file mode 100644
> index 000000000000..1abfcd3aa679
> --- /dev/null
> +++ b/kernel/module/hashes_root.c
> @@ -0,0 +1,6 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +#include <linux/module_hashes.h>
> +
> +/* Blank dummy data. Will be overridden by link-vmlinux.sh */
> +const struct module_hashes_root module_hashes_root __module_hashes_section = {};
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index e2d49122c2a1..e22837d3ac76 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -338,6 +338,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
> const char *secstrings);
>
> int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
> +int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len);
>
> #ifdef CONFIG_DEBUG_KMEMLEAK
> void kmemleak_load_module(const struct module *mod, const struct load_info *info);
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index 2a28a0ece809..fa30b6387936 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -3362,8 +3362,10 @@ static int module_integrity_check(struct load_info *info, int flags)
>
> if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
> err = module_sig_check(info, sig, sig_len);
> + } else if (IS_ENABLED(CONFIG_MODULE_HASHES) && sig_type == PKEY_ID_MERKLE) {
> + err = module_hash_check(info, sig, sig_len);
> } else {
> - pr_err("module: not signed with expected PKCS#7 message\n");
> + pr_err("module: not signed with signature mechanism\n");
> err = -ENOPKG;
To prevent others from running into the same issue:
My first test got stuck here, as I tested with virtme-ng, which symlinks
modules from build tree to /lib/modules/$(uname -r)/..., resulting in
[ 15.956855] module: not signed with signature mechanism
modprobe: ERROR: could not insert 'efivarfs': Package not installed
As the modules_install step was missing, modules were not being signed.
[...]
> diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> new file mode 100644
> index 000000000000..a6ec0e21213b
> --- /dev/null
> +++ b/scripts/modules-merkle-tree.c
> @@ -0,0 +1,467 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Compute hashes for modules files and build a merkle tree.
> + *
> + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> + *
> + */
> +#define _GNU_SOURCE 1
> +#include <arpa/inet.h>
> +#include <err.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <stdarg.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +
> +#include <openssl/evp.h>
> +#include <openssl/err.h>
> +
> +#include "ssl-common.h"
> +
> +static int hash_size;
> +static EVP_MD_CTX *ctx;
> +
> +struct module_signature {
> + uint8_t algo; /* Public-key crypto algorithm [0] */
> + uint8_t hash; /* Digest algorithm [0] */
> + uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
> + uint8_t signer_len; /* Length of signer's name [0] */
> + uint8_t key_id_len; /* Length of key identifier [0] */
> + uint8_t __pad[3];
> + uint32_t sig_len; /* Length of signature data */
> +};
> +
> +#define PKEY_ID_MERKLE 3
> +
> +static const char magic_number[] = "~Module signature appended~\n";
This here will be the forth definition of struct module_signature,
increasing the risk of unwanted diversion. I second Petr's suggestion
to reuse a _common_ definition instead.
(Here, even include/linux/module_signature.h could be included itself.)
> +
> +struct file_entry {
> + char *name;
> + unsigned int pos;
> + unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +static struct file_entry *fh_list;
> +static size_t num_files;
> +
> +struct leaf_hash {
> + unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +struct mtree {
> + struct leaf_hash **l;
> + unsigned int *entries;
> + unsigned int levels;
> +};
> +
> +static inline void *xcalloc(size_t n, size_t size)
> +{
> + void *p;
> +
> + p = calloc(n, size);
> + if (!p)
> + errx(1, "Memory allocation failed");
> +
> + return p;
> +}
> +
> +static void *xmalloc(size_t size)
> +{
> + void *p;
> +
> + p = malloc(size);
> + if (!p)
> + errx(1, "Memory allocation failed");
> +
> + return p;
> +}
> +
> +static inline void *xreallocarray(void *oldp, size_t n, size_t size)
> +{
> + void *p;
> +
> + p = reallocarray(oldp, n, size);
> + if (!p)
> + errx(1, "Memory allocation failed");
> +
> + return p;
> +}
> +
> +static inline char *xasprintf(const char *fmt, ...)
> +{
> + va_list ap;
> + char *strp;
> + int ret;
> +
> + va_start(ap, fmt);
> + ret = vasprintf(&strp, fmt, ap);
> + va_end(ap);
> + if (ret == -1)
> + err(1, "Memory allocation failed");
> +
> + return strp;
> +}
Please consider moving these x* functions into scripts/include/xalloc.h
for reuse. (I am sure someone else wrote this already, but I can't find
it...)
>
>
thanks for all your efforts for reproducibility!
As I have no clue about that: Is the patent for merkle trees [1] a
problem when integrating that here?
Can you verify if I get the mechanics roughly correct?
* Modules are merkle tree leaves. Modules are built and logically
paired by the order from modules.order; a single left-over module is
paired with itself.
* Hashes of paired modules are hashed again (branch node hash);
hashes of pairs of branch nodes' hashes are hashed again;
repeat until we reach the single merkle tree root hash
* The final merkle tree root hash (and the count of tree levels) is
included in vmlinux
'make && find . -name '*.ko' -exec rm {} \; && make' does not rebuild
the in-tree modules. Shifting the module-hashes support from
scripts/link-vmlinux.sh to scripts/Makefile.vmlinux might (make it
easier) to fix this again.
Kind regards,
Nicolas
[1]: https://worldwide.espacenet.com/patent/search/family/022107098/publication/US4309569A?q=pn%3DUS4309569
--
Nicolas
^ permalink raw reply
* [PATCH] landlock: Expand restrict flags example for ABI version 8
From: Panagiotis "Ivory" Vasilopoulos @ 2026-02-21 22:05 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack, Jonathan Corbet,
Shuah Khan
Cc: linux-security-module, linux-doc, linux-kernel
Add LANDLOCK_RESTRICT_SELF_TSYNC to the backwards compatibility example
for restrict flags. This introduces completeness, similar to that of
the ruleset attributes example.
Additionally, I modified the two comments of the example to make them
more consistent with the ruleset attributes example's.
Signed-off-by: Panagiotis 'Ivory' Vasilopoulos <git@n0toose.net>
---
Documentation/userspace-api/landlock.rst | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 13134bccdd39d78ddce3daf454f32dda162ce91b..d972af20791a722d4665bd2ac287b05c5e97248c 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -197,12 +197,18 @@ similar backwards compatibility check is needed for the restrict flags
.. code-block:: c
- __u32 restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
- if (abi < 7) {
- /* Clear logging flags unsupported before ABI 7. */
+ __u32 restrict_flags =
+ LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
+ LANDLOCK_RESTRICT_SELF_TSYNC;
+ switch (abi) {
+ case 1 ... 6:
+ /* Clear logging flags unsupported for ABI < 7 */
restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
+ case 7:
+ /* Removes multithread flag unsupported for ABI < 8 */
+ restrict_flags &= ~LANDLOCK_RESTRICT_SELF_TSYNC;
}
The next step is to restrict the current thread from gaining more privileges
---
base-commit: ceb977bfe9e8715e6cd3a4785c7aab8ea5cd2b77
change-id: 20260221-landlock-docs-add-tsync-example-e8fd5c64a366
Best regards,
--
Panagiotis "Ivory" Vasilopoulos <git@n0toose.net>
^ permalink raw reply related
* [PATCH v2] landlock: Expand restrict flags example for ABI version 8
From: Panagiotis "Ivory" Vasilopoulos @ 2026-02-21 22:12 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack, Jonathan Corbet,
Shuah Khan
Cc: linux-security-module, linux-doc, linux-kernel
Add LANDLOCK_RESTRICT_SELF_TSYNC to the backwards compatibility example
for restrict flags. This introduces completeness, similar to that of
the ruleset attributes example.
Additionally, I modified the two comments of the example to make them
more consistent with the ruleset attributes example's.
Signed-off-by: Panagiotis 'Ivory' Vasilopoulos <git@n0toose.net>
---
Changes in v2:
- Fix formatting error.
- Link to v1: https://lore.kernel.org/r/20260221-landlock-docs-add-tsync-example-v1-1-f89383809eb4@n0toose.net
---
Documentation/userspace-api/landlock.rst | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 13134bccdd39d78ddce3daf454f32dda162ce91b..0affe1c953d61a4b32aca700cd262c49cee6304a 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -197,12 +197,18 @@ similar backwards compatibility check is needed for the restrict flags
.. code-block:: c
- __u32 restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
- if (abi < 7) {
- /* Clear logging flags unsupported before ABI 7. */
+ __u32 restrict_flags =
+ LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
+ LANDLOCK_RESTRICT_SELF_TSYNC;
+ switch (abi) {
+ case 1 ... 6:
+ /* Clear logging flags unsupported for ABI < 7 */
restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
+ case 7:
+ /* Removes multithread flag unsupported for ABI < 8 */
+ restrict_flags &= ~LANDLOCK_RESTRICT_SELF_TSYNC;
}
The next step is to restrict the current thread from gaining more privileges
---
base-commit: ceb977bfe9e8715e6cd3a4785c7aab8ea5cd2b77
change-id: 20260221-landlock-docs-add-tsync-example-e8fd5c64a366
Best regards,
--
Panagiotis "Ivory" Vasilopoulos <git@n0toose.net>
^ permalink raw reply related
* Re: [PATCH 05/13] selinux: Use simple_start_creating() / simple_done_creating()
From: NeilBrown @ 2026-02-21 22:28 UTC (permalink / raw)
To: Paul Moore
Cc: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, James Morris, Serge E. Hallyn, Stephen Smalley,
linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <CAHC9VhThChVk1Dk+f-KANGj7Tu7zzHCiA==taeQ+=nQaH6a7sg@mail.gmail.com>
On Sat, 21 Feb 2026, Paul Moore wrote:
> On Wed, Feb 4, 2026 at 12:08 AM NeilBrown <neilb@ownmail.net> wrote:
> >
> > From: NeilBrown <neil@brown.name>
> >
> > Instead of explicitly locking the parent and performing a lookup in
> > selinux, use simple_start_creating(), and then use
> > simple_done_creating() to unlock.
> >
> > This extends the region that the directory is locked for, and also
> > performs a lookup.
> > The lock extension is of no real consequence.
> > The lookup uses simple_lookup() and so always succeeds. Thus when
> > d_make_persistent() is called the dentry will already be hashed.
> > d_make_persistent() handles this case.
> >
> > Signed-off-by: NeilBrown <neil@brown.name>
> > ---
> > security/selinux/selinuxfs.c | 15 +++++++--------
> > 1 file changed, 7 insertions(+), 8 deletions(-)
>
> Unless I'm missing something, there is no reason why I couldn't take
> just this patch into the SELinux tree once the merge window closes,
> yes?
Yes - but ...
Once this series lands (hopefully soon - I will resend after -rc1 is
out) I have another batch which depends on the new start_creating etc
API being used everywhere. So for Christian to be able to apply that,
he will need to pull in this patch from the SELinux tree.
So if you could apply just this patch to some branch and merge that
branch with your other work however works best for you, and make the
branch available, then I think Christian will be happy to merge that
with whatever vfs branch he includes my work in, and all should be good.
Thanks
NeilBrown
>
> --
> paul-moore.com
>
^ permalink raw reply
* Re: [PATCH 05/13] selinux: Use simple_start_creating() / simple_done_creating()
From: Paul Moore @ 2026-02-22 14:19 UTC (permalink / raw)
To: NeilBrown
Cc: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, James Morris, Serge E. Hallyn, Stephen Smalley,
linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <177171292163.8396.10671162503209732019@noble.neil.brown.name>
On Sat, Feb 21, 2026 at 5:28 PM NeilBrown <neilb@ownmail.net> wrote:
> On Sat, 21 Feb 2026, Paul Moore wrote:
> > On Wed, Feb 4, 2026 at 12:08 AM NeilBrown <neilb@ownmail.net> wrote:
> > >
> > > From: NeilBrown <neil@brown.name>
> > >
> > > Instead of explicitly locking the parent and performing a lookup in
> > > selinux, use simple_start_creating(), and then use
> > > simple_done_creating() to unlock.
> > >
> > > This extends the region that the directory is locked for, and also
> > > performs a lookup.
> > > The lock extension is of no real consequence.
> > > The lookup uses simple_lookup() and so always succeeds. Thus when
> > > d_make_persistent() is called the dentry will already be hashed.
> > > d_make_persistent() handles this case.
> > >
> > > Signed-off-by: NeilBrown <neil@brown.name>
> > > ---
> > > security/selinux/selinuxfs.c | 15 +++++++--------
> > > 1 file changed, 7 insertions(+), 8 deletions(-)
> >
> > Unless I'm missing something, there is no reason why I couldn't take
> > just this patch into the SELinux tree once the merge window closes,
> > yes?
>
> Yes - but ...
>
> Once this series lands (hopefully soon - I will resend after -rc1 is
> out) I have another batch which depends on the new start_creating etc
> API being used everywhere ...
Okay, thanks for letting me know. I was curious about something like
that based on the cover letter, but the timing wasn't clear.
Acked-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply
* Re: [RFC] Landlock: mutable domains (and supervisor notification uAPI options)
From: Tingmao Wang @ 2026-02-22 18:04 UTC (permalink / raw)
To: Günther Noack, Mickaël Salaün, Justin Suess
Cc: linux-security-module
In-Reply-To: <6981cdab-dcff-47fc-a236-1890f9bc9c55@maowtm.org>
On 2/15/26 02:54, Tingmao Wang wrote:
> PoC code used in the above videos are largely generated, somewhat buggy,
> and unreviewed, but they are available:
>
> - mutable domains:
> https://github.com/micromaomao/linux-dev/pull/26/changes
> - supervisor notification:
> https://github.com/micromaomao/linux-dev/pull/27/changes
btw, on second thought I should have clarified that I don't expect anyone
to review any of the code here. Those were done purely to ensure that the
design I'm asking for review here actually works, and I'm open to any
changes.
^ permalink raw reply
* Re: [RFC] Landlock: mutable domains (and supervisor notification uAPI options)
From: Tingmao Wang @ 2026-02-22 18:04 UTC (permalink / raw)
To: Justin Suess, Günther Noack, Mickaël Salaün
Cc: amir73il, jack, jannh, linux-security-module, penguin-kernel,
song
In-Reply-To: <20260215212353.3549464-1-utilityemal77@gmail.com>
On 2/15/26 21:23, Justin Suess wrote:
> On Sun, Feb 15, 2026 at 02:54:08AM +0000, Tingmao Wang wrote:
> [...]
>> Discussion on LANDLOCK_ADD_RULE_INTERSECT
>> -----------------------------------------
>>
>> This was initially proposed by Mickaël, although now after writing some
>> example code against it [7], I'm not 100% sure that it is the most useful
>> uAPI. For a supervisor based on some sort of config file, it already has
>> to track which rules are added to know what to remove, and thus I feel
>> that it would be easier (both to use and to implement) to have an API that
>> simply "replaces" a rule, rather than do a bitwise AND on the access.
>>
> Instead of intersection being done at the rule level via
> landlock_add_rule, would it be better for intersection to be done at the
> ruleset_fd/ruleset level?
>
> So instead of intersecting individual rules, you can intersect entire
> rulesets, with the added benefit of being able to intersect handled
> accesses as well. (so you could handle an access initially, and not
> handle it later).
Personally I don't think making the list of handled accesses mutable would
add a lot of value (after all, a sandbox would usually handle all accesses
that it knows of), and I would like to avoid the complexity of making the
list of handled accesses mutable. The semantic of "intersection" and
"union" of handled accesses is also not trivial: if ruleset A handles
read, and ruleset B handles read+write, their "intersection", if
interpreted as "only allow accesses allowed by both rulesets", would in
fact handle read+write (and their "union" would handle read only).
In the second (union) case, there is also the problem of what to do if
ruleset B has write access rules - these rules would technically become
invalid (although to no negative effect) in a ruleset that doesn't handle
write.
I do see the benefit of modifying scope bits (and maybe also
quiet_access_* bits), but I'm still worried about the extra complexity
(and thus also testing / docs needed etc)
>
> Intersecting at the ruleset level allows for grouping the intersection rules
> together, so you could create an unenforced ruleset for the sole purpose
> of intersecting with rulesets, and intersect all the rule(s) at once.
>
> That way, the ruleset fd can be reused for this purpose later with other
> supervisees, instead of creating ruleset, intersecting individual rules,
> repeat.
>
> I think also the semantics of having a function called
> "landlock_add_rule" actually removing accesses (when the intersect flag
> is added) is also confusing, because we're not really *add*-ing
> anything, we're removing.
>
> ALTERNATIVE #1
>
> Maybe the best way to do it is instead continue treating rulesets as
> immutable, but allow composition of them at ruleset creation time.
>
> This would look something like:
>
> Ruleset C = Ruleset A & Ruleset B
>
> Ruleset A and B are never modified, but instead a new Ruleset C is
> created that is the intersection of A and B. This could be done in a
> variety of ways (LANDLOCK_CREATE_RULESET_INTERSECT? new IOCTL?)
>
> An example API for what this might look like:
>
> struct landlock_ruleset_attr ruleset_attr = {
> // other fields for handled accesses must be blank.
> .left_fd = existing_fd,
> .right_fd = other_existing_fd,
> };
> int new_ruleset_fd = syscall(SYS_landlock_create_ruleset, &ruleset_attr,
> sizeof(ruleset_attr), LANDLOCK_CREATE_RULESET_INTERSECT);
>
> And then the resulting ruleset which is the intersection of existing_fd
> and other_existing_fd could be returned.
>
> Similarly, we could:
>
> int new_ruleset_fd = syscall(SYS_landlock_create_ruleset, &ruleset_attr,
> sizeof(ruleset_attr), LANDLOCK_CREATE_RULESET_UNION);
If we do keep with the "intersect" way of removing rules (instead of
replace / clear all), this does seem like an interesting idea. However,
it is more complex to implement (it will probably require traversing two
rbtrees at once to be implemented efficiently), and I'm not sure how much
utility this would add compared to just LANDLOCK_ADD_RULE_INTERSECT. See
below for more reasoning.
>
> Which would be convienent for creating unions of rulesets.
>
> Then instead mutating rulesets, we commit/replace an entirely new ruleset.
>
> ioctl(supervisee_fd, LANDLOCK_IOCTL_COMMIT_RULESET, &new_ruleset_fd);
Using a dedicated ioctl to commit is also a potentially better idea - I
find that having the commit be a side effect of landlock_add_rule() via a
flag a bit unwieldy, as it would either require the supervisor to track
when it adds the last rule, or to add an "empty" rule just to commit.
Mickaël, you initially suggested the LANDLOCK_ADD_RULE_COMMIT_SUPERVISOR
flag, but do you think this is better?
>
> This has the following benefits:
>
> 1. Clearer semantics: "landlock_add_rule" is just for adding rules, not
> removing.
>
> 2. Intersection of all ruleset attributes, not just individual rule
> attributes.
>
> 3. Better logical grouping of rules for the purpose of intersection, and
> better composition.
>
> It does have drawbacks:
>
> 1. Intersecting individual rules requires making an entire ruleset for
> that one rule.
>
> 2. Users must be responsible for closing the unused/old rulesets that
> they might not longer need.
>
> ALTERNATIVE #2
>
> A middle ground is to keep the ruleset mutation via landlock_add_rule,
> but have it be done at the ruleset_fd level.
>
> Something like this:
>
> struct landlock_ruleset_operand intersection = {
> .operand = other_ruleset_fd
> };
> landlock_add_rule(ruleset_fd, LANDLOCK_RULE_INTERSECT_RULESET, &intersection, 0))
>
> I think this is also a valid way to do things, and increases the
> reusibility of rulesets.
>
> 1. Again, having landlock_add_rule being used to actually remove rules
> is confusing.
In this case, wouldn't we also be removing rules via landlock_add_rule()?
Personally I feel like this inconsistency is tolerable (it's easy enough
to explain), but I guess we could also change this to an ioctl if this is
a problem.
>
> 2. I'm unsure if we can change handled accesses after ruleset creation,
> so we might not be able to intersect the handled accesses like we can in
> the ALTERNATIVE #1.
>
>> Another alternative is to simply have a "clear all rules in this ruleset"
>> flag. This allows the supervisor to not have to track what is already
>> allowed - if it reloads the config file, it can simply clear the ruleset,
>> re-add all rules based on the config, then commit it. Although I worry
>> that this might make implementing some other use cases more difficult.
>
> At a minimum, it is cumbersome, and I worry about file descriptors
> becoming inaccessible (due to bind mounts / namespace changes in the
> supervisor's environment).
>
> Of course they can just hold those file descriptors open for the purposes
> of future intersections, but this is annoying and error prone.
If a supervisor doesn't care about potential renames / mount / namespace
changes making the sandboxed application lose access to previously
accessible files, the "clear all rules" approach would not force it to
keep any fds open in order to remove rules (i.e. it can clear everything,
then re-open the fds to add the rules back). On the other hand, with the
"intersect" approach, it would have to keep the fds open in all cases to
correctly remove previously added rules, so I think this "clear all" is
not more cumbersome.
There is a general consideration here about how much we want to design the
API to advantage / disadvantage particular ways of using it. For example,
having ruleset-ruleset intersection / union operations would (in theory,
setting aside the fact that to remain compatible to older kernels it
cannot do this for constructions of existing static rulesets) work very
well for something like island [1] / landlock-config, where we compose
rulesets by intersecting / unioning different landlock configuration files
together. However, it will introduce more complexity to someone who just
wants to allow access as they come up (e.g. something like a "permissive /
learning mode"), as they now have to, every time when an access is denied,
create a new ruleset, add one single rule, and do a union.
IMO, there is also the general preference of having complicated logic
being in user-space rather than implemented by the kernel. In this case,
one can argue that for someone that wants to compose rulesets via logical
operations, this should really be handled by a Landlock library, which
could, for static rulesets, do it completely internally in-memory, and do
one landlock_create_ruleset() + n landlock_add_rule()s in the end. For
live modifications, it could then use the more low-level "intersect" /
"add rule" / "clear all" uAPI. Compared to intersecting single rules,
having the kernel do the logical operations on entire rulesets also
doesn't reduce the number of syscalls needed (you still need one
landlock_add_rule() for each rule to be modified), so there is not a
performance argument either
(although for the use case where the supervisor wants to incrementally add
and remove rules, there is a performance benefit to intersect vs "clear
all". But I do wonder how often in practice this would be implemented for
a supervisor that can remove rules - because it needs to keep an in-memory
table for the fds it has open anyway, in order to correctly remove rules,
a simpler approach would be to simply clear all, then re-add whatever it
wants based on what it still has in that table).
[1]: https://github.com/landlock-lsm/island
^ permalink raw reply
* Re: [RFC] Landlock: mutable domains (and supervisor notification uAPI options)
From: Tingmao Wang @ 2026-02-22 18:04 UTC (permalink / raw)
To: Justin Suess, Günther Noack, Mickaël Salaün
Cc: amir73il, jack, jannh, linux-security-module, penguin-kernel,
song
In-Reply-To: <20260216212753.4159224-1-utilityemal77@gmail.com>
On 2/16/26 21:27, Justin Suess wrote:
> On Sun, Feb 15, 2026 at 02:54:08AM +0000, Tingmao Wang wrote:
>> [...]
>> The next stage is to introduce "mutable domains". The motivation for this
>> is two fold:
>>
>> 1. This allows the supervisor to allow access to (large) file hierarchies
>> without needing to be woken up again for each access.
>> 2. Because we cannot block within security_path_mknod and other
>> directory-modification related hooks [6], the proposal was to return
>> immediately from those hooks after queuing the supervisor notification,
>> then wait in a separate task_work. This however means that we cannot
>> directly "allow" access (and even if we can, it may introduce TOCTOU
>> problems). In order to allow access to requested files, the supervisor
>> has to add additional rules to the (now mutable) domain which will
>> allow the required access.
>
> Is blocking during connect(2) allowed either if the socket is non-blocking?
>
> This may be another example case that needs to be handled differently than calls
> we can block in safely.
I think the non-blocking socket case is worth considering but is
orthogonal to the above discussion - even when the socket is marked
non-blocking, we can still "safely" block in the connect hook (as long as
there isn't another reason, like locks, that prevents us from doing so),
because it only affects the calling process. Although if we're doing the
-ERESTARTNOINTR approach for fs, we might as well do the same here and
return -ERESTARTNOINTR from the connect hook (but that would still "block"
the syscall). Also, even for the file create/delete case, while we don't
block in the hook, from the user-space perspective, we're still blocking
(i.e. in the task_work), since the syscall doesn't immediately return (but
instead waits for a response from the supervisor).
However we could consider implementing some special case for
non-blocking-capable operations (like socket connect) such that we return
from the syscall without waiting for a supervisor response (and we return
a value that a "try again later" response would normally return), but then
we have to figure out how to cause a "you can retry now" notification that
would normally be sent for the non-blocking operation.
Related, there is the question of how we want to handle "non-blocking
open()s". Technically there isn't a non-blocking open() for regular files
from the user-space perspective, but ideally, for example, a wait on a
Landlock supervisor would not prevent an io_uring_enter() processing
multiple openat()s from continuing on to other requests. But I suspect
this would be very hard to implement.
> [...]
>> (Disallowing) self-supervision
>> ------------------------------
>>
>> We should figure out a way to ensure that a process cannot call
>> landlock_restrict_self() with a ruleset that has a supervisor for which it
>> has access to (i.e. via a supervisor ruleset fd). This prevents
>> accidental misuse, and also prevents deadlocks as discussed in [11]. I'm
>> not sure if this will be easy to implement, however.
>
> This seems like a graph acyclicity problem.
>
> Here are a couple cases to consider:
>
> 1. LANDLOCK_RESTRICT_SELF_TSYNC misuse:
>
> In the case where a user wants to use this supervisor to supervise other
> threads within the same process, a user could naively call
> LANDLOCK_RESTRICT_SELF_TSYNC (merged into 7.0) when enforcing the
> supervisee_fd. This would enforce the same policy on the thread running
> the supervisor and the supervisee.
Yes, but if the intention is to supervise threads within the same process
(which IMO is already a slightly questionable use case, maybe it can be
used as a tracing method, but certainly not for security), calling
restrict with LANDLOCK_RESTRICT_SELF_TSYNC is itself a mistake. There is
a case for us to try to detect and prevent this, but I'm thinking it would
more be to prevent "useless sandboxing" rather than to prevent deadlocks
(after all, it might not deadlock if someone else also has a fd for that
supervisor).
>
> 2. Transfer of the supervisee_fd (SCM_RIGHTS)
>
> It's possible to transfer file descriptors over unix domain sockets. If
> we had a supervisor daemon that used this form of IPC to send precooked
> supervisee_fds to other threads, and one of those ended up in a parent
> process of the supervisor, we could inadvertently end up with problems.
Note that being a parent process of the supervisor itself is not a
problem, what matters here is the Landlock domain relationships. It will
only be a problem if the fd is then used to make the supervisor be
supervised by itself (but if it's an unrelated parent process that is
supervised, it should still be fine).
>
> 3. Blocking in other LSMs (pointed out in your source [11])
>
> The hardest case to deal with, other LSMs like TOMOYO can also block and
> cause dependency cycles.
>
> ---
>
> This gets tricky, and I don't know if just checking parent / child
> relationships would work. Because the supervisor and supervisee rulesets
> are just file descriptors, and there are potentially unlimited number of
> ways these FDs could be transfered or instantiated.
>
> I think the best way to deal with this is constraining the problem space:
>
> An idea (binding supervisors/supervisees to domains on first use)
>
> Whenever landlock_restrict_self(supervisee_fd,...) is called, check the
> current domain credentials and verify that the domain is a *proper
> subset* of the supervisors domain. Then permanently close the
> supervisee_fd and never allow reenforcement. Similarly, once a
> supervisor_fd is created, never allow commiting from a context with
> "current landlock domain != original landlock domain at creation"
>
> This prevents post-enforcement usage of the supervisee_fd by a parent
> domain, and post-commit usage of a supervisee_fd by any subdomain.
It's not the supervisee fd that matters right? For example, a supervisor
can still call landlock_restrict_self() with the supervisee_fd it acquired
from the ioctl() (after all, this call itself is fine, it's only the fact
that there is no other process holding the supervisor fd that means this
gets us into a deadlock situation). I would think that if we
automatically close any fds, it would be the supervisor fd that we would
close. In this particular case this then means that there are no longer
any process holding the supervisor fd, which is a case we can detect, and
just deny any access request by the supervisee from that point forward
(preventing deadlock).
>
> I'm not sure if it's possible to check whether one domain is a
> proper subset of another (ie supervisor domain includes but *doesn't equal*
> supervisee domain), but I think that's one way do do it.
>
> This idea would help, but doesn't address case 3 above.
>
>>
>> [11]: https://lore.kernel.org/all/cc3e131f-f9a3-417b-9267-907b45083dc3@maowtm.org/
>>
>>
>> Supervisor notification
>> -----------------------
>>
>> The above RFC only covers mutable domains. The natural next stage of this
>> work is to send notification to the supervisor on access denials, so that
>> it can decide whether to allow the access or not. For that, there are
>> also lots of questions at this stage:
>>
>>
>> - Should we in fact implement that first, before mutable domains? This
>> means that the supervisor would only be able to find out about denials,
>> but not allow them without a sandbox restart. We still eventually want
>> the mutable domains, since that makes this a lot more useful, but I can
>> see some use cases for just the notification part (e.g. island denial
>> log), and I can't see a likely use case for just mutable domains, aside
>> from live reload of landlock-config (maybe that _is_ useful on its own,
>> considering that you can also find out about denials from the kernel
>> audit log, and add missing rules based on that).
>>
>>
>> - Earlier when implementing the Landlock supervise v1 RFC, I basically
>> came up with an ad-hoc uAPI for the notification [12], and the PoC code
>> linked to above also uses this uAPI. There are of course many problems
>> with this as it stands, e.g. it only having one destname, which means
>> that for rename, the fd1 needs to be the child being moved, which does
>> not align with the vfs semantic and how Landlock treat it (i.e. the
>> thing being updated here is the parent directory, not the child itself).
>> Same for delete, which currently sends the child as fd1.
>>
>> But also, in discussion with Mickaël last year, he mentioned that we
>> could reuse the fsnotify infrastructure, and perhaps additionally, use
>> fanotify to deliver these notifications. I do think there is some
>> potential here, as fanotify already implements an event header, a
>> mechanism for receiving and replying to events, etc. We could possibly
>> extend it to send Landlock specific notifications via a new kind of mark
>> (FAN_MARK_LANDLOCK_DOMAIN ??) and add one or more new corresponding
>> event types. Mickaël mentioned mount notifications [13] as an example
>> of using fanotify to send notifications other than file/dir
>> modifications.
>>
>> I'm not sure if directly extending the fanotify uAPI is a good idea tho,
>> considering that Landlock is not a feature specific to the filesystem -
>> we will also have denial events for net_port rules, and perhaps more in
>> the future. However, Mickaël mentioned that there might be some
>> internal infrastructure which we can re-use (even if we have our own
>> notification uAPI).
> I think that a new FAN_MARK would be required to use fanotify uAPI.
>
> There are a couple questions I have with this: (if we extend fanotify)
>
> 1. What FAN_CLASS_* would notifications use?
>
> FAN_CLASS_* specifies the type of notification, when the notification is
> triggered.
>
> See [1] for the current classes.
>
> If we want interactive, pre-access blocking, that would correspond to
> FAN_CLASS_PRE_CONTENT or FAN_CLASS_CONTENT. Both of which currently
> require CAP_SYS_ADMIN regardless of FAN_MARK. Which requiring that
> would require that supervisors have CAP_SYS_ADMIN, if the current
> CAP_SYS_ADMIN requirements remain in place.
>
> (If we don't have interactive blocking denials, we could just use
> FAN_CLASS_NOTIF)
I'm not sure, using fanotify is just a vague idea at this point and I
don't have any concrete design to offer, and this is indeed a good point -
we definitely don't want to require CAP_SYS_ADMIN (since being
unprivileged is one advantage of Landlock)
>
> 2. How would fanotify events be encoded?
>
> Events in fanotify use this structure for event data (one or more of the
> following must be recieved in a notification) [2]
>
> struct fanotify_event_metadata {
> __u32 event_len;
> __u8 vers;
> __u8 reserved;
> __u16 metadata_len;
> __aligned_u64 mask;
> __s32 fd;
> __s32 pid;
> };
>
> There are access classes landlock restricts that might not have an fd at
> all, like abstract unix sockets, tcp ports, signals etc.
This was also one of my concerns, I guess we might still need a cookie to
identify such events. This is one of the reasons why I think extending
seccomp-unotify makes slightly more sense (if we have to choose one of
these two, but of course we can still create a standalone uAPI).
>
> Good news is fanotify supports multiple types of additional information
> records, and we could potentially extend fanotify to support new ones as
> you alluded to.
>
> For examples of this, see struct fanotify_event_info_mnt,
> fanotify_event_info_pidfd.
>
> These records get attached to the event so they could be used to pass
> landlock access data.
>
> 3. If we support interactive permission decisions (even for a
> subset of landlock access rights only), do we use the response code?
> (question might be moot if we don't do blocking/responses at all)
Regardless of uAPI choice, I would really like to support blocking (even
though it makes this whole thing much more difficult).
>
> From [2]:
>
> For permission events, the application must write(2) a structure
> of the following form to the fanotify file descriptor:
>
> struct fanotify_response {
> __s32 fd;
> __u32 response;
> };
>
>
> response is a FAN_ALLOW or FAN_DENY. This is used by fanotify as a
> one-time access decision. Would this be used to do one-off exceptions to
> policy, or would we require policy decisions to go through the
> supervisor_fd and ignore the response code?
I don't think we would be able to do one-off allow decisions (but we
should still be able to do deny) regardless of the uAPI we use, due to the
problem with inode locks.
>
> 4. How would we reconcile the disparity between fanotify access rights
> and landlock access rights?
>
> There's no clean 1:1 mapping between fanotify access rights and landlock
> access rights as Mickaël pointed out. [2] [3]
>
> Many fs rights (creation, deletion, rename, linking) are not handled or
> implemented, (not even considering network/unix/signal scoping), so we'd
> be adding all these landlock specific rights.
>
> We could make a "catch-all" FAN_LANDLOCK_ACCESS or similar and ignore
> all the existing rights, and put the actual access data in the event
> record. It's awkward either way.
> ---
>
> In conclusion, I think extending fanotify is more viable than seccomp,
> from a purely technical standpoint. because it seems extensible,
> and because it runs post-lsm hooks.
>
> That being said, it's awkward, requires large extensions to the API, and
> definition of permissions that are specific to landlock.
>
> Whether or not landlock makes sense in fanotify from a semantic point of
> view is an entirely different question. There's no precedent for
> non-filesystem access controls in fanotify, so it's a little... out-of-place
> for an LSM to expose features on a filesystem access notification api?
>
> Curious on what people think.
>
> [1]: https://man7.org/linux/man-pages/man2/fanotify_init.2.html
> [2]: https://man7.org/linux/man-pages/man7/fanotify.7.html
> [3]: https://lore.kernel.org/all/20250304.Choo7foe2eoj@digikod.net/
>>
>>
>> - The other uAPI alternative which I have been thinking of is to extend
>> seccomp-unotify. For example, a Landlock denial could result in the
>> syscall being trapped and a `struct seccomp_notif` being sent to the
>> seccomp supervisor (via the existing mechanism), with additional
>> information (mostly, the file(s) / net ports being accessed and access
>> rights requested) attached to the notification _somehow_. Then the
>> supervisor can use the same kind of responses one would use for
>> seccomp-unotify to cause the syscall to either be retried (possibly via
>> `SECCOMP_USER_NOTIF_FLAG_CONTINUE`) or return with an error code of its
>> choice (or alternatively, carry out the operation on behalf of the
>> child, and pretend that the syscall succeed, which might be useful to
>> implement an "allow file creation but only this file" / "allow `mktemp
>> -d` but not arbitrary create on anything under /tmp").
>>
>> Looking at `struct seccomp_notif` and `struct seccomp_data` however, I'm
>> not sure how feasible / doable this extension would be. Also,
>> seccomp-unotify is supposed to trigger before a syscall is actually
>> executed, whereas if we use it this way, we will want it to trigger
>> after we're already midway through the syscall (in the LSM hook). This
>> might make it hard to implement (and also twists a bit the uAPI
>> semantics of seccomp-unotify).
>>
>
> (Some of the stuff discussed with seccomp below is derived from a side
> conversation with Tingmao over this proposal)
>
> There are some problems with extending seccomp unotify. Passing the
> full context needed through this api to the supervisor is problematic.
> seccomp unotify notifications look like this [4]:
>
> struct seccomp_notif {
> __u64 id; /* Cookie */
> __u32 pid; /* TID of target thread */
> __u32 flags; /* Currently unused (0) */
> struct seccomp_data data; /* See seccomp(2) */
> };
>
> And struct seccomp_data [5]:
>
> struct seccomp_data {
> int nr; /* System call number */
> __u32 arch; /* AUDIT_ARCH_* value
> (see <linux/audit.h>) */
> __u64 instruction_pointer; /* CPU instruction pointer */
> __u64 args[6]; /* Up to 6 system call arguments */
> };
>
> Even if we pass the syscall data, for the userspace to actually decode
> the arguments to figure out what the access is doing we have two
> critical problems (1,2) and one annoyance (3):
>
> 1. The syscall itself doesn't necessarily contain the full context of the access.
>
> 2. We cannot decode the pointer-based arguments from userspace for a syscall
> in seccomp without TOCTOU. It also requires reaching into userspace
> memory. [6]
>
> 3. Decoding the syscall number is an arch-specific operation that we now have
> to expect userspace to deal with.
We would probably need to attach additional (potentially variable length,
like file names for mknod / link / rename requests) Landlock-specific
information. The supervisor should not have to do any syscall decoding
(otherwise this partially defeats the point of Landlock supervise - they
can just use seccomp-unotify instead, and handling all "monitored"
syscalls on behalf of the supervisee to prevent TOCTOU).
Also, if we do not allow one-off allows (but require the supervisor to
modify the ruleset), this cannot lead to TOCTOU because if the supervised
process tries to change the argument, it will cause another Landlock
denial and notification.
Thanks for the review!
Tingmao
^ permalink raw reply
* Re: [PATCH] ucount: check for CAP_SYS_RESOURCE using ns_capable_noaudit()
From: Andrew Morton @ 2026-02-22 19:58 UTC (permalink / raw)
To: Paul Moore
Cc: Ondrej Mosnacek, Serge E. Hallyn, Eric W . Biederman,
linux-kernel, linux-security-module, selinux
In-Reply-To: <CAHC9VhQOW4BQR+UCvrcXa_yiSqADN0HC2Xp0pMKs3FtnjcVaOQ@mail.gmail.com>
On Tue, 27 Jan 2026 17:09:40 -0500 Paul Moore <paul@paul-moore.com> wrote:
> > > > > Fixes: dbec28460a89 ("userns: Add per user namespace sysctls.")
> > > > > Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> > > > > ---
> > > > > kernel/ucount.c | 2 +-
> > > > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > > >
> > > > Reviewed-by: Paul Moore <paul@paul-moore.com>
> > >
> > > Acked-by: Serge Hallyn <serge@hallyn.com>
> > >
> > > Looks good to me. What tree should this go through? Network?
> >
> > Andrew has already applied the two patches I posted into his
> > mm-nonmm-unstable branch, so I assume they are set to go through his
> > tree.
>
> Andrew, any chance we can get a reply to these threads when you merge
> a patch into your tree?
I already spray out soooo much email.
A better approach would be for me to become more chatty. Reply
"thanks, applied <reasons> <caveats> <etc>". Shall do this.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox