* Re: [PATCH 02/11] hornet: invert map set check logic
From: Fan Wu @ 2026-05-30 0:57 UTC (permalink / raw)
To: Blaise Boscaccy
Cc: Jonathan Corbet, Shuah Khan, Paul Moore, James Morris,
Serge E. Hallyn, Eric Biggers, Fan Wu, James.Bottomley,
linux-security-module
In-Reply-To: <20260528030915.2654994-3-bboscaccy@linux.microsoft.com>
On Wed, May 27, 2026 at 8:09 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> In a multi-map hash verification scenario, a logic bug may have
> allowed an attacker to provide duplicate maps to satisfy the hash
> check count. Instead, invert the logic to verify each map discretely
>
> Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> ---
I just realized there is no audit event if hornet_check_prog_maps()
fails, probably should add one.
-Fan
^ permalink raw reply
* Re: security_task_prctl: why -ENOSYS
From: Serge E. Hallyn @ 2026-05-29 22:58 UTC (permalink / raw)
To: William Roberts; +Cc: Casey Schaufler, LSM, SElinux list
In-Reply-To: <CAFftDdqvvqrbm=+XsDdkJHdm12jxK9799_A=Txz6Q5goYC2ALQ@mail.gmail.com>
On Wed, May 27, 2026 at 11:05:56AM -0500, William Roberts wrote:
> On Tue, May 26, 2026 at 6:42 PM Casey Schaufler <casey@schaufler-ca.com> wrote:
> >
> > On 5/26/2026 4:21 PM, William Roberts wrote:
> > > On Tue, May 26, 2026 at 5:39 PM William Roberts
> > > <bill.c.roberts@gmail.com> wrote:
> > >> Hello,
> > >>
> > >> I am trying to understand the motivation behind having
> > >> security_task_prctl only continue if the return value is -ENOSYS. This
> > >> seems to be very different from other LSM hooks I have investigated.
> > >> For example, in other hooks, the value from SE Linux avc_has_perms is
> > >> used directly. This essentially means that a 0 will cause the check to
> > >> pass, and anything < 0 usually an error.
> > >>
> > >> In commit:
> > >> ----
> > >> commit d84f4f992cbd76e8f39c488cf0c5d123843923b1 ("CRED: Inaugurate COW
> > >> credentials")
> > >>
> > >> (8) security_task_prctl() and cap_task_prctl().
> > >>
> > >> security_task_prctl() has been modified to return -ENOSYS if it doesn't
> > >> want to handle a function, or otherwise return the return
> > >> value directly
> > >> rather than through an argument.
> > >>
> > >> Additionally, cap_task_prctl() now prepares a new set of
> > >> credentials, even
> > >> if it doesn't end up using it.
> > >> ----
> > >>
> > >> The check in kernel/sys.c is currently:
> > >> error = security_task_prctl(option, arg2, arg3, arg4, arg5);
> > >> if (error != -ENOSYS)
> > >> return error;
> > >>
> > >> Should this be something like, "error && error != -ENOSYS"?
> > >>
> > >> I ask because I am looking to leverage this hook in SE Linux, and it's
> > >> annoying to have to coerce all 0 returns to -ENOSYS.
> > > Of course after hours of banging my head and one email sent, it's more clear to
> > > me now WHY. This hook isn't meant for making yes or no decisions on an operation
> > > but rather to also handle special prctl flags for an LSM in question.
> > >
> > > I guess with the said, do we want this interface to be used for both
> > > a, let the lsm handle
> > > this prctl flag directed to me, as well as a yes/no security decision
> > > or do we want to split
> > > this out into two hooks?
> >
> > The task_prctl hook is used in capability and yama. It is only used to
> > provide a place to process LSM specific prctl options. It is not used to
> > make security decisions outside of processing the LSM's options. If you
> > want to make security decisions on general prctl options you will need
> > a new hook.
>
> Yeah I finally saw that after I sent the email, as always :-p, but thanks
> Casey for confirming my understanding.
>
> So now for naming... We have security_task_prctl for handling random
> prctl interface calls into LSMs. So what should this hook be called?
> Perhaps security_task_prctl_check? Should we rename the old hook to
How about security_task_prctl_allowed()? (Mirroring security_uring_*)
Renaming the existing hook security_task_prctl_handle() also wouldn't
be too bad, but that's probably more churn than it's worth.
> something else?
>
> For now, I will just pick a name, but suggestions are welcome.
>
> As an aside, any lore as to why go through generic prctl vs an
> LSM specific filesystem or an lsm specific prctl (we already
> have arch_prctl why not lsm_prctl)?
>
>
>
>
>
> >
> > >
> > >> Thanks,
> > >> Bill
^ permalink raw reply
* Re: [PATCH] keys: Pin request_key_auth payload in instantiate paths
From: Jarkko Sakkinen @ 2026-05-29 22:53 UTC (permalink / raw)
To: Shaomin Chen, David Howells
Cc: keyrings, linux-security-module, linux-kernel, David Howells,
Paul Moore, James Morris, Serge E. Hallyn
In-Reply-To: <20260526024838.3368409-1-eeesssooo020@gmail.com>
On Tue, May 26, 2026 at 10:48:38AM +0800, Shaomin Chen wrote:
> keyctl_instantiate_key_common() reads request_key_auth from the assumed
> auth key before copying an instantiation payload from userspace. The copy
> can fault and sleep. If the request completes and revokes the auth key in
> that window, the auth payload can be detached and freed before the
> instantiate path uses it again.
>
> A request-key helper reproducer can trigger this race. One helper child
> blocks in KEYCTL_INSTANTIATE_IOV while the original helper instantiates the
> requested key and returns. KASAN then reports a use-after-free from the
> stale request_key_auth payload in keyctl_instantiate_key_common().
>
> Give request_key_auth payloads a refcount. Take a payload reference while
> authkey->sem stabilizes the payload and revocation state. Hold that
> reference across the instantiate and reject paths. Drop the auth key
> owning reference from revoke and destroy.
>
> Reported-by: Shaomin Chen <eeesssooo020@gmail.com>
> Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com
> Signed-off-by: Shaomin Chen <eeesssooo020@gmail.com>
> ---
> include/keys/request_key_auth-type.h | 2 ++
> security/keys/internal.h | 2 ++
> security/keys/keyctl.c | 24 +++++++++++++++-----
> security/keys/request_key_auth.c | 33 ++++++++++++++++++++++++++--
> 4 files changed, 53 insertions(+), 8 deletions(-)
>
> diff --git a/include/keys/request_key_auth-type.h b/include/keys/request_key_auth-type.h
> index 36b89a933310..01e42ee5f409 100644
> --- a/include/keys/request_key_auth-type.h
> +++ b/include/keys/request_key_auth-type.h
> @@ -9,12 +9,14 @@
> #define _KEYS_REQUEST_KEY_AUTH_TYPE_H
>
> #include <linux/key.h>
> +#include <linux/refcount.h>
>
> /*
> * Authorisation record for request_key().
> */
> struct request_key_auth {
> struct rcu_head rcu;
> + refcount_t usage;
> struct key *target_key;
> struct key *dest_keyring;
> const struct cred *cred;
> diff --git a/security/keys/internal.h b/security/keys/internal.h
> index 2cffa6dc8255..b7b622bc36a1 100644
> --- a/security/keys/internal.h
> +++ b/security/keys/internal.h
> @@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(struct key *target,
> const void *callout_info,
> size_t callout_len,
> struct key *dest_keyring);
> +struct request_key_auth *request_key_auth_get(struct key *authkey);
> +void request_key_auth_put(struct request_key_auth *rka);
>
> extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
>
> diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
> index ef855d69c97a..d14ace88e529 100644
> --- a/security/keys/keyctl.c
> +++ b/security/keys/keyctl.c
> @@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_common(key_serial_t id,
> if (!instkey)
> goto error;
>
> - rka = instkey->payload.data[0];
> - if (rka->target_key->serial != id)
> + rka = request_key_auth_get(instkey);
> + if (!rka) {
> + ret = -EKEYREVOKED;
> goto error;
> + }
> + if (rka->target_key->serial != id)
> + goto error_put_rka;
>
> /* pull the payload in if one was supplied */
> payload = NULL;
> @@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_common(key_serial_t id,
> ret = -ENOMEM;
> payload = kvmalloc(plen, GFP_KERNEL);
> if (!payload)
> - goto error;
> + goto error_put_rka;
>
> ret = -EFAULT;
> if (!copy_from_iter_full(payload, plen, from))
> @@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_common(key_serial_t id,
>
> error2:
> kvfree_sensitive(payload, plen);
> +error_put_rka:
> + request_key_auth_put(rka);
> error:
> return ret;
> }
> @@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
> if (!instkey)
> goto error;
>
> - rka = instkey->payload.data[0];
> - if (rka->target_key->serial != id)
> + rka = request_key_auth_get(instkey);
> + if (!rka) {
> + ret = -EKEYREVOKED;
> goto error;
> + }
> + if (rka->target_key->serial != id)
> + goto error_put_rka;
>
> /* find the destination keyring if present (which must also be
> * writable) */
> ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
> if (ret < 0)
> - goto error;
> + goto error_put_rka;
>
> /* instantiate the key and link it into a keyring */
> ret = key_reject_and_link(rka->target_key, timeout, error,
> @@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
> if (ret == 0)
> keyctl_change_reqkey_auth(NULL);
>
> +error_put_rka:
> + request_key_auth_put(rka);
> error:
> return ret;
> }
> diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
> index a7d7538c1f70..282e09d8fa46 100644
> --- a/security/keys/request_key_auth.c
> +++ b/security/keys/request_key_auth.c
> @@ -23,6 +23,7 @@ static void request_key_auth_describe(const struct key *, struct seq_file *);
> static void request_key_auth_revoke(struct key *);
> static void request_key_auth_destroy(struct key *);
> static long request_key_auth_read(const struct key *, char *, size_t);
> +static void request_key_auth_rcu_disposal(struct rcu_head *);
>
> /*
> * The request-key authorisation key type definition.
> @@ -115,6 +116,31 @@ static void free_request_key_auth(struct request_key_auth *rka)
> kfree(rka);
> }
>
> +/*
> + * Take a reference to the request-key authorisation payload so callers can
> + * drop authkey->sem before doing operations that may sleep.
> + */
> +struct request_key_auth *request_key_auth_get(struct key *authkey)
> +{
> + struct request_key_auth *rka;
> +
> + down_read(&authkey->sem);
> + rka = dereference_key_locked(authkey);
> + if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags))
> + refcount_inc(&rka->usage);
> + else
> + rka = NULL;
> + up_read(&authkey->sem);
> +
> + return rka;
> +}
> +
> +void request_key_auth_put(struct request_key_auth *rka)
> +{
> + if (rka && refcount_dec_and_test(&rka->usage))
> + call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
> +}
> +
> /*
> * Dispose of the request_key_auth record under RCU conditions
> */
> @@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key)
> struct request_key_auth *rka = dereference_key_locked(key);
>
> kenter("{%d}", key->serial);
> + if (!rka)
> + return;
> rcu_assign_keypointer(key, NULL);
> - call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
> + request_key_auth_put(rka);
> }
>
> /*
> @@ -150,7 +178,7 @@ static void request_key_auth_destroy(struct key *key)
> kenter("{%d}", key->serial);
> if (rka) {
> rcu_assign_keypointer(key, NULL);
> - call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
> + request_key_auth_put(rka);
> }
> }
>
> @@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct key *target, const char *op,
> rka = kzalloc_obj(*rka);
> if (!rka)
> goto error;
> + refcount_set(&rka->usage, 1);
> rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL);
> if (!rka->callout_info)
> goto error_free_rka;
> --
> 2.47.3
I get the approach and for it makes sense but I'd really like to hear
David's feedback on this before moving forward.
In the meanwhile, were you able to do a script or similar as a
reproducer?
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v4 3/3] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
From: Jarkko Sakkinen @ 2026-05-29 22:46 UTC (permalink / raw)
To: Yeoreum Yun
Cc: linux-security-module, linux-kernel, linux-integrity, paul, zohar,
roberto.sassu, noodles, sudeep.holla, jmorris, serge,
dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <20260525075404.3480282-4-yeoreum.yun@arm.com>
On Mon, May 25, 2026 at 08:54:04AM +0100, Yeoreum Yun wrote:
> commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's build_in")
> probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA.
>
> However, IMA now provides the IMA_INIT_LATE_SYNC build option, which
> initialises IMA at the late_initcall_sync level, so this change is no
> longer required.
>
> Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> ---
> drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
> 1 file changed, 3 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
> index 99f1c1e5644b..025c4d4b17ca 100644
> --- a/drivers/char/tpm/tpm_crb_ffa.c
> +++ b/drivers/char/tpm/tpm_crb_ffa.c
> @@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno)
> */
> int tpm_crb_ffa_init(void)
> {
> - int ret = 0;
> -
> - if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
> - ret = ffa_register(&tpm_crb_ffa_driver);
> - if (ret) {
> - tpm_crb_ffa = ERR_PTR(-ENODEV);
> - return ret;
> - }
> - }
> -
> if (!tpm_crb_ffa)
> - ret = -ENOENT;
> + return -ENOENT;
>
> if (IS_ERR_VALUE(tpm_crb_ffa))
> - ret = -ENODEV;
> + return -ENODEV;
>
> - return ret;
> + return 0;
> }
> EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
>
> @@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = {
> .id_table = tpm_crb_ffa_device_id,
> };
>
> -#ifdef MODULE
> module_ffa_driver(tpm_crb_ffa_driver);
> -#endif
>
> MODULE_AUTHOR("Arm");
> MODULE_DESCRIPTION("TPM CRB FFA driver");
> --
> LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
>
How we would sync up this patch? Through which tree etc.
BR, Jarkko
^ permalink raw reply
* Re: [PATCH] tpm-buf: memory-safe allocations
From: Jarkko Sakkinen @ 2026-05-29 22:37 UTC (permalink / raw)
To: Srish Srinivasan
Cc: linux-integrity, Jarkko Sakkinen, Arun Menon, Daniel P. Smith,
Alec Brown, Ross Philipson, Stefan Berger, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, linux-kernel, keyrings,
linux-security-module
In-Reply-To: <b4e26e4c-d62d-4551-bbdd-b4409316c4a9@linux.ibm.com>
On Mon, May 25, 2026 at 07:16:03PM +0530, Srish Srinivasan wrote:
> Tested on emulated TPM 1.2 and TPM 2.0 backends.
>
> Coverage:
> TPM 1.2: sysfs and trusted-key paths
> TPM 2.0: PCR, random, and trusted-key paths
>
> Tested-by: Srish Srinivasan <ssrish@linux.ibm.com>
>
> On 5/22/26 7:05 AM, Jarkko Sakkinen wrote:
> > From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
> >
> > Decouple kzalloc from buffer creation, so that a managed allocation can be
> > used:
> >
> > struct tpm_buf *buf __free(kfree) buf = kzalloc(TPM_BUFSIZE,
> > GFP_KERNEL);
> > if (!buf)
> > return -ENOMEM;
> >
> > tpm_buf_init(buf, TPM_BUFSIZE);
> >
> > Alternatively, stack allocations are also possible:
> >
> > u8 buf_data[512];
> > struct tpm_buf *buf = (struct tpm_buf *)buf_data;
> > tpm_buf_init(buf, sizeof(buf_data));
> >
> > This is achieved by embedding buffer's header inside the allocated blob,
> > instead of having an outer wrapper.
> >
> > Cc: Arun Menon <armenon@redhat.com>
> > Cc: Daniel P. Smith <dpsmith@apertussolutions.com>
> > Cc: Alec Brown <alec.r.brown@oracle.com>
> > Cc: Ross Philipson <ross.philipson@gmail.com>
> > Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> > Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
> > ---
> > Rebased the managed allocations patch, which has been probably like a
> > year in circulation.
> > drivers/char/tpm/tpm-buf.c | 122 ++++++----
> > drivers/char/tpm/tpm-sysfs.c | 17 +-
> > drivers/char/tpm/tpm.h | 1 -
> > drivers/char/tpm/tpm1-cmd.c | 150 ++++++------
> > drivers/char/tpm/tpm2-cmd.c | 277 +++++++++++-----------
> > drivers/char/tpm/tpm2-sessions.c | 149 ++++++------
> > drivers/char/tpm/tpm2-space.c | 44 ++--
> > drivers/char/tpm/tpm_vtpm_proxy.c | 30 +--
> > include/linux/tpm.h | 18 +-
> > security/keys/trusted-keys/trusted_tpm1.c | 44 ++--
> > security/keys/trusted-keys/trusted_tpm2.c | 165 ++++++-------
> > 11 files changed, 505 insertions(+), 512 deletions(-)
> >
> > diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
> > index dc882fc9fa9e..b16d824ef0af 100644
> > --- a/drivers/char/tpm/tpm-buf.c
> > +++ b/drivers/char/tpm/tpm-buf.c
> > @@ -7,82 +7,110 @@
> > #include <linux/module.h>
> > #include <linux/tpm.h>
> >
> > -/**
> > - * tpm_buf_init() - Allocate and initialize a TPM command
> > - * @buf: A &tpm_buf
> > - * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
> > - * @ordinal: A command ordinal
> > - *
> > - * Return: 0 or -ENOMEM
> > - */
> > -int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
> > +static void __tpm_buf_size_invariant(struct tpm_buf *buf, u16 buf_size)
> > {
> > - buf->data = (u8 *)__get_free_page(GFP_KERNEL);
> > - if (!buf->data)
> > - return -ENOMEM;
> > -
> > - tpm_buf_reset(buf, tag, ordinal);
> > - return 0;
> > + u32 buf_size_2 = (u32)buf->capacity + (u32)sizeof(*buf);
> > +
> > + if (!buf->capacity) {
> > + if (buf_size > TPM_BUFSIZE) {
> > + WARN(1, "%s: size overflow: %u\n", __func__, buf_size);
> > + buf->flags |= TPM_BUF_OVERFLOW;
> > + }
> > + } else {
> > + if (buf_size != buf_size_2) {
> > + WARN(1, "%s: size mismatch: %u != %u\n", __func__,
> > + buf_size, buf_size_2);
> > + buf->flags |= TPM_BUF_OVERFLOW;
> > + }
> > + }
> > }
> > -EXPORT_SYMBOL_GPL(tpm_buf_init);
> >
> > -/**
> > - * tpm_buf_reset() - Initialize a TPM command
> > - * @buf: A &tpm_buf
> > - * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
> > - * @ordinal: A command ordinal
> > - */
> > -void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
> > +static void __tpm_buf_reset(struct tpm_buf *buf, u16 buf_size, u16 tag,
> > + u32 ordinal)
> > {
> > struct tpm_header *head = (struct tpm_header *)buf->data;
> >
> > + __tpm_buf_size_invariant(buf, buf_size);
> > +
> > + if (buf->flags & TPM_BUF_OVERFLOW)
> > + return;
> > +
> > WARN_ON(tag != TPM_TAG_RQU_COMMAND && tag != TPM2_ST_NO_SESSIONS &&
> > tag != TPM2_ST_SESSIONS && tag != 0);
> >
> > buf->flags = 0;
> > buf->length = sizeof(*head);
> > + buf->capacity = buf_size - sizeof(*buf);
> > + buf->handles = 0;
> > head->tag = cpu_to_be16(tag);
> > head->length = cpu_to_be32(sizeof(*head));
> > head->ordinal = cpu_to_be32(ordinal);
> > +}
> > +
> > +static void __tpm_buf_reset_sized(struct tpm_buf *buf, u16 buf_size)
> > +{
> > + __tpm_buf_size_invariant(buf, buf_size);
> > +
> > + if (buf->flags & TPM_BUF_OVERFLOW)
> > + return;
> > +
> > + buf->flags = TPM_BUF_TPM2B;
> > + buf->length = 2;
> > + buf->capacity = buf_size - sizeof(*buf);
> > buf->handles = 0;
> > + buf->data[0] = 0;
> > + buf->data[1] = 0;
> > }
> > -EXPORT_SYMBOL_GPL(tpm_buf_reset);
> >
> > /**
> > - * tpm_buf_init_sized() - Allocate and initialize a sized (TPM2B) buffer
> > - * @buf: A @tpm_buf
> > - *
> > - * Return: 0 or -ENOMEM
> > + * tpm_buf_init() - Initialize a TPM command
> > + * @buf: A &tpm_buf
> > + * @buf_size: Size of the buffer.
> > */
> > -int tpm_buf_init_sized(struct tpm_buf *buf)
> > +void tpm_buf_init(struct tpm_buf *buf, u16 buf_size)
> > {
> > - buf->data = (u8 *)__get_free_page(GFP_KERNEL);
> > - if (!buf->data)
> > - return -ENOMEM;
> > + memset(buf, 0, buf_size);
> > + __tpm_buf_reset(buf, buf_size, TPM_TAG_RQU_COMMAND, 0);
> > +}
> > +EXPORT_SYMBOL_GPL(tpm_buf_init);
> >
> > - tpm_buf_reset_sized(buf);
> > - return 0;
> > +/**
> > + * tpm_buf_init_sized() - Initialize a sized buffer
> > + * @buf: A &tpm_buf
> > + * @buf_size: Size of the buffer.
> > + */
> > +void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size)
> > +{
> > + memset(buf, 0, buf_size);
> > + __tpm_buf_reset_sized(buf, buf_size);
> > }
> > EXPORT_SYMBOL_GPL(tpm_buf_init_sized);
> >
> > /**
> > - * tpm_buf_reset_sized() - Initialize a sized buffer
> > + * tpm_buf_reset() - Re-initialize a TPM command
> > * @buf: A &tpm_buf
> > + * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
> > + * @ordinal: A command ordinal
> > */
> > -void tpm_buf_reset_sized(struct tpm_buf *buf)
> > +void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
> > {
> > - buf->flags = TPM_BUF_TPM2B;
> > - buf->length = 2;
> > - buf->data[0] = 0;
> > - buf->data[1] = 0;
> > + u16 buf_size = buf->capacity + sizeof(*buf);
> > +
> > + __tpm_buf_reset(buf, buf_size, tag, ordinal);
> > }
> > -EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
> > +EXPORT_SYMBOL_GPL(tpm_buf_reset);
> >
> > -void tpm_buf_destroy(struct tpm_buf *buf)
> > +/**
> > + * tpm_buf_reset_sized() - Re-initialize a sized buffer
> > + * @buf: A &tpm_buf
> > + */
> > +void tpm_buf_reset_sized(struct tpm_buf *buf)
> > {
> > - free_page((unsigned long)buf->data);
> > + u16 buf_size = buf->capacity + sizeof(*buf);
> > +
> > + __tpm_buf_reset_sized(buf, buf_size);
> > }
> > -EXPORT_SYMBOL_GPL(tpm_buf_destroy);
> > +EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
> >
> > /**
> > * tpm_buf_length() - Return the number of bytes consumed by the data
> > @@ -90,7 +118,7 @@ EXPORT_SYMBOL_GPL(tpm_buf_destroy);
> > *
> > * Return: The number of bytes consumed by the buffer
> > */
> > -u32 tpm_buf_length(struct tpm_buf *buf)
> > +u16 tpm_buf_length(struct tpm_buf *buf)
> > {
> > return buf->length;
> > }
> > @@ -104,11 +132,13 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
> > */
> > void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
> > {
> > + u32 total_length = (u32)buf->length + (u32)new_length;
> > +
> > /* Return silently if overflow has already happened. */
> > if (buf->flags & TPM_BUF_OVERFLOW)
> > return;
> >
> > - if ((buf->length + new_length) > PAGE_SIZE) {
> > + if (total_length > (u32)buf->capacity) {
> > WARN(1, "tpm_buf: write overflow\n");
> > buf->flags |= TPM_BUF_OVERFLOW;
> > return;
> > diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
> > index 94231f052ea7..1de03cf340b3 100644
> > --- a/drivers/char/tpm/tpm-sysfs.c
> > +++ b/drivers/char/tpm/tpm-sysfs.c
> > @@ -32,28 +32,31 @@ struct tpm_readpubek_out {
> > static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
> > char *buf)
> > {
> > - struct tpm_buf tpm_buf;
> > struct tpm_readpubek_out *out;
> > int i;
> > char *str = buf;
> > struct tpm_chip *chip = to_tpm_chip(dev);
> > char anti_replay[20];
> > + struct tpm_buf *tpm_buf __free(kfree) = NULL;
> >
> > memset(&anti_replay, 0, sizeof(anti_replay));
> >
> > if (tpm_try_get_ops(chip))
> > return 0;
> >
> > - if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
> > + tpm_buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!tpm_buf)
> > goto out_ops;
> >
> > - tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
> > + tpm_buf_init(tpm_buf, TPM_BUFSIZE);
> > + tpm_buf_reset(tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK);
> > + tpm_buf_append(tpm_buf, anti_replay, sizeof(anti_replay));
> >
> > - if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
> > + if (tpm_transmit_cmd(chip, tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
> > "attempting to read the PUBEK"))
> > - goto out_buf;
> > + goto out_ops;
> >
> > - out = (struct tpm_readpubek_out *)&tpm_buf.data[10];
> > + out = (struct tpm_readpubek_out *)&tpm_buf->data[10];
> > str +=
> > sprintf(str,
> > "Algorithm: %4ph\n"
> > @@ -71,8 +74,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
> > for (i = 0; i < 256; i += 16)
> > str += sprintf(str, "%16ph\n", &out->modulus[i]);
> >
> > -out_buf:
> > - tpm_buf_destroy(&tpm_buf);
> > out_ops:
> > tpm_put_ops(chip);
> > return str - buf;
> > diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> > index 87d68ddf270a..03f5346343ab 100644
> > --- a/drivers/char/tpm/tpm.h
> > +++ b/drivers/char/tpm/tpm.h
> > @@ -33,7 +33,6 @@
> > #endif
> >
> > #define TPM_MINOR 224 /* officially assigned */
> > -#define TPM_BUFSIZE 4096
> > #define TPM_NUM_DEVICES 65536
> > #define TPM_RETRY 50
> >
> > diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
> > index b49a790f1bd5..6facc3de2c46 100644
> > --- a/drivers/char/tpm/tpm1-cmd.c
> > +++ b/drivers/char/tpm/tpm1-cmd.c
> > @@ -323,20 +323,18 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
> > */
> > static int tpm1_startup(struct tpm_chip *chip)
> > {
> > - struct tpm_buf buf;
> > - int rc;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> >
> > dev_info(&chip->dev, "starting up the TPM manually\n");
> >
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
> > - if (rc < 0)
> > - return rc;
> > -
> > - tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
> > - tpm_buf_destroy(&buf);
> > - return rc;
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
> > + tpm_buf_append_u16(buf, TPM_ST_CLEAR);
> > + return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
> > }
> >
> > int tpm1_get_timeouts(struct tpm_chip *chip)
> > @@ -463,50 +461,47 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
> > int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
> > const char *log_msg)
> > {
> > - struct tpm_buf buf;
> > - int rc;
> > -
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
> > - if (rc)
> > - return rc;
> > -
> > - tpm_buf_append_u32(&buf, pcr_idx);
> > - tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
> > -
> > - rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
> > - tpm_buf_destroy(&buf);
> > - return rc;
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
> > + tpm_buf_append_u32(buf, pcr_idx);
> > + tpm_buf_append(buf, hash, TPM_DIGEST_SIZE);
> > + return tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, log_msg);
> > }
> >
> > #define TPM_ORD_GET_CAP 101
> > ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
> > const char *desc, size_t min_cap_length)
> > {
> > - struct tpm_buf buf;
> > int rc;
> >
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
> > - if (rc)
> > - return rc;
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
> >
> > if (subcap_id == TPM_CAP_VERSION_1_1 ||
> > subcap_id == TPM_CAP_VERSION_1_2) {
> > - tpm_buf_append_u32(&buf, subcap_id);
> > - tpm_buf_append_u32(&buf, 0);
> > + tpm_buf_append_u32(buf, subcap_id);
> > + tpm_buf_append_u32(buf, 0);
> > } else {
> > if (subcap_id == TPM_CAP_FLAG_PERM ||
> > subcap_id == TPM_CAP_FLAG_VOL)
> > - tpm_buf_append_u32(&buf, TPM_CAP_FLAG);
> > + tpm_buf_append_u32(buf, TPM_CAP_FLAG);
> > else
> > - tpm_buf_append_u32(&buf, TPM_CAP_PROP);
> > + tpm_buf_append_u32(buf, TPM_CAP_PROP);
> >
> > - tpm_buf_append_u32(&buf, 4);
> > - tpm_buf_append_u32(&buf, subcap_id);
> > + tpm_buf_append_u32(buf, 4);
> > + tpm_buf_append_u32(buf, subcap_id);
> > }
> > - rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
> > + rc = tpm_transmit_cmd(chip, buf, min_cap_length, desc);
> > if (!rc)
> > - *cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
> > - tpm_buf_destroy(&buf);
> > + *cap = *(cap_t *)&buf->data[TPM_HEADER_SIZE + 4];
> > return rc;
> > }
> > EXPORT_SYMBOL_GPL(tpm1_getcap);
> > @@ -530,21 +525,24 @@ struct tpm1_get_random_out {
> > int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > {
> > struct tpm1_get_random_out *out;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > u32 num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
> > - struct tpm_buf buf;
> > u32 total = 0;
> > int retries = 5;
> > u32 recd;
> > int rc;
> >
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> > - if (rc)
> > - return rc;
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> >
> > do {
> > - tpm_buf_append_u32(&buf, num_bytes);
> > + tpm_buf_append_u32(buf, num_bytes);
> >
> > - rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
> > + rc = tpm_transmit_cmd(chip, buf, sizeof(out->rng_data_len),
> > "attempting get random");
> > if (rc) {
> > if (rc > 0)
> > @@ -552,7 +550,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > goto out;
> > }
> >
> > - out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
> > + out = (struct tpm1_get_random_out *)&buf->data[TPM_HEADER_SIZE];
> >
> > recd = be32_to_cpu(out->rng_data_len);
> > if (recd > num_bytes) {
> > @@ -560,8 +558,8 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > goto out;
> > }
> >
> > - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
> > - sizeof(out->rng_data_len) + recd) {
> > + if (tpm_buf_length(buf) < TPM_HEADER_SIZE +
> > + sizeof(out->rng_data_len) + recd) {
> > rc = -EFAULT;
> > goto out;
> > }
> > @@ -571,41 +569,36 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > total += recd;
> > num_bytes -= recd;
> >
> > - tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> > } while (retries-- && total < max);
> >
> > rc = total ? (int)total : -EIO;
> > out:
> > - tpm_buf_destroy(&buf);
> > return rc;
> > }
> >
> > #define TPM_ORD_PCRREAD 21
> > int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> > {
> > - struct tpm_buf buf;
> > int rc;
> >
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
> > - if (rc)
> > - return rc;
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > - tpm_buf_append_u32(&buf, pcr_idx);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
> > + tpm_buf_append_u32(buf, pcr_idx);
> >
> > - rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE,
> > + rc = tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE,
> > "attempting to read a pcr value");
> > if (rc)
> > - goto out;
> > -
> > - if (tpm_buf_length(&buf) < TPM_DIGEST_SIZE) {
> > - rc = -EFAULT;
> > - goto out;
> > - }
> > + return rc;
> >
> > - memcpy(res_buf, &buf.data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
> > + if (tpm_buf_length(buf) < TPM_DIGEST_SIZE)
> > + return -EFAULT;
> >
> > -out:
> > - tpm_buf_destroy(&buf);
> > + memcpy(res_buf, &buf->data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
> > return rc;
> > }
> >
> > @@ -619,16 +612,13 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> > */
> > static int tpm1_continue_selftest(struct tpm_chip *chip)
> > {
> > - struct tpm_buf buf;
> > - int rc;
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
> > - if (rc)
> > - return rc;
> > -
> > - rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
> > - tpm_buf_destroy(&buf);
> > - return rc;
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
> > + return tpm_transmit_cmd(chip, buf, 0, "continue selftest");
> > }
> >
> > /**
> > @@ -742,22 +732,24 @@ int tpm1_auto_startup(struct tpm_chip *chip)
> > int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> > {
> > u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
> > - struct tpm_buf buf;
> > unsigned int try;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > int rc;
> >
> > -
> > /* for buggy tpm, flush pcrs with extend to selected dummy */
> > if (tpm_suspend_pcr)
> > rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
> > "extending dummy pcr before suspend");
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> >
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> > - if (rc)
> > - return rc;
> > /* now do the actual savestate */
> > for (try = 0; try < TPM_RETRY; try++) {
> > - rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> > + rc = tpm_transmit_cmd(chip, buf, 0, NULL);
> > /*
> > * If the TPM indicates that it is too busy to respond to
> > * this command then retry before giving up. It can take
> > @@ -772,7 +764,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> > break;
> > tpm_msleep(TPM_TIMEOUT_RETRY);
> >
> > - tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> > }
> >
> > if (rc)
> > @@ -782,8 +774,6 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> > dev_warn(&chip->dev, "TPM savestate took %dms\n",
> > try * TPM_TIMEOUT_RETRY);
> >
> > - tpm_buf_destroy(&buf);
> > -
> > return rc;
> > }
> >
> > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> > index 52ee350da867..f619ce390f6d 100644
> > --- a/drivers/char/tpm/tpm2-cmd.c
> > +++ b/drivers/char/tpm/tpm2-cmd.c
> > @@ -119,12 +119,13 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> > {
> > int i;
> > int rc;
> > - struct tpm_buf buf;
> > struct tpm2_pcr_read_out *out;
> > u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
> > u16 digest_size;
> > u16 expected_digest_size = 0;
> >
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > +
> > if (pcr_idx >= TPM2_PLATFORM_PCR)
> > return -EINVAL;
> >
> > @@ -139,36 +140,35 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> > expected_digest_size = chip->allocated_banks[i].digest_size;
> > }
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
> > - if (rc)
> > - return rc;
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
> >
> > pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
> >
> > - tpm_buf_append_u32(&buf, 1);
> > - tpm_buf_append_u16(&buf, digest->alg_id);
> > - tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
> > - tpm_buf_append(&buf, (const unsigned char *)pcr_select,
> > + tpm_buf_append_u32(buf, 1);
> > + tpm_buf_append_u16(buf, digest->alg_id);
> > + tpm_buf_append_u8(buf, TPM2_PCR_SELECT_MIN);
> > + tpm_buf_append(buf, (const unsigned char *)pcr_select,
> > sizeof(pcr_select));
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value");
> > + rc = tpm_transmit_cmd(chip, buf, 0, "attempting to read a pcr value");
> > if (rc)
> > - goto out;
> > + return rc;
> >
> > - out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
> > + out = (struct tpm2_pcr_read_out *)&buf->data[TPM_HEADER_SIZE];
> > digest_size = be16_to_cpu(out->digest_size);
> > if (digest_size > sizeof(digest->digest) ||
> > - (!digest_size_ptr && digest_size != expected_digest_size)) {
> > - rc = -EINVAL;
> > - goto out;
> > - }
> > + (!digest_size_ptr && digest_size != expected_digest_size))
> > + return -EINVAL;
> >
> > if (digest_size_ptr)
> > *digest_size_ptr = digest_size;
> >
> > memcpy(digest->digest, out->digest, digest_size);
> > -out:
> > - tpm_buf_destroy(&buf);
> > return rc;
> > }
> >
> > @@ -184,56 +184,54 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> > int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > struct tpm_digest *digests)
> > {
> > - struct tpm_buf buf;
> > int rc;
> > int i;
> >
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > +
> > if (!disable_pcr_integrity) {
> > rc = tpm2_start_auth_session(chip);
> > if (rc)
> > return rc;
> > }
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
> > - if (rc) {
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > if (!disable_pcr_integrity)
> > tpm2_end_auth_session(chip);
> > - return rc;
> > + return -ENOMEM;
> > }
> >
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
> > +
> > if (!disable_pcr_integrity) {
> > - rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
> > - if (rc) {
> > - tpm_buf_destroy(&buf);
> > + rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
> > + if (rc)
> > return rc;
> > - }
> > - tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
> > + tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
> > } else {
> > - tpm_buf_append_handle(chip, &buf, pcr_idx);
> > - tpm_buf_append_auth(chip, &buf, NULL, 0);
> > + tpm_buf_append_handle(chip, buf, pcr_idx);
> > + tpm_buf_append_auth(chip, buf, NULL, 0);
> > }
> >
> > - tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
> > + tpm_buf_append_u32(buf, chip->nr_allocated_banks);
> >
> > for (i = 0; i < chip->nr_allocated_banks; i++) {
> > - tpm_buf_append_u16(&buf, digests[i].alg_id);
> > - tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
> > + tpm_buf_append_u16(buf, digests[i].alg_id);
> > + tpm_buf_append(buf, (const unsigned char *)&digests[i].digest,
> > chip->allocated_banks[i].digest_size);
> > }
> >
> > if (!disable_pcr_integrity) {
> > - rc = tpm_buf_fill_hmac_session(chip, &buf);
> > - if (rc) {
> > - tpm_buf_destroy(&buf);
> > + rc = tpm_buf_fill_hmac_session(chip, buf);
> > + if (rc)
> > return rc;
> > - }
> > }
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
> > + rc = tpm_transmit_cmd(chip, buf, 0, "attempting extend a PCR value");
> > if (!disable_pcr_integrity)
> > - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> > -
> > - tpm_buf_destroy(&buf);
> > + rc = tpm_buf_check_hmac_response(chip, buf, rc);
> >
> > return rc;
> > }
> > @@ -258,7 +256,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > {
> > struct tpm2_get_random_out *out;
> > struct tpm_header *head;
> > - struct tpm_buf buf;
> > u32 recd;
> > u32 num_bytes = max;
> > int err;
> > @@ -267,6 +264,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > u8 *dest_ptr = dest;
> > off_t offset;
> >
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > +
> > if (!num_bytes || max > TPM_MAX_RNG_DATA)
> > return -EINVAL;
> >
> > @@ -274,50 +273,52 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > if (err)
> > return err;
> >
> > - err = tpm_buf_init(&buf, 0, 0);
> > - if (err) {
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > tpm2_end_auth_session(chip);
> > - return err;
> > + return -ENOMEM;
> > }
> >
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > +
> > do {
> > - tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
> > + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
> > if (tpm2_chip_auth(chip)) {
> > - tpm_buf_append_hmac_session(chip, &buf,
> > + tpm_buf_append_hmac_session(chip, buf,
> > TPM2_SA_ENCRYPT |
> > TPM2_SA_CONTINUE_SESSION,
> > NULL, 0);
> > } else {
> > - offset = buf.handles * 4 + TPM_HEADER_SIZE;
> > - head = (struct tpm_header *)buf.data;
> > - if (tpm_buf_length(&buf) == offset)
> > + offset = buf->handles * 4 + TPM_HEADER_SIZE;
> > + head = (struct tpm_header *)buf->data;
> > + if (tpm_buf_length(buf) == offset)
> > head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> > }
> > - tpm_buf_append_u16(&buf, num_bytes);
> > - err = tpm_buf_fill_hmac_session(chip, &buf);
> > + tpm_buf_append_u16(buf, num_bytes);
> > + err = tpm_buf_fill_hmac_session(chip, buf);
> > if (err)
> > goto out;
> >
> > - err = tpm_transmit_cmd(chip, &buf,
> > + err = tpm_transmit_cmd(chip, buf,
> > offsetof(struct tpm2_get_random_out,
> > buffer),
> > "attempting get random");
> > - err = tpm_buf_check_hmac_response(chip, &buf, err);
> > + err = tpm_buf_check_hmac_response(chip, buf, err);
> > if (err) {
> > if (err > 0)
> > err = -EIO;
> > goto out;
> > }
> >
> > - head = (struct tpm_header *)buf.data;
> > + head = (struct tpm_header *)buf->data;
> > offset = TPM_HEADER_SIZE;
> > /* Skip the parameter size field: */
> > if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
> > offset += 4;
> >
> > - out = (struct tpm2_get_random_out *)&buf.data[offset];
> > + out = (struct tpm2_get_random_out *)&buf->data[offset];
> > recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
> > - if (tpm_buf_length(&buf) <
> > + if (tpm_buf_length(buf) <
> > TPM_HEADER_SIZE +
> > offsetof(struct tpm2_get_random_out, buffer) +
> > recd) {
> > @@ -331,11 +332,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > num_bytes -= recd;
> > } while (retries-- && total < max);
> >
> > - tpm_buf_destroy(&buf);
> > -
> > return total ? total : -EIO;
> > out:
> > - tpm_buf_destroy(&buf);
> > tpm2_end_auth_session(chip);
> > return err;
> > }
> > @@ -347,20 +345,18 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > */
> > void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
> > {
> > - struct tpm_buf buf;
> > - int rc;
> > -
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
> > - if (rc) {
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
> > handle);
> > return;
> > }
> >
> > - tpm_buf_append_u32(&buf, handle);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
> > + tpm_buf_append_u32(buf, handle);
> >
> > - tpm_transmit_cmd(chip, &buf, 0, "flushing context");
> > - tpm_buf_destroy(&buf);
> > + tpm_transmit_cmd(chip, buf, 0, "flushing context");
> > }
> > EXPORT_SYMBOL_GPL(tpm2_flush_context);
> >
> > @@ -387,19 +383,21 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
> > const char *desc)
> > {
> > struct tpm2_get_cap_out *out;
> > - struct tpm_buf buf;
> > int rc;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > - if (rc)
> > - return rc;
> > - tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
> > - tpm_buf_append_u32(&buf, property_id);
> > - tpm_buf_append_u32(&buf, 1);
> > - rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > + tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
> > + tpm_buf_append_u32(buf, property_id);
> > + tpm_buf_append_u32(buf, 1);
> > + rc = tpm_transmit_cmd(chip, buf, 0, NULL);
> > if (!rc) {
> > out = (struct tpm2_get_cap_out *)
> > - &buf.data[TPM_HEADER_SIZE];
> > + &buf->data[TPM_HEADER_SIZE];
> > /*
> > * To prevent failing boot up of some systems, Infineon TPM2.0
> > * returns SUCCESS on TPM2_Startup in field upgrade mode. Also
> > @@ -411,7 +409,6 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
> > else
> > rc = -ENODATA;
> > }
> > - tpm_buf_destroy(&buf);
> > return rc;
> > }
> > EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
> > @@ -428,15 +425,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
> > */
> > void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
> > {
> > - struct tpm_buf buf;
> > - int rc;
> > -
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
> > - if (rc)
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > return;
> > - tpm_buf_append_u16(&buf, shutdown_type);
> > - tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
> > - tpm_buf_destroy(&buf);
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
> > + tpm_buf_append_u16(buf, shutdown_type);
> > + tpm_transmit_cmd(chip, buf, 0, "stopping the TPM");
> > }
> >
> > /**
> > @@ -454,20 +450,21 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
> > */
> > static int tpm2_do_selftest(struct tpm_chip *chip)
> > {
> > - struct tpm_buf buf;
> > int full;
> > int rc;
> >
> > for (full = 0; full < 2; full++) {
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
> > - if (rc)
> > - return rc;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> >
> > - tpm_buf_append_u8(&buf, full);
> > - rc = tpm_transmit_cmd(chip, &buf, 0,
> > - "attempting the self test");
> > - tpm_buf_destroy(&buf);
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
> > + tpm_buf_append_u8(buf, full);
> > + rc = tpm_transmit_cmd(chip, buf, 0,
> > + "attempting the self test");
> > if (rc == TPM2_RC_TESTING)
> > rc = TPM2_RC_SUCCESS;
> > if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
> > @@ -492,23 +489,24 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
> > int tpm2_probe(struct tpm_chip *chip)
> > {
> > struct tpm_header *out;
> > - struct tpm_buf buf;
> > int rc;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > - if (rc)
> > - return rc;
> > - tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
> > - tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
> > - tpm_buf_append_u32(&buf, 1);
> > - rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > + tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
> > + tpm_buf_append_u32(buf, TPM_PT_TOTAL_COMMANDS);
> > + tpm_buf_append_u32(buf, 1);
> > + rc = tpm_transmit_cmd(chip, buf, 0, NULL);
> > /* We ignore TPM return codes on purpose. */
> > if (rc >= 0) {
> > - out = (struct tpm_header *)buf.data;
> > + out = (struct tpm_header *)buf->data;
> > if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
> > chip->flags |= TPM_CHIP_FLAG_TPM2;
> > }
> > - tpm_buf_destroy(&buf);
> > return 0;
> > }
> > EXPORT_SYMBOL_GPL(tpm2_probe);
> > @@ -548,7 +546,6 @@ struct tpm2_pcr_selection {
> > ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> > {
> > struct tpm2_pcr_selection pcr_selection;
> > - struct tpm_buf buf;
> > void *marker;
> > void *end;
> > void *pcr_select_offset;
> > @@ -560,20 +557,22 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> > int rc;
> > int i = 0;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > - if (rc)
> > - return rc;
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > - tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
> > - tpm_buf_append_u32(&buf, 0);
> > - tpm_buf_append_u32(&buf, 1);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > + tpm_buf_append_u32(buf, TPM2_CAP_PCRS);
> > + tpm_buf_append_u32(buf, 0);
> > + tpm_buf_append_u32(buf, 1);
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation");
> > + rc = tpm_transmit_cmd(chip, buf, 9, "get tpm pcr allocation");
> > if (rc)
> > goto out;
> >
> > nr_possible_banks = be32_to_cpup(
> > - (__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
> > + (__be32 *)&buf->data[TPM_HEADER_SIZE + 5]);
> > if (nr_possible_banks > TPM2_MAX_PCR_BANKS) {
> > pr_err("tpm: out of bank capacity: %u > %u\n",
> > nr_possible_banks, TPM2_MAX_PCR_BANKS);
> > @@ -581,10 +580,10 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> > goto out;
> > }
> >
> > - marker = &buf.data[TPM_HEADER_SIZE + 9];
> > + marker = &buf->data[TPM_HEADER_SIZE + 9];
> >
> > - rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
> > - end = &buf.data[rsp_len];
> > + rsp_len = be32_to_cpup((__be32 *)&buf->data[2]);
> > + end = &buf->data[rsp_len];
> >
> > for (i = 0; i < nr_possible_banks; i++) {
> > pcr_select_offset = marker +
> > @@ -617,20 +616,19 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> >
> > chip->nr_allocated_banks = nr_alloc_banks;
> > out:
> > - tpm_buf_destroy(&buf);
> > -
> > return rc;
> > }
> >
> > int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> > {
> > - struct tpm_buf buf;
> > u32 nr_commands;
> > __be32 *attrs;
> > u32 cc;
> > int i;
> > int rc;
> >
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > +
> > rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
> > if (rc)
> > goto out;
> > @@ -647,30 +645,31 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> > goto out;
> > }
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > - if (rc)
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > + rc = -ENOMEM;
> > goto out;
> > + }
> >
> > - tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
> > - tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
> > - tpm_buf_append_u32(&buf, nr_commands);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> > + tpm_buf_append_u32(buf, TPM2_CAP_COMMANDS);
> > + tpm_buf_append_u32(buf, TPM2_CC_FIRST);
> > + tpm_buf_append_u32(buf, nr_commands);
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
> > - if (rc) {
> > - tpm_buf_destroy(&buf);
> > + rc = tpm_transmit_cmd(chip, buf, 9 + 4 * nr_commands, NULL);
> > + if (rc)
> > goto out;
> > - }
> >
> > if (nr_commands !=
> > - be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
> > + be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE + 5])) {
> > rc = -EFAULT;
> > - tpm_buf_destroy(&buf);
> > goto out;
> > }
> >
> > chip->nr_commands = nr_commands;
> >
> > - attrs = (__be32 *)&buf.data[TPM_HEADER_SIZE + 9];
> > + attrs = (__be32 *)&buf->data[TPM_HEADER_SIZE + 9];
> > for (i = 0; i < nr_commands; i++, attrs++) {
> > chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
> > cc = chip->cc_attrs_tbl[i] & 0xFFFF;
> > @@ -682,8 +681,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> > }
> > }
> >
> > - tpm_buf_destroy(&buf);
> > -
> > out:
> > if (rc > 0)
> > rc = -ENODEV;
> > @@ -704,20 +701,18 @@ EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);
> >
> > static int tpm2_startup(struct tpm_chip *chip)
> > {
> > - struct tpm_buf buf;
> > - int rc;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> >
> > dev_info(&chip->dev, "starting up the TPM manually\n");
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
> > - if (rc < 0)
> > - return rc;
> > -
> > - tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
> > - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
> > - tpm_buf_destroy(&buf);
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > - return rc;
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
> > + tpm_buf_append_u16(buf, TPM2_SU_CLEAR);
> > + return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
> > }
> >
> > /**
> > diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> > index 795cd99dc6fe..b6a93db5a5ee 100644
> > --- a/drivers/char/tpm/tpm2-sessions.c
> > +++ b/drivers/char/tpm/tpm2-sessions.c
> > @@ -167,8 +167,8 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
> > {
> > u32 mso = tpm2_handle_mso(handle);
> > off_t offset = TPM_HEADER_SIZE;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > int rc, name_size_alg;
> > - struct tpm_buf buf;
> >
> > if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
> > mso != TPM2_MSO_NVRAM) {
> > @@ -176,50 +176,40 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
> > return sizeof(u32);
> > }
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
> > - if (rc)
> > - return rc;
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > - tpm_buf_append_u32(&buf, handle);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
> > + tpm_buf_append_u32(buf, handle);
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
> > - if (rc) {
> > - tpm_buf_destroy(&buf);
> > + rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
> > + if (rc)
> > return tpm_ret_to_err(rc);
> > - }
> >
> > /* Skip TPMT_PUBLIC: */
> > - offset += tpm_buf_read_u16(&buf, &offset);
> > + offset += tpm_buf_read_u16(buf, &offset);
> >
> > /*
> > * Ensure space for the length field of TPM2B_NAME and hashAlg field of
> > * TPMT_HA (the extra four bytes).
> > */
> > - if (offset + 4 > tpm_buf_length(&buf)) {
> > - tpm_buf_destroy(&buf);
> > + if (offset + 4 > tpm_buf_length(buf))
> > return -EIO;
> > - }
> > -
> > - rc = tpm_buf_read_u16(&buf, &offset);
> > - name_size_alg = name_size(&buf.data[offset]);
> >
> > - if (name_size_alg < 0) {
> > - tpm_buf_destroy(&buf);
> > + rc = tpm_buf_read_u16(buf, &offset);
> > + name_size_alg = name_size(&buf->data[offset]);
> > + if (name_size_alg < 0)
> > return name_size_alg;
> > - }
> >
> > - if (rc != name_size_alg) {
> > - tpm_buf_destroy(&buf);
> > + if (rc != name_size_alg)
> > return -EIO;
> > - }
> >
> > - if (offset + rc > tpm_buf_length(&buf)) {
> > - tpm_buf_destroy(&buf);
> > + if (offset + rc > tpm_buf_length(buf))
> > return -EIO;
> > - }
> >
> > - memcpy(name, &buf.data[offset], rc);
> > - tpm_buf_destroy(&buf);
> > + memcpy(name, &buf->data[offset], rc);
> > return name_size_alg;
> > }
> > #endif /* CONFIG_TCG_TPM2_HMAC */
> > @@ -987,8 +977,8 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
> > */
> > int tpm2_start_auth_session(struct tpm_chip *chip)
> > {
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > struct tpm2_auth *auth;
> > - struct tpm_buf buf;
> > u32 null_key;
> > int rc;
> >
> > @@ -1007,41 +997,43 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
> >
> > auth->session = TPM_HEADER_SIZE;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
> > - if (rc)
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > + rc = -ENOMEM;
> > goto out;
> > + }
> >
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
> > /* salt key handle */
> > - tpm_buf_append_u32(&buf, null_key);
> > + tpm_buf_append_u32(buf, null_key);
> > /* bind key handle */
> > - tpm_buf_append_u32(&buf, TPM2_RH_NULL);
> > + tpm_buf_append_u32(buf, TPM2_RH_NULL);
> > /* nonce caller */
> > get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
> > - tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
> > - tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
> > + tpm_buf_append_u16(buf, sizeof(auth->our_nonce));
> > + tpm_buf_append(buf, auth->our_nonce, sizeof(auth->our_nonce));
> >
> > /* append encrypted salt and squirrel away unencrypted in auth */
> > - tpm_buf_append_salt(&buf, chip, auth);
> > + tpm_buf_append_salt(buf, chip, auth);
> > /* session type (HMAC, audit or policy) */
> > - tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
> > + tpm_buf_append_u8(buf, TPM2_SE_HMAC);
> >
> > /* symmetric encryption parameters */
> > /* symmetric algorithm */
> > - tpm_buf_append_u16(&buf, TPM_ALG_AES);
> > + tpm_buf_append_u16(buf, TPM_ALG_AES);
> > /* bits for symmetric algorithm */
> > - tpm_buf_append_u16(&buf, AES_KEY_BITS);
> > + tpm_buf_append_u16(buf, AES_KEY_BITS);
> > /* symmetric algorithm mode (must be CFB) */
> > - tpm_buf_append_u16(&buf, TPM_ALG_CFB);
> > + tpm_buf_append_u16(buf, TPM_ALG_CFB);
> > /* hash algorithm for session */
> > - tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
> > + tpm_buf_append_u16(buf, TPM_ALG_SHA256);
> >
> > - rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession"));
> > + rc = tpm_ret_to_err(tpm_transmit_cmd(chip, buf, 0, "StartAuthSession"));
> > tpm2_flush_context(chip, null_key);
> >
> > if (rc == TPM2_RC_SUCCESS)
> > - rc = tpm2_parse_start_auth_session(auth, &buf);
> > -
> > - tpm_buf_destroy(&buf);
> > + rc = tpm2_parse_start_auth_session(auth, buf);
> >
> > if (rc == TPM2_RC_SUCCESS) {
> > chip->auth = auth;
> > @@ -1262,19 +1254,21 @@ static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
> > static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
> > u32 *handle, u8 *name)
> > {
> > + struct tpm_buf *template __free(kfree) = NULL;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > int rc;
> > - struct tpm_buf buf;
> > - struct tpm_buf template;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
> > - if (rc)
> > - return rc;
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> >
> > - rc = tpm_buf_init_sized(&template);
> > - if (rc) {
> > - tpm_buf_destroy(&buf);
> > - return rc;
> > - }
> > + template = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!template)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
> > + tpm_buf_init_sized(template, TPM_BUFSIZE);
> >
> > /*
> > * create the template. Note: in order for userspace to
> > @@ -1286,75 +1280,72 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
> > */
> >
> > /* key type */
> > - tpm_buf_append_u16(&template, TPM_ALG_ECC);
> > + tpm_buf_append_u16(template, TPM_ALG_ECC);
> >
> > /* name algorithm */
> > - tpm_buf_append_u16(&template, TPM_ALG_SHA256);
> > + tpm_buf_append_u16(template, TPM_ALG_SHA256);
> >
> > /* object properties */
> > - tpm_buf_append_u32(&template, TPM2_OA_NULL_KEY);
> > + tpm_buf_append_u32(template, TPM2_OA_NULL_KEY);
> >
> > /* sauth policy (empty) */
> > - tpm_buf_append_u16(&template, 0);
> > + tpm_buf_append_u16(template, 0);
> >
> > /* BEGIN parameters: key specific; for ECC*/
> >
> > /* symmetric algorithm */
> > - tpm_buf_append_u16(&template, TPM_ALG_AES);
> > + tpm_buf_append_u16(template, TPM_ALG_AES);
> >
> > /* bits for symmetric algorithm */
> > - tpm_buf_append_u16(&template, AES_KEY_BITS);
> > + tpm_buf_append_u16(template, AES_KEY_BITS);
> >
> > /* algorithm mode (must be CFB) */
> > - tpm_buf_append_u16(&template, TPM_ALG_CFB);
> > + tpm_buf_append_u16(template, TPM_ALG_CFB);
> >
> > /* scheme (NULL means any scheme) */
> > - tpm_buf_append_u16(&template, TPM_ALG_NULL);
> > + tpm_buf_append_u16(template, TPM_ALG_NULL);
> >
> > /* ECC Curve ID */
> > - tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
> > + tpm_buf_append_u16(template, TPM2_ECC_NIST_P256);
> >
> > /* KDF Scheme */
> > - tpm_buf_append_u16(&template, TPM_ALG_NULL);
> > + tpm_buf_append_u16(template, TPM_ALG_NULL);
> >
> > /* unique: key specific; for ECC it is two zero size points */
> > - tpm_buf_append_u16(&template, 0);
> > - tpm_buf_append_u16(&template, 0);
> > + tpm_buf_append_u16(template, 0);
> > + tpm_buf_append_u16(template, 0);
> >
> > /* END parameters */
> >
> > /* primary handle */
> > - tpm_buf_append_u32(&buf, hierarchy);
> > - tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
> > + tpm_buf_append_u32(buf, hierarchy);
> > + tpm_buf_append_empty_auth(buf, TPM2_RS_PW);
> >
> > /* sensitive create size is 4 for two empty buffers */
> > - tpm_buf_append_u16(&buf, 4);
> > + tpm_buf_append_u16(buf, 4);
> >
> > /* sensitive create auth data (empty) */
> > - tpm_buf_append_u16(&buf, 0);
> > + tpm_buf_append_u16(buf, 0);
> >
> > /* sensitive create sensitive data (empty) */
> > - tpm_buf_append_u16(&buf, 0);
> > + tpm_buf_append_u16(buf, 0);
> >
> > /* the public template */
> > - tpm_buf_append(&buf, template.data, template.length);
> > - tpm_buf_destroy(&template);
> > + tpm_buf_append(buf, template->data, template->length);
> >
> > /* outside info (empty) */
> > - tpm_buf_append_u16(&buf, 0);
> > + tpm_buf_append_u16(buf, 0);
> >
> > /* creation PCR (none) */
> > - tpm_buf_append_u32(&buf, 0);
> > + tpm_buf_append_u32(buf, 0);
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 0,
> > + rc = tpm_transmit_cmd(chip, buf, 0,
> > "attempting to create NULL primary");
> >
> > if (rc == TPM2_RC_SUCCESS)
> > - rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy,
> > + rc = tpm2_parse_create_primary(chip, buf, handle, hierarchy,
> > name);
> >
> > - tpm_buf_destroy(&buf);
> > -
> > return rc;
> > }
> >
> > diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
> > index 60354cd53b5c..cbf86ff5931f 100644
> > --- a/drivers/char/tpm/tpm2-space.c
> > +++ b/drivers/char/tpm/tpm2-space.c
> > @@ -71,24 +71,25 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
> > int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
> > unsigned int *offset, u32 *handle)
> > {
> > - struct tpm_buf tbuf;
> > struct tpm2_context *ctx;
> > unsigned int body_size;
> > int rc;
> >
> > - rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
> > - if (rc)
> > - return rc;
> > + struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!tbuf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(tbuf, TPM_BUFSIZE);
> > + tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
> >
> > ctx = (struct tpm2_context *)&buf[*offset];
> > body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
> > - tpm_buf_append(&tbuf, &buf[*offset], body_size);
> > + tpm_buf_append(tbuf, &buf[*offset], body_size);
> >
> > - rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
> > + rc = tpm_transmit_cmd(chip, tbuf, 4, NULL);
> > if (rc < 0) {
> > dev_warn(&chip->dev, "%s: failed with a system error %d\n",
> > __func__, rc);
> > - tpm_buf_destroy(&tbuf);
> > return -EFAULT;
> > } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
> > rc == TPM2_RC_REFERENCE_H0) {
> > @@ -103,64 +104,55 @@ int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
> > * flushed outside the space
> > */
> > *handle = 0;
> > - tpm_buf_destroy(&tbuf);
> > return -ENOENT;
> > } else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {
> > - tpm_buf_destroy(&tbuf);
> > return -EINVAL;
> > } else if (rc > 0) {
> > dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
> > __func__, rc);
> > - tpm_buf_destroy(&tbuf);
> > return -EFAULT;
> > }
> >
> > - *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
> > + *handle = be32_to_cpup((__be32 *)&tbuf->data[TPM_HEADER_SIZE]);
> > *offset += body_size;
> > -
> > - tpm_buf_destroy(&tbuf);
> > return 0;
> > }
> >
> > int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
> > unsigned int buf_size, unsigned int *offset)
> > {
> > - struct tpm_buf tbuf;
> > unsigned int body_size;
> > int rc;
> >
> > - rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
> > - if (rc)
> > - return rc;
> > + struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!tbuf)
> > + return -ENOMEM;
> >
> > - tpm_buf_append_u32(&tbuf, handle);
> > + tpm_buf_init(tbuf, TPM_BUFSIZE);
> > + tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
> > + tpm_buf_append_u32(tbuf, handle);
> >
> > - rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
> > + rc = tpm_transmit_cmd(chip, tbuf, 0, NULL);
> > if (rc < 0) {
> > dev_warn(&chip->dev, "%s: failed with a system error %d\n",
> > __func__, rc);
> > - tpm_buf_destroy(&tbuf);
> > return -EFAULT;
> > } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
> > - tpm_buf_destroy(&tbuf);
> > return -ENOENT;
> > } else if (rc) {
> > dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
> > __func__, rc);
> > - tpm_buf_destroy(&tbuf);
> > return -EFAULT;
> > }
> >
> > - body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
> > + body_size = tpm_buf_length(tbuf) - TPM_HEADER_SIZE;
> > if ((*offset + body_size) > buf_size) {
> > dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
> > - tpm_buf_destroy(&tbuf);
> > return -ENOMEM;
> > }
> >
> > - memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
> > + memcpy(&buf[*offset], &tbuf->data[TPM_HEADER_SIZE], body_size);
> > *offset += body_size;
> > - tpm_buf_destroy(&tbuf);
> > return 0;
> > }
> >
> > diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
> > index 7bb0f4d4a2ed..b81fd2a537df 100644
> > --- a/drivers/char/tpm/tpm_vtpm_proxy.c
> > +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
> > @@ -395,40 +395,36 @@ static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
> >
> > static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
> > {
> > - struct tpm_buf buf;
> > int rc;
> > const struct tpm_header *header;
> > struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
> >
> > + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > if (chip->flags & TPM_CHIP_FLAG_TPM2)
> > - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
> > - TPM2_CC_SET_LOCALITY);
> > + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_SET_LOCALITY);
> > else
> > - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
> > - TPM_ORD_SET_LOCALITY);
> > - if (rc)
> > - return rc;
> > - tpm_buf_append_u8(&buf, locality);
> > + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SET_LOCALITY);
> > +
> > + tpm_buf_append_u8(buf, locality);
> >
> > proxy_dev->state |= STATE_DRIVER_COMMAND;
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
> > + rc = tpm_transmit_cmd(chip, buf, 0, "attempting to set locality");
> >
> > proxy_dev->state &= ~STATE_DRIVER_COMMAND;
> >
> > - if (rc < 0) {
> > - locality = rc;
> > - goto out;
> > - }
> > + if (rc < 0)
> > + return rc;
> >
> > - header = (const struct tpm_header *)buf.data;
> > + header = (const struct tpm_header *)buf->data;
> > rc = be32_to_cpu(header->return_code);
> > if (rc)
> > locality = -1;
> >
> > -out:
> > - tpm_buf_destroy(&buf);
> > -
> > return locality;
> > }
> >
> > diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> > index 202da079d500..14d75c1482d6 100644
> > --- a/include/linux/tpm.h
> > +++ b/include/linux/tpm.h
> > @@ -26,6 +26,7 @@
> > #include <crypto/aes.h>
> >
> > #define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
> > +#define TPM_BUFSIZE 4096
> >
> > #define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
> > #define TPM2_MAX_PCR_BANKS 8
> > @@ -378,13 +379,15 @@ enum tpm_buf_flags {
> > };
> >
> > /*
> > - * A string buffer type for constructing TPM commands.
> > + * A buffer for constructing and parsing TPM commands, responses and sized
> > + * (TPM2B) buffers.
> > */
> > struct tpm_buf {
> > - u32 flags;
> > - u32 length;
> > - u8 *data;
> > + u8 flags;
> > + u16 length;
> > + u16 capacity;
> > u8 handles;
> > + u8 data[];
> > };
> >
> > enum tpm2_object_attributes {
> > @@ -415,12 +418,11 @@ struct tpm2_hash {
> > unsigned int tpm_id;
> > };
> >
> > -int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal);
> > +void tpm_buf_init(struct tpm_buf *buf, u16 buf_size);
> > +void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size);
> > void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal);
> > -int tpm_buf_init_sized(struct tpm_buf *buf);
> > void tpm_buf_reset_sized(struct tpm_buf *buf);
> > -void tpm_buf_destroy(struct tpm_buf *buf);
> > -u32 tpm_buf_length(struct tpm_buf *buf);
> > +u16 tpm_buf_length(struct tpm_buf *buf);
> > void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length);
> > void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value);
> > void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value);
> > diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> > index 13513819991e..6e03fa7227e4 100644
> > --- a/security/keys/trusted-keys/trusted_tpm1.c
> > +++ b/security/keys/trusted-keys/trusted_tpm1.c
> > @@ -317,9 +317,8 @@ static int TSS_checkhmac2(unsigned char *buffer,
> > * For key specific tpm requests, we will generate and send our
> > * own TPM command packets using the drivers send function.
> > */
> > -static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
> > +static int trusted_tpm_send(struct tpm_buf *buf)
> > {
> > - struct tpm_buf buf;
> > int rc;
> >
> > if (!chip)
> > @@ -329,12 +328,9 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
> > if (rc)
> > return rc;
> >
> > - buf.flags = 0;
> > - buf.length = buflen;
> > - buf.data = cmd;
> > - dump_tpm_buf(cmd);
> > - rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
> > - dump_tpm_buf(cmd);
> > + dump_tpm_buf(buf->data);
> > + rc = tpm_transmit_cmd(chip, buf, 4, "sending data");
> > + dump_tpm_buf(buf->data);
> >
> > if (rc > 0)
> > /* TPM error */
> > @@ -380,7 +376,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
> > tpm_buf_append_u32(tb, handle);
> > tpm_buf_append(tb, ononce, TPM_NONCE_SIZE);
> >
> > - ret = trusted_tpm_send(tb->data, tb->length);
> > + ret = trusted_tpm_send(tb);
> > if (ret < 0)
> > return ret;
> >
> > @@ -404,7 +400,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
> > return -ENODEV;
> >
> > tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP);
> > - ret = trusted_tpm_send(tb->data, tb->length);
> > + ret = trusted_tpm_send(tb);
> > if (ret < 0)
> > return ret;
> >
> > @@ -513,7 +509,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
> > tpm_buf_append_u8(tb, cont);
> > tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE);
> >
> > - ret = trusted_tpm_send(tb->data, tb->length);
> > + ret = trusted_tpm_send(tb);
> > if (ret < 0)
> > goto out;
> >
> > @@ -604,7 +600,7 @@ static int tpm_unseal(struct tpm_buf *tb,
> > tpm_buf_append_u8(tb, cont);
> > tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE);
> >
> > - ret = trusted_tpm_send(tb->data, tb->length);
> > + ret = trusted_tpm_send(tb);
> > if (ret < 0) {
> > pr_info("authhmac failed (%d)\n", ret);
> > return ret;
> > @@ -631,23 +627,23 @@ static int tpm_unseal(struct tpm_buf *tb,
> > static int key_seal(struct trusted_key_payload *p,
> > struct trusted_key_options *o)
> > {
> > - struct tpm_buf tb;
> > int ret;
> >
> > - ret = tpm_buf_init(&tb, 0, 0);
> > - if (ret)
> > - return ret;
> > + struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!tb)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(tb, TPM_BUFSIZE);
> >
> > /* 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, o->keyhandle, o->keyauth,
> > p->key, p->key_len + 1, p->blob, &p->blob_len,
> > o->blobauth, o->pcrinfo, o->pcrinfo_len);
> > if (ret < 0)
> > pr_info("srkseal failed (%d)\n", ret);
> >
> > - tpm_buf_destroy(&tb);
> > return ret;
> > }
> >
> > @@ -657,14 +653,15 @@ static int key_seal(struct trusted_key_payload *p,
> > static int key_unseal(struct trusted_key_payload *p,
> > struct trusted_key_options *o)
> > {
> > - struct tpm_buf tb;
> > int ret;
> >
> > - ret = tpm_buf_init(&tb, 0, 0);
> > - if (ret)
> > - return ret;
> > + struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!tb)
> > + return -ENOMEM;
> > +
> > + tpm_buf_init(tb, TPM_BUFSIZE);
> >
> > - ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
> > + ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
> > o->blobauth, p->key, &p->key_len);
> > if (ret < 0)
> > pr_info("srkunseal failed (%d)\n", ret);
> > @@ -672,7 +669,6 @@ static int key_unseal(struct trusted_key_payload *p,
> > /* pull migratable flag out of sealed key */
> > p->migratable = p->key[--p->key_len];
> >
> > - tpm_buf_destroy(&tb);
> > return ret;
> > }
> >
> > diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> > index 6340823f8b53..6f5c34b885fb 100644
> > --- a/security/keys/trusted-keys/trusted_tpm2.c
> > +++ b/security/keys/trusted-keys/trusted_tpm2.c
> > @@ -234,7 +234,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> > struct trusted_key_options *options)
> > {
> > off_t offset = TPM_HEADER_SIZE;
> > - struct tpm_buf buf, sized;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > + struct tpm_buf *sized __free(kfree) = NULL;
> > int blob_len = 0;
> > int hash;
> > u32 flags;
> > @@ -255,97 +256,100 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> > if (rc)
> > goto out_put;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
> > - if (rc) {
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > + rc = -ENOMEM;
> > tpm2_end_auth_session(chip);
> > goto out_put;
> > }
> >
> > - rc = tpm_buf_init_sized(&sized);
> > - if (rc) {
> > - tpm_buf_destroy(&buf);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
> > +
> > + sized = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!sized) {
> > + rc = -ENOMEM;
> > tpm2_end_auth_session(chip);
> > goto out_put;
> > }
> >
> > - rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > + tpm_buf_init_sized(sized, TPM_BUFSIZE);
> > +
> > + rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
> > if (rc)
> > goto out;
> >
> > - tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> > + tpm_buf_append_hmac_session(chip, buf, TPM2_SA_DECRYPT,
> > options->keyauth, TPM_DIGEST_SIZE);
> >
> > /* sensitive */
> > - tpm_buf_append_u16(&sized, options->blobauth_len);
> > + tpm_buf_append_u16(sized, options->blobauth_len);
> >
> > if (options->blobauth_len)
> > - tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
> > + tpm_buf_append(sized, options->blobauth, options->blobauth_len);
> >
> > - tpm_buf_append_u16(&sized, payload->key_len);
> > - tpm_buf_append(&sized, payload->key, payload->key_len);
> > - tpm_buf_append(&buf, sized.data, sized.length);
> > + tpm_buf_append_u16(sized, payload->key_len);
> > + tpm_buf_append(sized, payload->key, payload->key_len);
> > + tpm_buf_append(buf, sized->data, sized->length);
> >
> > /* public */
> > - tpm_buf_reset_sized(&sized);
> > - tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH);
> > - tpm_buf_append_u16(&sized, hash);
> > + tpm_buf_reset_sized(sized);
> > + tpm_buf_append_u16(sized, TPM_ALG_KEYEDHASH);
> > + tpm_buf_append_u16(sized, hash);
> >
> > /* key properties */
> > flags = 0;
> > flags |= options->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);
> > + tpm_buf_append_u32(sized, flags);
> >
> > /* policy */
> > - tpm_buf_append_u16(&sized, options->policydigest_len);
> > + tpm_buf_append_u16(sized, options->policydigest_len);
> > if (options->policydigest_len)
> > - tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
> > + tpm_buf_append(sized, options->policydigest, options->policydigest_len);
> >
> > /* public parameters */
> > - tpm_buf_append_u16(&sized, TPM_ALG_NULL);
> > - tpm_buf_append_u16(&sized, 0);
> > + tpm_buf_append_u16(sized, TPM_ALG_NULL);
> > + tpm_buf_append_u16(sized, 0);
> >
> > - tpm_buf_append(&buf, sized.data, sized.length);
> > + tpm_buf_append(buf, sized->data, sized->length);
> >
> > /* outside info */
> > - tpm_buf_append_u16(&buf, 0);
> > + tpm_buf_append_u16(buf, 0);
> >
> > /* creation PCR */
> > - tpm_buf_append_u32(&buf, 0);
> > + tpm_buf_append_u32(buf, 0);
> >
> > - if (buf.flags & TPM_BUF_OVERFLOW) {
> > + if (buf->flags & TPM_BUF_OVERFLOW) {
> > rc = -E2BIG;
> > tpm2_end_auth_session(chip);
> > goto out;
> > }
> >
> > - rc = tpm_buf_fill_hmac_session(chip, &buf);
> > + rc = tpm_buf_fill_hmac_session(chip, buf);
> > if (rc)
> > goto out;
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
> > - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> > + rc = tpm_transmit_cmd(chip, buf, 4, "sealing data");
> > + rc = tpm_buf_check_hmac_response(chip, buf, rc);
> > if (rc)
> > goto out;
> >
> > - blob_len = tpm_buf_read_u32(&buf, &offset);
> > - if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
> > + blob_len = tpm_buf_read_u32(buf, &offset);
> > + if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_BOUNDARY_ERROR) {
> > rc = -E2BIG;
> > goto out;
> > }
> > - if (buf.length - offset < blob_len) {
> > + if (buf->length - offset < blob_len) {
> > rc = -EFAULT;
> > goto out;
> > }
> >
> > - blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
> > + blob_len = tpm2_key_encode(payload, options, &buf->data[offset], blob_len);
> > if (blob_len < 0)
> > rc = blob_len;
> >
> > out:
> > - tpm_buf_destroy(&sized);
> > - tpm_buf_destroy(&buf);
> > -
> > if (!rc)
> > payload->blob_len = blob_len;
> >
> > @@ -373,7 +377,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> > u32 *blob_handle)
> > {
> > u8 *blob_ref __free(kfree) = NULL;
> > - struct tpm_buf buf;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > unsigned int private_len;
> > unsigned int public_len;
> > unsigned int blob_len;
> > @@ -427,39 +431,38 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> > if (rc)
> > return rc;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
> > - if (rc) {
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > tpm2_end_auth_session(chip);
> > - return rc;
> > + return -ENOMEM;
> > }
> >
> > - rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
> > +
> > + rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
> > if (rc)
> > - goto out;
> > + return rc;
> >
> > - tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> > + tpm_buf_append_hmac_session(chip, buf, 0, options->keyauth,
> > TPM_DIGEST_SIZE);
> >
> > - tpm_buf_append(&buf, blob, blob_len);
> > + tpm_buf_append(buf, blob, blob_len);
> >
> > - if (buf.flags & TPM_BUF_OVERFLOW) {
> > - rc = -E2BIG;
> > + if (buf->flags & TPM_BUF_OVERFLOW) {
> > tpm2_end_auth_session(chip);
> > - goto out;
> > + return -E2BIG;
> > }
> >
> > - rc = tpm_buf_fill_hmac_session(chip, &buf);
> > + rc = tpm_buf_fill_hmac_session(chip, buf);
> > if (rc)
> > - goto out;
> > + return rc;
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
> > - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> > + rc = tpm_transmit_cmd(chip, buf, 4, "loading blob");
> > + rc = tpm_buf_check_hmac_response(chip, buf, rc);
> > if (!rc)
> > *blob_handle = be32_to_cpup(
> > - (__be32 *) &buf.data[TPM_HEADER_SIZE]);
> > -
> > -out:
> > - tpm_buf_destroy(&buf);
> > + (__be32 *)&buf->data[TPM_HEADER_SIZE]);
> >
> > return tpm_ret_to_err(rc);
> > }
> > @@ -482,7 +485,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> > u32 blob_handle)
> > {
> > struct tpm_header *head;
> > - struct tpm_buf buf;
> > + struct tpm_buf *buf __free(kfree) = NULL;
> > u16 data_len;
> > int offset;
> > u8 *data;
> > @@ -492,18 +495,21 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> > if (rc)
> > return rc;
> >
> > - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
> > - if (rc) {
> > + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> > + if (!buf) {
> > tpm2_end_auth_session(chip);
> > - return rc;
> > + return -ENOMEM;
> > }
> >
> > - rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL);
> > + tpm_buf_init(buf, TPM_BUFSIZE);
> > + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
> > +
> > + rc = tpm_buf_append_name(chip, buf, blob_handle, NULL);
> > if (rc)
> > - goto out;
> > + return rc;
> >
> > if (!options->policyhandle) {
> > - tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
> > + tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
> > options->blobauth,
> > options->blobauth_len);
> > } else {
> > @@ -518,39 +524,36 @@ 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, options->policyhandle,
> > NULL /* nonce */, 0, 0,
> > options->blobauth, options->blobauth_len);
> > if (tpm2_chip_auth(chip)) {
> > - tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
> > + tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
> > + NULL, 0);
> > } else {
> > - offset = buf.handles * 4 + TPM_HEADER_SIZE;
> > - head = (struct tpm_header *)buf.data;
> > - if (tpm_buf_length(&buf) == offset)
> > + offset = buf->handles * 4 + TPM_HEADER_SIZE;
> > + head = (struct tpm_header *)buf->data;
> > + if (tpm_buf_length(buf) == offset)
> > head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> > }
> > }
> >
> > - rc = tpm_buf_fill_hmac_session(chip, &buf);
> > + rc = tpm_buf_fill_hmac_session(chip, buf);
> > if (rc)
> > - goto out;
> > + return rc;
> >
> > - rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
> > - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> > + rc = tpm_transmit_cmd(chip, buf, 6, "unsealing");
> > + rc = tpm_buf_check_hmac_response(chip, buf, rc);
> >
> > if (!rc) {
> > data_len = be16_to_cpup(
> > - (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
> > - if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) {
> > - rc = -EFAULT;
> > - goto out;
> > - }
> > + (__be16 *)&buf->data[TPM_HEADER_SIZE + 4]);
> > + if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE)
> > + return -EFAULT;
> >
> > - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
> > - rc = -EFAULT;
> > - goto out;
> > - }
> > - data = &buf.data[TPM_HEADER_SIZE + 6];
> > + if (tpm_buf_length(buf) < TPM_HEADER_SIZE + 6 + data_len)
> > + return -EFAULT;
> > + data = &buf->data[TPM_HEADER_SIZE + 6];
> >
> > if (payload->old_format) {
> > /* migratable flag is at the end of the key */
> > @@ -567,8 +570,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> > }
> > }
> >
> > -out:
> > - tpm_buf_destroy(&buf);
> > return tpm_ret_to_err(rc);
> > }
> >
> > --
> > 2.47.3
> >
> >
Thank you.
BR, Jarkko
^ permalink raw reply
* Re: [PATCH] tpm-buf: memory-safe allocations
From: Jarkko Sakkinen @ 2026-05-29 22:33 UTC (permalink / raw)
To: James Bottomley
Cc: linux-integrity, Jarkko Sakkinen, Arun Menon, Daniel P. Smith,
Alec Brown, Ross Philipson, Stefan Berger, Peter Huewe,
Jason Gunthorpe, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, linux-kernel, keyrings,
linux-security-module
In-Reply-To: <27db53d88a44e057c2f0ed5a637f65e4e18c8c3d.camel@HansenPartnership.com>
On Fri, May 29, 2026 at 10:08:52AM -0400, James Bottomley wrote:
> On Tue, 2026-05-26 at 10:53 +0300, Jarkko Sakkinen wrote:
> > On Mon, May 25, 2026 at 01:50:51PM -0400, James Bottomley wrote:
> > > On Fri, 2026-05-22 at 04:35 +0300, Jarkko Sakkinen wrote:
> > > > Decouple kzalloc from buffer creation, so that a managed
> > > > allocation
> > > > can be
> > > > used:
> > > >
> > > > struct tpm_buf *buf __free(kfree) buf =
> > > > kzalloc(TPM_BUFSIZE,
> > > > GFP_KERNEL);
> > > > if (!buf)
> > > > return -ENOMEM;
> > > >
> > > > tpm_buf_init(buf, TPM_BUFSIZE);
> > > >
> > > > Alternatively, stack allocations are also possible:
> > > >
> > > > u8 buf_data[512];
> > > > struct tpm_buf *buf = (struct tpm_buf *)buf_data;
> > > > tpm_buf_init(buf, sizeof(buf_data));
> > >
> > > This isn't really a good idea from a security point of view.
> > > Remember the buffer has to be big enough for both the sent and the
> > > received data. Today we simply set TPM_BUFSIZE to the maximum
> > > amount a TPM requires and all the send and receives just work. If
> > > we let callers set this size, we're asking for them to get it wrong
> > > (or at least forget about the receive part) and for us to get a DMA
> > > overrun from the TPM ... which might be potentially exploitable
> > > depending on how it occurs (think of an unseal of user chosen data
> > > overrunning).
> >
> > It's one patch so you're free to remark the call sites where this
> > happens. This is not a majorn concern at all.
>
> Nearly twenty years ago, when the kernel was a lot smaller, a then
> kernel luminary called Rusty Russell realized we needed to pay much
> more attention to how we design APIs inside the kernel if we wanted it
> to grow successfully. He published his initial thoughts and gave talks
> at both the kernel summit and OLS on it:
>
> https://ozlabs.org/~rusty/index.cgi/tech/2008-03-18.html
>
> The key point that's always stuck with me is "hard to misuse beats easy
> to use". Later he came up with a rating scale (now known as the Rusty
> API classification):
>
> https://ozlabs.org/~rusty/index.cgi/tech/2008-03-30.html
>
> and for chuckles and grins on April fools day he came up with a
> negative rating ridiculing some of our dafter API choices:
>
> https://ozlabs.org/~rusty/index.cgi/tech/2008-04-01.html
>
> The point for this patch set is that the sizing of the original tpm_buf
> interface scores 10/10 on the Rusty scale (it's impossible to get
> wrong). Simply threading size through the whole API, as this patch
> does, may look like the right answer, but it causes a massive reduction
> in API score. In fact, since the buffer has to be sized not only
> according to what goes in, but also what gets returned and this is
> nowhere mentioned in the new documentation it scores -3 (read the
> documentation and you can still get it wrong). Now by mentioning the
> sizing problems in the doc, you can probably get it up to +3 (read the
> documentation and you'll get it right) but my question was not if you
> got it wrong somewhere in the patch but whether we couldn't do a whole
> lot better in terms of API score by designing a better API.
>
> A key point about the 185 version of the TPM spec is that it's really
> only a few commands that need larger buffers (the Post Quantum ML-KEM
> keys) which doesn't apply to most of the in-kernel TPM callsites.
> Since tpm_buf_init takes the ordinal, we can actually tell at runtime
> (or compile time if the ordinal is a constant) if the command would
> need a larger buffer. We can also tell from the TPM properties whether
> the TPM itself can take a larger buffer, so for every current TPM we
> could retain the original score 10/10 API and warn at runtime if there
> might be a problem. Then the larger keys seem to fit into 8k, so we
> could still retain most of the original API properties of being
> difficult to misuse simply by having an 8k size flag (which we could
> ignore if the TPM doesn't support it) and warn at runtime if
> tpm_buf_init sends an ordinal which might need a larger buffer. At
> worst we should be able to get to an API which scores 5/10 (do it right
> or it will break at runtime).
>
> Regards,
>
> James
This patch has pre-existed long before any of this post-quantum stuff,
and there are good reasons so to have buffers managed given e.g.,
complexity of tpm2-sessions code. It prevents any major risk for
memory leaks.
Trenchboot extends the use buffers to early boot and we want a robust
structure. I'm not going to spend my time reading about philosophical
aspects of API design. There are quantitative reasons to decrease
the risk of memory leaks.
BR, Jarkko
^ permalink raw reply
* Re: [PATCH 00/11] hornet: security, tooling and selftest fixes
From: Paul Moore @ 2026-05-29 20:56 UTC (permalink / raw)
To: Blaise Boscaccy
Cc: Jonathan Corbet, Shuah Khan, James Morris, Serge E. Hallyn,
Eric Biggers, Fan Wu, James.Bottomley, linux-security-module
In-Reply-To: <CAHC9VhSQzV0xZPFwkNdUJ5Lz8Mw6gppBhR6QyRdrY8xCTohHDw@mail.gmail.com>
On Thu, May 28, 2026 at 9:39 PM Paul Moore <paul@paul-moore.com> wrote:
> On Wed, May 27, 2026 at 11:09 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
> >
> > Patch 1 closes a TOCTOU race in signature verification. Map
> > contents were hashed at the program-load hook and re-hashed at
> > the program-run hook, leaving a window in which a sufficiently
> > privileged attacker could mutate a map between the two checks
> > and run a program whose maps no longer matched what was signed.
> > The fix records the verified hashes on the prog at load time
> > and, in security_bpf_prog, checks them against
> > prog->aux->used_maps — the same map set the verifier and
> > runtime resolve against — so the verified and executed sets
> > cannot diverge. The per-map index in the signature format is no
> > longer needed and is dropped; the check becomes a subset test.
> > Reported by Eric Biggers.
> >
> > Patches 2-3 fix two counting bugs in the same area: duplicate maps
> > could satisfy the required hash count, and an off-by-one capped
> > accepted maps at MAX_USED_MAPS.
> >
> > Patches 4-11 are in response to sashiko feedback found here:
> > https://sashiko.dev/#/patchset/20260507191416.2984054-1-bboscaccy%40linux.microsoft.com
> >
> > They provide some correctness fixes in the hornet tooling along with
> > making the selftest behave under cross-compilation and skip cleanly
> > when signing keys / bpftool / vmlinux BTF are unavailable, instead of
> > breaking the global selftest build.
> >
> > Blaise Boscaccy (11):
> > hornet: fix TOCTOU in signed program verification
> > hornet: invert map set check logic
> > hornet: fix off-by-one bug in max used maps check
> > selftests: hornet: handle cross compilation and test skipping
> > hornet: gen_sig: fix off-by-one check for used maps
> > hornet: gen_sig: fix error string allocations
> > hornet: gen_sig: check for bad allocations
> > hornet: gen_sig: fix missing command line switches
> > hornet: scripts: set a non-zero error code for usage
> > hornet: scripts: harden scripts to handle trailing whitespace
> > hornet: scripts: Improve argument handling and error messages
> >
> > Documentation/admin-guide/LSM/Hornet.rst | 39 +++---
> > scripts/hornet/extract-insn.sh | 24 ++--
> > scripts/hornet/extract-map.sh | 25 ++--
> > scripts/hornet/extract-skel.sh | 35 ++++--
> > scripts/hornet/gen_sig.c | 61 ++++++----
> > scripts/hornet/write-sig.sh | 10 +-
> > security/hornet/hornet.asn1 | 1 -
> > security/hornet/hornet_lsm.c | 148 ++++-------------------
> > tools/testing/selftests/hornet/Makefile | 114 +++++++++++++----
> > 9 files changed, 235 insertions(+), 222 deletions(-)
>
> Aside from a possible (?) typo in patch 5/11, this patchset looks okay
> to me so I'm going to merge it to lsm/dev-staging now with the idea of
> moving it to lsm/dev once Blaise provides some clarity on patch-5.
With the typo in 5/11 sorted out, I've gone ahead and moved these
fixes over to lsm/dev. I also took the liberty of adding the
associated 'Fixes:' tags, not critical in this case, but nice to have.
--
paul-moore.com
^ permalink raw reply
* [PATCH] selftests/landlock: explicitly disable audit
From: Maximilian Heyne @ 2026-05-29 20:03 UTC (permalink / raw)
To: stable
Cc: Maximilian Heyne, Mickaël Salaün, Günther Noack,
Shuah Khan, linux-security-module, linux-kselftest, linux-kernel
I'm seeing sporadic selftest failures, such as
# RUN scoped_audit.connect_to_child ...
# scoped_abstract_unix_test.c:314:connect_to_child:Expected 0 (0) == records.access (8)
# connect_to_child: Test failed
# FAIL scoped_audit.connect_to_child
not ok 19 scoped_audit.connect_to_child
This seems similar to what commit 3647a4977fb73d ("selftests/landlock:
Drain stale audit records on init") tried to fix. However, the added
drain loop is not effective. When setting the AUDIT_STATUS_PID, the
kauditd_thread is woken up starting to send messages from the hold queue
to the netlink. Depending on scheduling of this kthread not all messages
might be send via the netlink in the 1 us interval.
Therefore, instead of trying to drain the queue, let's just disable
audit when running non-audit tests or more precisely disable it after
audit-tests. This way we won't generate any new audit message that could
interfere with the other tests.
The comment saying that on process exit audit will be disabled is wrong.
The closed file descriptor just causes an auditd_reset(), not a
disablement. So future messages will be queued in the hold queue.
Cc: stable@vger.kernel.org
Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs")
Signed-off-by: Maximilian Heyne <mheyne@amazon.de>
---
I've seen the failures on the 6.18 kernels but haven't tested on latest
upstream. However, I still think this is an issue.
---
tools/testing/selftests/landlock/audit.h | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/landlock/audit.h b/tools/testing/selftests/landlock/audit.h
index 834005b2b0f09..7842330875f53 100644
--- a/tools/testing/selftests/landlock/audit.h
+++ b/tools/testing/selftests/landlock/audit.h
@@ -494,10 +494,9 @@ static int audit_init_filter_exe(struct audit_filter *filter, const char *path)
static int audit_cleanup(int audit_fd, struct audit_filter *filter)
{
struct audit_filter new_filter;
+ int err;
if (audit_fd < 0 || !filter) {
- int err;
-
/*
* Simulates audit_init_with_exe_filter() when called from
* FIXTURE_TEARDOWN_PARENT().
@@ -518,12 +517,10 @@ static int audit_cleanup(int audit_fd, struct audit_filter *filter)
audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE);
audit_filter_drop(audit_fd, AUDIT_DEL_RULE);
- /*
- * Because audit_cleanup() might not be called by the test auditd
- * process, it might not be possible to explicitly set it. Anyway,
- * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd
- * process will exit.
- */
+ err = audit_set_status(audit_fd, AUDIT_STATUS_ENABLED, 0);
+ if (err)
+ return err;
+
return close(audit_fd);
}
--
2.50.1
Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597
^ permalink raw reply related
* [syzbot] [lsm?] KASAN: slab-use-after-free Read in security_inode_follow_link
From: syzbot @ 2026-05-29 20:01 UTC (permalink / raw)
To: jmorris, linux-kernel, linux-security-module, paul, serge,
syzkaller-bugs
Hello,
syzbot found the following issue on:
HEAD commit: eb3f4b7426cf Merge tag 'nfsd-7.1-2' of git://git.kernel.or..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=17dae52e580000
kernel config: https://syzkaller.appspot.com/x/.config?x=8118209836970b54
dashboard link: https://syzkaller.appspot.com/bug?extid=0962e3a1af6d5e26a52c
compiler: gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=14427ed2580000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=109e452e580000
Downloadable assets:
disk image (non-bootable): https://storage.googleapis.com/syzbot-assets/d900f083ada3/non_bootable_disk-eb3f4b74.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/1406171f5cf6/vmlinux-eb3f4b74.xz
kernel image: https://storage.googleapis.com/syzbot-assets/19b8bb9b727a/bzImage-eb3f4b74.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+0962e3a1af6d5e26a52c@syzkaller.appspotmail.com
==================================================================
BUG: KASAN: slab-use-after-free in security_inode_follow_link+0x277/0x280 security/security.c:1819
Read of size 4 at addr ffff8880393e0004 by task syz.0.594/8610
CPU: 3 UID: 0 PID: 8610 Comm: syz.0.594 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0x13d/0x4b0 mm/kasan/report.c:482
kasan_report+0xdf/0x1d0 mm/kasan/report.c:595
security_inode_follow_link+0x277/0x280 security/security.c:1819
pick_link+0x433/0x13c0 fs/namei.c:2049
step_into_slowpath+0x9ba/0xf90 fs/namei.c:2123
step_into fs/namei.c:2148 [inline]
walk_component fs/namei.c:2284 [inline]
link_path_walk+0xf28/0x1cc0 fs/namei.c:2652
path_parentat fs/namei.c:2856 [inline]
__filename_parentat+0x213/0x740 fs/namei.c:2880
filename_parentat fs/namei.c:2898 [inline]
filename_unlinkat+0xf7/0x730 fs/namei.c:5537
__do_sys_unlinkat fs/namei.c:5597 [inline]
__se_sys_unlinkat fs/namei.c:5589 [inline]
__x64_sys_unlinkat+0xc0/0x130 fs/namei.c:5589
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f60b0f9ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f60b1dc9028 EFLAGS: 00000246 ORIG_RAX: 0000000000000107
RAX: ffffffffffffffda RBX: 00007f60b1215fa0 RCX: 00007f60b0f9ce59
RDX: 0000000000000000 RSI: 00002000000001c0 RDI: 0000000000000006
RBP: 00007f60b1032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f60b1216038 R14: 00007f60b1215fa0 R15: 00007ffddfc81a28
</TASK>
Allocated by task 8610:
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
unpoison_slab_object mm/kasan/common.c:340 [inline]
__kasan_slab_alloc+0x89/0x90 mm/kasan/common.c:366
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4570 [inline]
slab_alloc_node mm/slub.c:4899 [inline]
kmem_cache_alloc_lru_noprof+0x246/0x6e0 mm/slub.c:4918
alloc_inode+0x183/0x250 fs/inode.c:347
new_inode+0x22/0x1c0 fs/inode.c:1179
bpf_get_inode kernel/bpf/inode.c:117 [inline]
bpf_get_inode kernel/bpf/inode.c:102 [inline]
bpf_symlink+0x69/0x240 kernel/bpf/inode.c:391
vfs_symlink fs/namei.c:5643 [inline]
vfs_symlink+0x178/0x4d0 fs/namei.c:5622
filename_symlinkat+0x2a6/0x560 fs/namei.c:5668
__do_sys_symlinkat fs/namei.c:5688 [inline]
__se_sys_symlinkat fs/namei.c:5683 [inline]
__x64_sys_symlinkat+0x9c/0xe0 fs/namei.c:5683
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 8611:
kasan_save_stack+0x30/0x50 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
kasan_save_free_info+0x3b/0x70 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5f/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2689 [inline]
slab_free mm/slub.c:6251 [inline]
kmem_cache_free+0x127/0x6c0 mm/slub.c:6378
destroy_inode+0xcb/0x1c0 fs/inode.c:394
evict+0x599/0xad0 fs/inode.c:865
iput_final fs/inode.c:1960 [inline]
iput.part.0+0x605/0xf50 fs/inode.c:2009
iput+0x35/0x40 fs/inode.c:1975
filename_unlinkat+0x466/0x730 fs/namei.c:5572
__do_sys_unlinkat fs/namei.c:5597 [inline]
__se_sys_unlinkat fs/namei.c:5589 [inline]
__x64_sys_unlinkat+0xc0/0x130 fs/namei.c:5589
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
The buggy address belongs to the object at ffff8880393e0000
which belongs to the cache inode_cache of size 1088
The buggy address is located 4 bytes inside of
freed 1088-byte region [ffff8880393e0000, ffff8880393e0440)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x393e0
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
memcg:ffff88803ce9f201
flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000040 ffff888100090000 dead000000000100 dead000000000122
raw: 0000000000000000 00000008001a001a 00000000f5000000 ffff88803ce9f201
head: 00fff00000000040 ffff888100090000 dead000000000100 dead000000000122
head: 0000000000000000 00000008001a001a 00000000f5000000 ffff88803ce9f201
head: 00fff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Reclaimable, gfp_mask 0xd20d0(__GFP_RECLAIMABLE|__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5753, tgid 5753 (syz-execprog), ts 103858448509, free_ts 0
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0xfd/0x120 mm/page_alloc.c:1853
prep_new_page mm/page_alloc.c:1861 [inline]
get_page_from_freelist+0x11a6/0x3410 mm/page_alloc.c:3941
__alloc_frozen_pages_noprof+0x27c/0x2bc0 mm/page_alloc.c:5221
alloc_slab_page mm/slub.c:3278 [inline]
allocate_slab mm/slub.c:3467 [inline]
new_slab+0xa6/0x6c0 mm/slub.c:3525
refill_objects+0x277/0x420 mm/slub.c:7272
refill_sheaf mm/slub.c:2816 [inline]
__pcs_replace_empty_main+0x375/0x650 mm/slub.c:4652
alloc_from_pcs mm/slub.c:4750 [inline]
slab_alloc_node mm/slub.c:4884 [inline]
kmem_cache_alloc_lru_noprof+0x485/0x6e0 mm/slub.c:4918
alloc_inode+0x183/0x250 fs/inode.c:347
iget_locked+0x1d9/0x6d0 fs/inode.c:1474
kernfs_get_inode+0x46/0x470 fs/kernfs/inode.c:252
kernfs_iop_lookup+0x1a7/0x2d0 fs/kernfs/dir.c:1274
lookup_open.isra.0+0x631/0x11b0 fs/namei.c:4484
open_last_lookups fs/namei.c:4611 [inline]
path_openat+0xa98/0x31a0 fs/namei.c:4855
do_file_open+0x20e/0x430 fs/namei.c:4887
do_sys_openat2+0x10d/0x1e0 fs/open.c:1364
do_sys_open fs/open.c:1370 [inline]
__do_sys_openat fs/open.c:1386 [inline]
__se_sys_openat fs/open.c:1381 [inline]
__x64_sys_openat+0x12d/0x210 fs/open.c:1381
page_owner free stack trace missing
Memory state around the buggy address:
ffff8880393dff00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff8880393dff80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff8880393e0000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff8880393e0080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff8880393e0100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply
* [PATCH v3 2/2] selftests/landlock: test SCOPE_SIGNAL on the SIGIO/fowner pgid path
From: hexlabsecurity @ 2026-05-29 19:08 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Justin Suess, gnoack@google.com,
linux-security-module@vger.kernel.org, stable@vger.kernel.org
From 06174d6988915949342c86fe4d1ee210571a2321 Mon Sep 17 00:00:00 2001
From: Bryam Vargas <hexlabsecurity@proton.me>
Date: Fri, 29 May 2026 12:51:27 -0500
Subject: [PATCH v3 2/2] selftests/landlock: test SCOPE_SIGNAL on the
SIGIO/fowner pgid path
Add a regression test for the LANDLOCK_SCOPE_SIGNAL bypass on the
asynchronous SIGIO delivery path. A sandboxed task that owns a file via
fcntl(F_SETOWN, -pgrp) while sitting at the head of its process group's
PID hlist (the default position after fork()) used to have its Landlock
subject capture skipped, letting the SIGIO fan-out reach non-sandboxed
members of the process group.
The test creates a dedicated process group, sandboxes the (hlist-head)
child with LANDLOCK_SCOPE_SIGNAL, arms F_SETSIG(SIGURG) / F_SETOWN(-pgrp)
/ O_ASYNC on a pipe and triggers the fan-out. The in-domain child must
receive the signal (proving the trigger fired); the non-sandboxed parent,
which is outside the child's domain, must not. Without the fix the parent
is signaled and the test fails.
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
.../selftests/landlock/scoped_signal_test.c | 97 +++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c
index d8bf33417619..05151929c263 100644
--- a/tools/testing/selftests/landlock/scoped_signal_test.c
+++ b/tools/testing/selftests/landlock/scoped_signal_test.c
@@ -559,4 +559,101 @@ TEST_F(fown, sigurg_socket)
_metadata->exit_code = KSFT_FAIL;
}
+/*
+ * Checks that LANDLOCK_SCOPE_SIGNAL is enforced on the asynchronous SIGIO
+ * delivery path (fcntl(F_SETOWN)) when the file owner is a process group.
+ *
+ * A sandboxed task sitting at the head of its process group's PID hlist (the
+ * default position right after fork()) used to escape the
+ * fcntl(F_SETOWN, -pgrp) subject capture: pid_task(pgrp, PIDTYPE_PGID)
+ * resolved to the task itself, so the same-thread-group exemption skipped
+ * recording its Landlock domain. At SIGIO time the cached subject was then
+ * empty and the signal fanned out to every group member, including
+ * non-sandboxed tasks outside the domain.
+ */
+TEST(sigio_to_pgid_members)
+{
+ int trigger[2], sync_child[2];
+ char buf;
+ pid_t child;
+ int status, i;
+
+ drop_caps(_metadata);
+
+ /*
+ * Isolates the test in its own process group so the SIGIO fan-out
+ * stays bounded to this parent and the child forked below.
+ */
+ ASSERT_EQ(0, setpgid(0, 0));
+
+ /* The non-sandboxed parent is the protected (out-of-domain) target. */
+ ASSERT_EQ(0, setup_signal_handler(SIGURG));
+ signal_received = 0;
+
+ ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC));
+ ASSERT_EQ(0, pipe2(sync_child, O_CLOEXEC));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ /*
+ * The child inherits the parent's new process group and, just
+ * attached with hlist_add_head_rcu(), is now the head of the
+ * pgid hlist: this is the case that used to skip the capture.
+ */
+ EXPECT_EQ(0, close(sync_child[0]));
+
+ /* In-domain positive control: the child must be signaled. */
+ ASSERT_EQ(0, setup_signal_handler(SIGURG));
+ signal_received = 0;
+
+ create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
+
+ /* Owns the SIGIO source for the whole process group. */
+ ASSERT_EQ(0, fcntl(trigger[0], F_SETSIG, SIGURG));
+ ASSERT_EQ(0, fcntl(trigger[0], F_SETOWN, -getpgrp()));
+ ASSERT_EQ(0, fcntl(trigger[0], F_SETFL, O_ASYNC));
+
+ /* Fans SIGURG out to every member of the process group. */
+ ASSERT_EQ(1, write(trigger[1], ".", 1));
+
+ /*
+ * The sandboxed child is in its own domain and must always be
+ * signaled: this proves the SIGIO actually fired.
+ */
+ for (i = 0; i < 1000 && !signal_received; i++)
+ usleep(1000);
+ EXPECT_EQ(1, signal_received);
+
+ ASSERT_EQ(1, write(sync_child[1], ".", 1));
+ EXPECT_EQ(0, close(sync_child[1]));
+
+ _exit(_metadata->exit_code);
+ return;
+ }
+ EXPECT_EQ(0, close(sync_child[1]));
+ EXPECT_EQ(0, close(trigger[0]));
+ EXPECT_EQ(0, close(trigger[1]));
+
+ /* Waits for the child to generate the SIGIO. */
+ ASSERT_EQ(1, read(sync_child[0], &buf, 1));
+ EXPECT_EQ(0, close(sync_child[0]));
+
+ /* Lets a delivered-but-pending signal run our handler, if any. */
+ for (i = 0; i < 100 && !signal_received; i++)
+ usleep(1000);
+
+ /*
+ * SCOPE_SIGNAL must block the fan-out to this non-sandboxed parent,
+ * which is outside the child's Landlock domain. Before the fix the
+ * parent was signaled here.
+ */
+ EXPECT_EQ(0, signal_received);
+
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ if (WIFSIGNALED(status) || !WIFEXITED(status) ||
+ WEXITSTATUS(status) != EXIT_SUCCESS)
+ _metadata->exit_code = KSFT_FAIL;
+}
+
TEST_HARNESS_MAIN
--
2.43.0
^ permalink raw reply related
* [PATCH v3 1/2] landlock: fix LANDLOCK_SCOPE_SIGNAL bypass via F_SETOWN to invoker's pgid
From: hexlabsecurity @ 2026-05-29 19:07 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Justin Suess, gnoack@google.com,
linux-security-module@vger.kernel.org, stable@vger.kernel.org
From b5fdc79ce1cb2881d59dfed01d3d9170306be9e8 Mon Sep 17 00:00:00 2001
From: Bryam Vargas <hexlabsecurity@proton.me>
Date: Fri, 29 May 2026 12:49:41 -0500
Subject: [PATCH v3 1/2] landlock: fix LANDLOCK_SCOPE_SIGNAL bypass via
F_SETOWN to invoker's pgid
A Landlock-restricted process can bypass LANDLOCK_SCOPE_SIGNAL on the
SIGIO delivery path and deliver arbitrary signals (including SIGKILL via
F_SETSIG) to non-Landlocked targets that share its pgid, by exploiting a
producer-side cache-vs-live evaluation gap.
The SIGIO path in hook_file_send_sigiotask() consults a cached subject
stored in landlock_file(file)->fown_subject at fcntl(F_SETOWN) time
(via hook_file_set_fowner()), instead of evaluating the live Landlock
domain of the invoking task at signal-send time. The capture is gated
by control_current_fowner(), which returns false (skipping capture)
when pid_task(fown->pid, fown->pid_type) is in current's thread group.
This is correct for PIDTYPE_TGID / PIDTYPE_PID, where the target is a
single task sharing current's cred. It is unsafe for PIDTYPE_PGID and
PIDTYPE_SID: when current is at the head of its pgid hlist -- the
default placement after fork(), hlist_add_head_rcu() in kernel/fork.c --
pid_task(pgid, PIDTYPE_PGID) resolves to current itself,
same_thread_group(current, current) is true, the capture is skipped, and
fown_subject.domain stays NULL. hook_file_send_sigiotask() then
short-circuits at "if (!subject->domain) return 0;", letting the kernel
fan the signal out to every member of the group, including tasks outside
current's Landlock domain that SCOPE_SIGNAL is supposed to protect.
The direct kill() path (hook_task_kill) is unaffected: it evaluates
current's live domain on every call. Only the cached SIGIO path is
broken.
Tighten control_current_fowner() to apply the thread-group exemption
only when the target identifies a single task whose Landlock cred is
necessarily shared with current (PIDTYPE_TGID, PIDTYPE_PID). For
PIDTYPE_PGID and PIDTYPE_SID, always capture the current Landlock
subject so the consumer's scope check runs against every member of the
group at delivery time.
Stable kernels before the fown_subject conversion store the domain in
landlock_file(file)->fown_domain; control_current_fowner() is identical
there, so the same exemption and the same fix apply.
Fixes: 18eb75f3af40 ("landlock: Always allow signals between threads of the same process")
Cc: stable@vger.kernel.org
Reported-by: Bryam Vargas <hexlabsecurity@proton.me>
Tested-by: Justin Suess <utilityemal77@gmail.com>
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
security/landlock/fs.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index c1ecfe239032..edaa52572cbd 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1909,6 +1909,18 @@ static bool control_current_fowner(struct fown_struct *const fown)
if (!p)
return true;
+ /*
+ * For PIDTYPE_PGID and PIDTYPE_SID, signal delivery fans out to
+ * every member of the group at SIGIO time. Even when pid_task()
+ * resolves to current itself (e.g., current is the pgid hlist
+ * head post-fork), non-current members of the group are still
+ * valid targets that must be checked by hook_file_send_sigiotask().
+ * Always capture the current subject for those types so the
+ * consumer scope check runs against the live fown_subject.
+ */
+ if (fown->pid_type == PIDTYPE_PGID || fown->pid_type == PIDTYPE_SID)
+ return true;
+
return !same_thread_group(p, current);
}
--
2.43.0
^ permalink raw reply related
* Re: [REPORT] landlock: SCOPE_SIGNAL bypass via F_SETOWN to invoker pgid -> SIGIO/SIGKILL to non-sandboxed targets
From: hexlabsecurity @ 2026-05-29 19:03 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Justin Suess, gnoack@google.com,
linux-security-module@vger.kernel.org, stable@vger.kernel.org
In-Reply-To: <20260529.li6kaiDaim4B@digikod.net>
Hi Mickaël,
> Could you please replace the reproducer code with a proper kselftest?
> That would need to be a new email patch (v3) [...]
Done -- v3 is a two-patch series:
[PATCH v3 1/2] landlock: fix LANDLOCK_SCOPE_SIGNAL bypass via F_SETOWN to invoker's pgid
[PATCH v3 2/2] selftests/landlock: test SCOPE_SIGNAL on the SIGIO/fowner pgid path
Patch 2 replaces the informal reproducer with a regression test in
scoped_signal_test.c, reusing the existing fown/SIGURG idiom. It adds
TEST(sigio_to_pgid_members): a sandboxed child at the head of its pgid hlist
arms F_SETSIG(SIGURG) / F_SETOWN(-pgrp) / O_ASYNC and triggers the fan-out; the
in-domain child must be signaled (positive control) and the non-sandboxed
parent must not.
I also added the Fixes: tag and Cc: stable that v2 was missing:
Fixes: 18eb75f3af40 ("landlock: Always allow signals between threads of the same process")
That is where the same-thread-group exemption on the fowner path was
introduced (v6.15; backported to 6.12.y/6.13.y/6.14.y -- the original v6.12
signal scoping captured the subject unconditionally and was not affected).
The fix hunk itself is unchanged from v1/v2 and keeps Justin's Tested-by.
A/B on 6.12.90 + CONFIG_SECURITY_LANDLOCK (same .config, only the hunk
differs): without patch 1 the new test fails (the parent is signaled); with it
the test passes and the landlock signal-scoping suite is 20/20. checkpatch is
clean except one expected Reported-by/Closes warning -- the original report was
sent to security@kernel.org, so there is no public URL to point Closes: at.
Thanks,
Bryam Vargas
Independent security researcher. HEXLAB SAS (registration pending) -- Cali, Colombia.
This series fixes a LANDLOCK_SCOPE_SIGNAL bypass on the asynchronous SIGIO
(fcntl(F_SETOWN)) delivery path and adds the kselftest requested in review.
Patch 1 narrows the same-thread-group exemption in control_current_fowner()
so that F_SETOWN to a process group (or session) always captures the caller's
Landlock subject. Without it, a sandboxed task at the head of its pgid hlist
(the default position after fork()) skips the capture, and the SIGIO fan-out
reaches non-sandboxed members of the process group, defeating SCOPE_SIGNAL.
The direct kill() path (hook_task_kill) is unaffected.
Patch 2 adds a regression test to scoped_signal_test.c, replacing the informal
reproducer that previously accompanied the fix.
The defect was introduced by commit 18eb75f3af40 ("landlock: Always allow
signals between threads of the same process") in v6.15, and is present in the
stable branches that backported it (6.12.y, 6.13.y, 6.14.y).
control_current_fowner() is identical across those branches, so patch 1 applies
as-is (stable kernels before the fown_subject conversion store the domain in
landlock_file(file)->fown_domain; the exemption and the fix are the same).
A/B verified on 6.12.90 + CONFIG_SECURITY_LANDLOCK (same .config, only the fix
hunk differs):
- without patch 1: the new test fails -- the non-sandboxed parent receives
the signal (SCOPE_SIGNAL bypassed);
- with patch 1: the new test passes, and the whole landlock signal-scoping
suite passes 20/20 (no regression).
v2 -> v3:
- patch 1: add Fixes: tag and Cc: stable; the fix hunk is unchanged from v1/v2.
- patch 2 (new): replace the git-notes reproducer with a kselftest.
- v1/v2 were sent to security@kernel.org (embargoed; not in a public archive).
Bryam Vargas (2):
landlock: fix LANDLOCK_SCOPE_SIGNAL bypass via F_SETOWN to invoker's pgid
selftests/landlock: test SCOPE_SIGNAL on the SIGIO/fowner pgid path
security/landlock/fs.c | 12 +++
.../selftests/landlock/scoped_signal_test.c | 97 +++++++++++++++++++
2 files changed, 109 insertions(+)
base-commit: 27fa82620cbaa89a7fc11ac3057701d598813e87
^ permalink raw reply
* Re: [PATCH 05/11] hornet: gen_sig: fix off-by-one check for used maps
From: Paul Moore @ 2026-05-29 18:54 UTC (permalink / raw)
To: Blaise Boscaccy
Cc: Jonathan Corbet, Shuah Khan, James Morris, Serge E. Hallyn,
Eric Biggers, Fan Wu, James.Bottomley, linux-security-module
In-Reply-To: <87tsrqxfdc.fsf@microsoft.com>
On Fri, May 29, 2026 at 2:03 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
> Paul Moore <paul@paul-moore.com> writes:
>
> > On Wed, May 27, 2026 at 11:09 PM Blaise Boscaccy
> > <bboscaccy@linux.microsoft.com> wrote:
> >>
> >> A logic bug limited the maximum number of used maps to
> >> MAX_USED_MAPS-1.
> >
> > Should this be MAX_HASHES-1 and not MAX_USED_MAPS-1?
>
> Good eye. Yes that should be MAX_HASHES-1 in the commit message.
Okay, I'll fix that up when merging.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH 05/11] hornet: gen_sig: fix off-by-one check for used maps
From: Blaise Boscaccy @ 2026-05-29 18:03 UTC (permalink / raw)
To: Paul Moore
Cc: Jonathan Corbet, Shuah Khan, James Morris, Serge E. Hallyn,
Eric Biggers, Fan Wu, James.Bottomley, linux-security-module
In-Reply-To: <CAHC9VhR6G5qmd3kXPas_L_SiJx=6J=wUw80xxL9Eu4=tSjMAoQ@mail.gmail.com>
Paul Moore <paul@paul-moore.com> writes:
> On Wed, May 27, 2026 at 11:09 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
>>
>> A logic bug limited the maximum number of used maps to
>> MAX_USED_MAPS-1.
>
> Should this be MAX_HASHES-1 and not MAX_USED_MAPS-1?
>
Good eye. Yes that should be MAX_HASHES-1 in the commit message.
>> Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
>> ---
>> scripts/hornet/gen_sig.c | 4 ++--
>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/scripts/hornet/gen_sig.c b/scripts/hornet/gen_sig.c
>> index b4f983ab24bcd..4e8caad22f381 100644
>> --- a/scripts/hornet/gen_sig.c
>> +++ b/scripts/hornet/gen_sig.c
>> @@ -317,11 +317,11 @@ int main(int argc, char **argv)
>> data_path = optarg;
>> break;
>> case 'A':
>> - hashes[hash_count].file = optarg;
>> - if (++hash_count >= MAX_HASHES) {
>> + if (hash_count >= MAX_HASHES) {
>> usage(argv[0]);
>> return EXIT_FAILURE;
>> }
>> + hashes[hash_count++].file = optarg;
>> break;
>> default:
>> usage(argv[0]);
>> --
>> 2.53.0
>
> --
> paul-moore.com
^ permalink raw reply
* Re: [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
From: Alexei Starovoitov @ 2026-05-29 15:01 UTC (permalink / raw)
To: Daniel Borkmann
Cc: KP Singh, bpf, LSM List, Alexei Starovoitov,
Kumar Kartikeya Dwivedi
In-Reply-To: <544dbc0d-24d2-423f-9db4-07976d67a9d0@iogearbox.net>
On Fri, May 29, 2026 at 5:25 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> On 5/23/26 5:12 PM, Alexei Starovoitov wrote:
> > On Fri, May 22, 2026 at 11:53 PM KP Singh <kpsingh@kernel.org> wrote:
> >>
> >> The loader verifies map->sha against the metadata hash in its
> >> instructions. map->sha is calculated when BPF_OBJ_GET_INFO_BY_FD is called
> >> on the frozen map.
> >>
> >> While the map is frozen, the loader must also ensure the map is
> >> exclusive, as, without exclusivity, another BPF program with map access
> >> can mutate the contents afterwards, so the check passes on stale data.
> >
> > Hold on. How is this an issue? excl_prog_sha guarantees
> > that only loader prog can use this map.
> > Are you saying the same loader prog will use the same map
> > for the 2nd time. Ok. I still don't see a problem.
> >
> >> Place excl_prog_sha right after sha[] in struct bpf_map and have
> >> gen_loader bail with -EINVAL when it is NULL, via BPF_PSEUDO_MAP_IDX at
> >> fixed offset 32. The 8-byte read of the pointer field limits this to
> >> 64-bit kernels; gen_loader needs target pointer size tracking to emit
> >> the right sized read on 32-bit (follow-up).
> >
> > I don't think we can go from maybe-racy to certainly-broken-on-32-bit.
> > So only applied patch 1.
>
> I've looked a bit more into it with regards to above question from Alexei
> as well as the __bpf_md_ptr issue.
>
> Imho, KP is correct that the extra check/enforcement is needed. So Alice
> as a trusted signer generates the loader program (loader_insns + data_blob)
> and signs it. The loader program contains the below enforcement to reject
> if the metadata map was not exclusive.
>
> Now the (untrusted) host that wants to load the program, it holds a signed
> loader where they can't change a byte of it without breaking the signature.
>
> However, it could simply omit excl_prog_hash on BPF_MAP_CREATE for the data
> map (which would "normally" be bound exclusively to the loader).
>
> Then check_map_prog_compatibility() enforcement is skipped on verifier side
> given excl_prog_sha is not set. The loader loads fine, the fingerprint check
> can then pass against a stale snapshot while a different program mangled the
> data_blob underneath.
>
> Regarding __bpf_md_ptr, I would solve it differently via fixed size, see below
> together with the excl check coming before the signature check in the loader
> and the build bug assertions, and a jmp not eq to 1.
>
> include/linux/bpf.h | 1 +
> kernel/bpf/syscall.c | 5 +++++
> tools/lib/bpf/gen_loader.c | 17 +++++++++++++++++
> 3 files changed, 23 insertions(+)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index cd191c5fdb0a..487f4653d8a6 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -295,6 +295,7 @@ struct bpf_map_owner {
>
> struct bpf_map {
> u8 sha[SHA256_DIGEST_SIZE];
> + u32 excl;
> const struct bpf_map_ops *ops;
> struct bpf_map *inner_map_meta;
> #ifdef CONFIG_SECURITY
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 630d530782fe..37dacdbc5c01 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -1572,6 +1572,11 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
> err = -EFAULT;
> goto free_map;
> }
> +
> + /* See libbpf: emit_signature_match() */
> + BUILD_BUG_ON(offsetof(struct bpf_map, excl) != SHA256_DIGEST_SIZE);
> + BUILD_BUG_ON(offsetof(struct bpf_map, sha) != 0);
> + map->excl = 1;
> } else if (attr->excl_prog_hash_size) {
> err = -EINVAL;
> goto free_map;
> diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
> index bcea21c3b7bb..cd8d7df94ac7 100644
> --- a/tools/lib/bpf/gen_loader.c
> +++ b/tools/lib/bpf/gen_loader.c
> @@ -586,6 +586,23 @@ static void emit_signature_match(struct bpf_gen *gen)
> __s64 off;
> int i;
>
> + /*
> + * Reject if the metadata map is not exclusive. Without exclusivity
> + * the cached map->sha[] verified above can be stale: another BPF
> + * program with map access could have mutated the contents between
> + * BPF_OBJ_GET_INFO_BY_FD and loader execution.
> + */
> + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
> + 0, 0, 0, 0));
> + emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, SHA256_DIGEST_LENGTH));
> + off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
> + if (is_simm16(off)) {
> + emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
> + emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, off));
> + } else {
> + gen->error = -ERANGE;
> + }
yeah. much cleaner. ship it.
^ permalink raw reply
* Re: [PATCH v5 12/13] ima: Return error on deleting measurements already copied during kexec
From: Roberto Sassu @ 2026-05-29 14:59 UTC (permalink / raw)
To: Mimi Zohar, corbet, skhan, dmitry.kasatkin, eric.snowberg, paul,
jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <ea886419ef3047ede1885504fad8f865cdcc5ce3.camel@linux.ibm.com>
On Tue, 2026-05-26 at 10:02 -0400, Mimi Zohar wrote:
> On Wed, 2026-04-29 at 18:03 +0200, Roberto Sassu wrote:
> > From: Roberto Sassu <roberto.sassu@huawei.com>
> >
> > Refuse to delete staged or active list measurements, if a kexec racing with
> > the deletion already copied those measurements in the kexec buffer. In this
> > way, user space becomes aware that those measurements are going to appear
> > in the secondary kernel, and thus they don't have to be saved twice.
>
> There are two reboot notifiers: one to prevent additional measurements extending
> the TPM, while the other copies the measurements for kexec. This patch prevents
> deleting the staged measurements after the latter notifier.
>
> Instead of introducing a specific method for detecting whether the measurement
> list has been copied, rely on one of the two existing reboot notifiers. The
> simplest method would test "ima_measurements_suspended", which would prevent
> deleting the staged measurements a bit earlier.
Testing that the reboot notifier fired (with the
ima_measurements_suspended variable) is not enough to know whether the
measurements dump took place or not.
We need a flag (one is enough) protected by ima_extend_list_mutex, so
that we know reliably which event occurred first, or the dump or the
staging/delete (which are also protected by ima_extend_list_mutex).
Roberto
^ permalink raw reply
* Re: [PATCH] tpm-buf: memory-safe allocations
From: James Bottomley @ 2026-05-29 14:08 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-integrity, Jarkko Sakkinen, Arun Menon, Daniel P. Smith,
Alec Brown, Ross Philipson, Stefan Berger, Peter Huewe,
Jason Gunthorpe, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, linux-kernel, keyrings,
linux-security-module
In-Reply-To: <ahVRefyT4BTKOu0m@kernel.org>
On Tue, 2026-05-26 at 10:53 +0300, Jarkko Sakkinen wrote:
> On Mon, May 25, 2026 at 01:50:51PM -0400, James Bottomley wrote:
> > On Fri, 2026-05-22 at 04:35 +0300, Jarkko Sakkinen wrote:
> > > Decouple kzalloc from buffer creation, so that a managed
> > > allocation
> > > can be
> > > used:
> > >
> > > struct tpm_buf *buf __free(kfree) buf =
> > > kzalloc(TPM_BUFSIZE,
> > > GFP_KERNEL);
> > > if (!buf)
> > > return -ENOMEM;
> > >
> > > tpm_buf_init(buf, TPM_BUFSIZE);
> > >
> > > Alternatively, stack allocations are also possible:
> > >
> > > u8 buf_data[512];
> > > struct tpm_buf *buf = (struct tpm_buf *)buf_data;
> > > tpm_buf_init(buf, sizeof(buf_data));
> >
> > This isn't really a good idea from a security point of view.
> > Remember the buffer has to be big enough for both the sent and the
> > received data. Today we simply set TPM_BUFSIZE to the maximum
> > amount a TPM requires and all the send and receives just work. If
> > we let callers set this size, we're asking for them to get it wrong
> > (or at least forget about the receive part) and for us to get a DMA
> > overrun from the TPM ... which might be potentially exploitable
> > depending on how it occurs (think of an unseal of user chosen data
> > overrunning).
>
> It's one patch so you're free to remark the call sites where this
> happens. This is not a majorn concern at all.
Nearly twenty years ago, when the kernel was a lot smaller, a then
kernel luminary called Rusty Russell realized we needed to pay much
more attention to how we design APIs inside the kernel if we wanted it
to grow successfully. He published his initial thoughts and gave talks
at both the kernel summit and OLS on it:
https://ozlabs.org/~rusty/index.cgi/tech/2008-03-18.html
The key point that's always stuck with me is "hard to misuse beats easy
to use". Later he came up with a rating scale (now known as the Rusty
API classification):
https://ozlabs.org/~rusty/index.cgi/tech/2008-03-30.html
and for chuckles and grins on April fools day he came up with a
negative rating ridiculing some of our dafter API choices:
https://ozlabs.org/~rusty/index.cgi/tech/2008-04-01.html
The point for this patch set is that the sizing of the original tpm_buf
interface scores 10/10 on the Rusty scale (it's impossible to get
wrong). Simply threading size through the whole API, as this patch
does, may look like the right answer, but it causes a massive reduction
in API score. In fact, since the buffer has to be sized not only
according to what goes in, but also what gets returned and this is
nowhere mentioned in the new documentation it scores -3 (read the
documentation and you can still get it wrong). Now by mentioning the
sizing problems in the doc, you can probably get it up to +3 (read the
documentation and you'll get it right) but my question was not if you
got it wrong somewhere in the patch but whether we couldn't do a whole
lot better in terms of API score by designing a better API.
A key point about the 185 version of the TPM spec is that it's really
only a few commands that need larger buffers (the Post Quantum ML-KEM
keys) which doesn't apply to most of the in-kernel TPM callsites.
Since tpm_buf_init takes the ordinal, we can actually tell at runtime
(or compile time if the ordinal is a constant) if the command would
need a larger buffer. We can also tell from the TPM properties whether
the TPM itself can take a larger buffer, so for every current TPM we
could retain the original score 10/10 API and warn at runtime if there
might be a problem. Then the larger keys seem to fit into 8k, so we
could still retain most of the original API properties of being
difficult to misuse simply by having an 8k size flag (which we could
ignore if the TPM doesn't support it) and warn at runtime if
tpm_buf_init sends an ordinal which might need a larger buffer. At
worst we should be able to get to an API which scores 5/10 (do it right
or it will break at runtime).
Regards,
James
^ permalink raw reply
* Re: [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
From: Daniel Borkmann @ 2026-05-29 12:25 UTC (permalink / raw)
To: Alexei Starovoitov, KP Singh
Cc: bpf, LSM List, Alexei Starovoitov, Kumar Kartikeya Dwivedi
In-Reply-To: <CAADnVQLJsvCfRxyLT-NJRubwSPTNd0k5bEp45Zyu9q1B_3oG+A@mail.gmail.com>
On 5/23/26 5:12 PM, Alexei Starovoitov wrote:
> On Fri, May 22, 2026 at 11:53 PM KP Singh <kpsingh@kernel.org> wrote:
>>
>> The loader verifies map->sha against the metadata hash in its
>> instructions. map->sha is calculated when BPF_OBJ_GET_INFO_BY_FD is called
>> on the frozen map.
>>
>> While the map is frozen, the loader must also ensure the map is
>> exclusive, as, without exclusivity, another BPF program with map access
>> can mutate the contents afterwards, so the check passes on stale data.
>
> Hold on. How is this an issue? excl_prog_sha guarantees
> that only loader prog can use this map.
> Are you saying the same loader prog will use the same map
> for the 2nd time. Ok. I still don't see a problem.
>
>> Place excl_prog_sha right after sha[] in struct bpf_map and have
>> gen_loader bail with -EINVAL when it is NULL, via BPF_PSEUDO_MAP_IDX at
>> fixed offset 32. The 8-byte read of the pointer field limits this to
>> 64-bit kernels; gen_loader needs target pointer size tracking to emit
>> the right sized read on 32-bit (follow-up).
>
> I don't think we can go from maybe-racy to certainly-broken-on-32-bit.
> So only applied patch 1.
I've looked a bit more into it with regards to above question from Alexei
as well as the __bpf_md_ptr issue.
Imho, KP is correct that the extra check/enforcement is needed. So Alice
as a trusted signer generates the loader program (loader_insns + data_blob)
and signs it. The loader program contains the below enforcement to reject
if the metadata map was not exclusive.
Now the (untrusted) host that wants to load the program, it holds a signed
loader where they can't change a byte of it without breaking the signature.
However, it could simply omit excl_prog_hash on BPF_MAP_CREATE for the data
map (which would "normally" be bound exclusively to the loader).
Then check_map_prog_compatibility() enforcement is skipped on verifier side
given excl_prog_sha is not set. The loader loads fine, the fingerprint check
can then pass against a stale snapshot while a different program mangled the
data_blob underneath.
Regarding __bpf_md_ptr, I would solve it differently via fixed size, see below
together with the excl check coming before the signature check in the loader
and the build bug assertions, and a jmp not eq to 1.
include/linux/bpf.h | 1 +
kernel/bpf/syscall.c | 5 +++++
tools/lib/bpf/gen_loader.c | 17 +++++++++++++++++
3 files changed, 23 insertions(+)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index cd191c5fdb0a..487f4653d8a6 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -295,6 +295,7 @@ struct bpf_map_owner {
struct bpf_map {
u8 sha[SHA256_DIGEST_SIZE];
+ u32 excl;
const struct bpf_map_ops *ops;
struct bpf_map *inner_map_meta;
#ifdef CONFIG_SECURITY
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 630d530782fe..37dacdbc5c01 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1572,6 +1572,11 @@ static int map_create(union bpf_attr *attr, bpfptr_t uattr)
err = -EFAULT;
goto free_map;
}
+
+ /* See libbpf: emit_signature_match() */
+ BUILD_BUG_ON(offsetof(struct bpf_map, excl) != SHA256_DIGEST_SIZE);
+ BUILD_BUG_ON(offsetof(struct bpf_map, sha) != 0);
+ map->excl = 1;
} else if (attr->excl_prog_hash_size) {
err = -EINVAL;
goto free_map;
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index bcea21c3b7bb..cd8d7df94ac7 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -586,6 +586,23 @@ static void emit_signature_match(struct bpf_gen *gen)
__s64 off;
int i;
+ /*
+ * Reject if the metadata map is not exclusive. Without exclusivity
+ * the cached map->sha[] verified above can be stale: another BPF
+ * program with map access could have mutated the contents between
+ * BPF_OBJ_GET_INFO_BY_FD and loader execution.
+ */
+ emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
+ 0, 0, 0, 0));
+ emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, SHA256_DIGEST_LENGTH));
+ off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
+ if (is_simm16(off)) {
+ emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
+ emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, off));
+ } else {
+ gen->error = -ERANGE;
+ }
+
for (i = 0; i < SHA256_DWORD_SIZE; i++) {
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
0, 0, 0, 0));
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v4 1/2] rust: task: clarify comments on task UID accessors
From: Gary Guo @ 2026-05-29 12:17 UTC (permalink / raw)
To: Alice Ryhl, Paul Moore, Serge Hallyn, Jonathan Corbet,
Greg Kroah-Hartman, Shuah Khan, Alex Shi, Yanteng Si,
Dongliang Mu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Jann Horn, linux-security-module, linux-doc, linux-kernel,
rust-for-linux
In-Reply-To: <20260529-remove-task-euid-v4-1-07cbdf3af980@google.com>
On Fri May 29, 2026 at 10:33 AM BST, Alice Ryhl wrote:
> From: Jann Horn <jannh@google.com>
>
> Linux has separate subjective and objective task credentials, see the
> comment above `struct cred`. Clarify which accessor functions operate on
> which set of credentials.
>
> Also document that Task::euid() is a very weird operation. You can see how
> weird it is by grepping for task_euid() - binder is its only user.
> Task::euid() obtains the objective effective UID - it looks at the
> credentials of the task for purposes of acting on it as an object, but then
> accesses the effective UID (which the credentials.7 man page describes as
> "[...] used by the kernel to determine the permissions that the process
> will have when accessing shared resources [...]").
>
> For context:
> Arguably, binder's use of task_euid() is a theoretical security problem,
> which only has no impact on Android because Android has no setuid binaries
> executable by apps.
> commit 29bc22ac5e5b ("binder: use euid from cred instead of using task")
> fixed that by removing that only user of task_euid(), but the fix got
> reverted in commit c21a80ca0684 ("binder: fix test regression due to
> sender_euid change") because some Android test started failing.
>
> Signed-off-by: Jann Horn <jannh@google.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> Originally sent as:
> https://lore.kernel.org/r/20260212-rust-uid-v1-1-deff4214c766@google.com
> ---
> rust/kernel/task.rs | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
^ permalink raw reply
* Re: [REPORT] landlock: SCOPE_SIGNAL bypass via F_SETOWN to invoker pgid -> SIGIO/SIGKILL to non-sandboxed targets
From: Mickaël Salaün @ 2026-05-29 11:08 UTC (permalink / raw)
To: hexlabsecurity
Cc: Justin Suess, gnoack@google.com,
linux-security-module@vger.kernel.org, stable@vger.kernel.org
In-Reply-To: <TSwHGN3I-u6p6xv7CqnvDOhR3la_kQWq0rdjBdA0gt30AsYLwddoxjCCFmqXcQMxWHS4ShULEp7sO_8HdFRGPLk30rIQHy3EurwJyrjP3NQ=@proton.me>
Hi,
Thanks for the report. Could you please replace the reproducer code
with a proper kselftest?
That would need to be a new email patch (v3) as explained here:
https://docs.kernel.org/process/submitting-patches.html
Regards,
Mickaël
On Fri, May 29, 2026 at 04:43:02AM +0000, hexlabsecurity@proton.me wrote:
> Thanks Justin -- much appreciated for reproducing on mic/next and for the
> Tested-by.
>
> v2 below addresses your review:
> - the commit message is trimmed to just the bug and the fix;
> - the reproducer and the A/B verification are moved below the --- so
> they become git notes, not part of the commit;
> - added your Tested-by.
>
> The fix hunk is unchanged. I agree the concise statement of the defect is
> "we fail to check the subject on fan-out signal types (PIDTYPE_PGID and
> PIDTYPE_SID, i.e. type > PIDTYPE_TGID)". The patch keeps the explicit
> PIDTYPE_PGID / PIDTYPE_SID test for readability and to stay robust if the
> enum is ever reordered -- happy to switch to "> PIDTYPE_TGID" if you
> prefer. I'll follow up separately on the erratum entry and a regression
> test, as you suggested.
>
> Independent security researcher. HEXLAB SAS (registration pending) --
> Cali, Colombia.
>
> Thanks,
> Bryam Vargas
>
> ----- v2 patch (inline, plain text) -----
>
> From 75f801309cd64f74d04ef86236bd973314dd7d94 Mon Sep 17 00:00:00 2001
> From: Bryam Vargas <hexlabsecurity@proton.me>
> Date: Thu, 28 May 2026 23:33:13 -0500
> Subject: [PATCH v2] landlock: fix LANDLOCK_SCOPE_SIGNAL bypass via F_SETOWN to
> invoker's pgid
>
> A Landlock-restricted process can bypass LANDLOCK_SCOPE_SIGNAL on the
> SIGIO delivery path and deliver arbitrary signals (including SIGKILL via
> F_SETSIG) to non-Landlocked targets that share its pgid, by exploiting a
> producer-side cache-vs-live evaluation gap.
>
> The SIGIO path in hook_file_send_sigiotask() consults a cached subject
> stored in landlock_file(file)->fown_subject at fcntl(F_SETOWN) time
> (via hook_file_set_fowner()), instead of evaluating the live Landlock
> domain of the invoking task at signal-send time. The capture is gated
> by control_current_fowner(), which returns false (skipping capture)
> when pid_task(fown->pid, fown->pid_type) is in current's thread group.
>
> This is correct for PIDTYPE_TGID / PIDTYPE_PID, where the target is a
> single task sharing current's cred. It is unsafe for PIDTYPE_PGID and
> PIDTYPE_SID: when current is at the head of its pgid hlist -- the
> default placement after fork(), hlist_add_head_rcu() in kernel/fork.c --
> pid_task(pgid, PIDTYPE_PGID) resolves to current itself,
> same_thread_group(current, current) is true, the capture is skipped, and
> fown_subject.domain stays NULL. hook_file_send_sigiotask() then
> short-circuits at "if (!subject->domain) return 0;", letting the kernel
> fan the signal out to every member of the group, including tasks outside
> current's Landlock domain that SCOPE_SIGNAL is supposed to protect.
>
> The direct kill() path (hook_task_kill) is unaffected: it evaluates
> current's live domain on every call. Only the cached SIGIO path is
> broken.
>
> Tighten control_current_fowner() to apply the thread-group exemption
> only when the target identifies a single task whose Landlock cred is
> necessarily shared with current (PIDTYPE_TGID, PIDTYPE_PID). For
> PIDTYPE_PGID and PIDTYPE_SID, always capture the current Landlock
> subject so the consumer's scope check runs against every member of the
> group at delivery time.
>
> Reported-by: Bryam Vargas <hexlabsecurity@proton.me>
> Tested-by: Justin Suess <utilityemal77@gmail.com>
> Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
> ---
> v2: per review, the commit message is trimmed to the bug + the fix; the
> reproducer and the A/B verification are moved below the --- so they
> stay out of the commit. Added Tested-by. The hunk is unchanged from
> v1 (v1 sent to security@kernel.org 2026-05-28, embargoed -- not yet
> in a public archive).
>
> Reproducer (ordinary unprivileged user; sandbox active in the child):
>
> int pfd[2]; pipe(pfd);
> landlock_create_ruleset(&{.scoped = LANDLOCK_SCOPE_SIGNAL},
> sizeof(attr), 0);
> prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
> landlock_restrict_self(rfd, 0);
> fcntl(pfd[0], F_SETSIG, SIGKILL);
> fcntl(pfd[0], F_SETOWN, -getpgrp()); /* PIDTYPE_PGID */
> fcntl(pfd[0], F_SETFL, O_ASYNC);
> write(pfd[1], "X", 1); /* trigger SIGIO */
> /* every pgid member receives SIGKILL, including the non-sandboxed
> * parent / supervisor / sibling workers */
>
> A/B-verified on a 6.12.90 lab kernel (same .config, only this hunk
> differs): pre-fix the sandboxed child's SIGKILL reaches the
> non-sandboxed parent (SCOPE_SIGNAL bypassed); post-fix it is blocked.
> hook_task_kill's direct-kill enforcement and the intra-thread-group
> F_SETOWN cases continue to work post-patch.
>
> security/landlock/fs.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index c1ecfe239032..edaa52572cbd 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -1909,6 +1909,18 @@ static bool control_current_fowner(struct fown_struct *const fown)
> if (!p)
> return true;
>
> + /*
> + * For PIDTYPE_PGID and PIDTYPE_SID, signal delivery fans out to
> + * every member of the group at SIGIO time. Even when pid_task()
> + * resolves to current itself (e.g., current is the pgid hlist
> + * head post-fork), non-current members of the group are still
> + * valid targets that must be checked by hook_file_send_sigiotask().
> + * Always capture the current subject for those types so the
> + * consumer scope check runs against the live fown_subject.
> + */
> + if (fown->pid_type == PIDTYPE_PGID || fown->pid_type == PIDTYPE_SID)
> + return true;
> +
> return !same_thread_group(p, current);
> }
> --
> 2.43.0
^ permalink raw reply
* [PATCH v4 2/2] cred: delete task_euid()
From: Alice Ryhl @ 2026-05-29 9:33 UTC (permalink / raw)
To: Paul Moore, Serge Hallyn, Jonathan Corbet, Greg Kroah-Hartman,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Jann Horn, linux-security-module, linux-doc, linux-kernel,
rust-for-linux, Alice Ryhl
In-Reply-To: <20260529-remove-task-euid-v4-0-07cbdf3af980@google.com>
task_euid() is a very weird operation. You can see how weird it is by
grepping for task_euid() - binder is its only user. task_euid() obtains
the objective effective UID - it looks at the credentials of the task
for purposes of acting on it as an object, but then accesses the
effective UID (which the credentials.7 man page describes as "[...] used
by the kernel to determine the permissions that the process will have
when accessing shared resources [...]").
Since usage in Binder has now been removed, get rid of the resulting
dead code.
Changes to the zh_CN translation was carried out with the help of
Gemini and Google Translate, and since adjusted as per Alex Shi's
feedback.
Suggested-by: Jann Horn <jannh@google.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Documentation/security/credentials.rst | 6 ++----
Documentation/translations/zh_CN/security/credentials.rst | 4 +---
include/linux/cred.h | 1 -
rust/helpers/task.c | 5 -----
rust/kernel/task.rs | 10 ----------
5 files changed, 3 insertions(+), 23 deletions(-)
diff --git a/Documentation/security/credentials.rst b/Documentation/security/credentials.rst
index d0191c8b8060..81d3b5737d85 100644
--- a/Documentation/security/credentials.rst
+++ b/Documentation/security/credentials.rst
@@ -393,16 +393,14 @@ the credentials so obtained when they're finished with.
The result of ``__task_cred()`` should not be passed directly to
``get_cred()`` as this may race with ``commit_cred()``.
-There are a couple of convenience functions to access bits of another task's
-credentials, hiding the RCU magic from the caller::
+There is a convenience function to access bits of another task's credentials,
+hiding the RCU magic from the caller::
uid_t task_uid(task) Task's real UID
- uid_t task_euid(task) Task's effective UID
If the caller is holding the RCU read lock at the time anyway, then::
__task_cred(task)->uid
- __task_cred(task)->euid
should be used instead. Similarly, if multiple aspects of a task's credentials
need to be accessed, RCU read lock should be used, ``__task_cred()`` called,
diff --git a/Documentation/translations/zh_CN/security/credentials.rst b/Documentation/translations/zh_CN/security/credentials.rst
index 88fcd9152ffe..20c8696f8198 100644
--- a/Documentation/translations/zh_CN/security/credentials.rst
+++ b/Documentation/translations/zh_CN/security/credentials.rst
@@ -337,15 +337,13 @@ const指针上操作,因此不需要进行类型转换,但需要临时放弃
``__task_cred()`` 的结果不应直接传递给 ``get_cred()`` ,
因为这可能与 ``commit_cred()`` 发生竞争条件。
-还有一些方便的函数可以访问另一个任务凭据的特定部分,将RCU操作对调用方隐藏起来::
+有一个方便的函数可用于访问另一个任务凭据的特定部分,从而对调用方隐藏RCU机制::
uid_t task_uid(task) Task's real UID
- uid_t task_euid(task) Task's effective UID
如果调用方在此时已经持有RCU读锁,则应使用::
__task_cred(task)->uid
- __task_cred(task)->euid
类似地,如果需要访问任务凭据的多个方面,应使用RCU读锁,调用 ``__task_cred()``
函数,将结果存储在临时指针中,然后从临时指针中调用凭据的各个方面,最后释放锁。
diff --git a/include/linux/cred.h b/include/linux/cred.h
index c6676265a985..6ef1750c93e2 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -371,7 +371,6 @@ DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_NULL(_T)) put_cred(_T))
})
#define task_uid(task) (task_cred_xxx((task), uid))
-#define task_euid(task) (task_cred_xxx((task), euid))
#define task_ucounts(task) (task_cred_xxx((task), ucounts))
#define current_cred_xxx(xxx) \
diff --git a/rust/helpers/task.c b/rust/helpers/task.c
index c0e1a06ede78..b46b1433a67e 100644
--- a/rust/helpers/task.c
+++ b/rust/helpers/task.c
@@ -28,11 +28,6 @@ __rust_helper kuid_t rust_helper_task_uid(struct task_struct *task)
return task_uid(task);
}
-__rust_helper kuid_t rust_helper_task_euid(struct task_struct *task)
-{
- return task_euid(task);
-}
-
#ifndef CONFIG_USER_NS
__rust_helper uid_t rust_helper_from_kuid(struct user_namespace *to, kuid_t uid)
{
diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
index eabd65bfde12..c2b3457b700c 100644
--- a/rust/kernel/task.rs
+++ b/rust/kernel/task.rs
@@ -217,16 +217,6 @@ pub fn uid(&self) -> Kuid {
Kuid::from_raw(unsafe { bindings::task_uid(self.as_ptr()) })
}
- /// Returns the objective effective UID of the given task.
- ///
- /// You should probably not be using this; the effective UID is normally
- /// only relevant in subjective credentials.
- #[inline]
- pub fn euid(&self) -> Kuid {
- // SAFETY: It's always safe to call `task_euid` on a valid task.
- Kuid::from_raw(unsafe { bindings::task_euid(self.as_ptr()) })
- }
-
/// Determines whether the given task has pending signals.
#[inline]
pub fn signal_pending(&self) -> bool {
--
2.54.0.823.g6e5bcc1fc9-goog
^ permalink raw reply related
* [PATCH v4 1/2] rust: task: clarify comments on task UID accessors
From: Alice Ryhl @ 2026-05-29 9:33 UTC (permalink / raw)
To: Paul Moore, Serge Hallyn, Jonathan Corbet, Greg Kroah-Hartman,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Jann Horn, linux-security-module, linux-doc, linux-kernel,
rust-for-linux, Alice Ryhl
In-Reply-To: <20260529-remove-task-euid-v4-0-07cbdf3af980@google.com>
From: Jann Horn <jannh@google.com>
Linux has separate subjective and objective task credentials, see the
comment above `struct cred`. Clarify which accessor functions operate on
which set of credentials.
Also document that Task::euid() is a very weird operation. You can see how
weird it is by grepping for task_euid() - binder is its only user.
Task::euid() obtains the objective effective UID - it looks at the
credentials of the task for purposes of acting on it as an object, but then
accesses the effective UID (which the credentials.7 man page describes as
"[...] used by the kernel to determine the permissions that the process
will have when accessing shared resources [...]").
For context:
Arguably, binder's use of task_euid() is a theoretical security problem,
which only has no impact on Android because Android has no setuid binaries
executable by apps.
commit 29bc22ac5e5b ("binder: use euid from cred instead of using task")
fixed that by removing that only user of task_euid(), but the fix got
reverted in commit c21a80ca0684 ("binder: fix test regression due to
sender_euid change") because some Android test started failing.
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Originally sent as:
https://lore.kernel.org/r/20260212-rust-uid-v1-1-deff4214c766@google.com
---
rust/kernel/task.rs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
index 38273f4eedb5..eabd65bfde12 100644
--- a/rust/kernel/task.rs
+++ b/rust/kernel/task.rs
@@ -210,14 +210,17 @@ pub fn pid(&self) -> Pid {
unsafe { *ptr::addr_of!((*self.as_ptr()).pid) }
}
- /// Returns the UID of the given task.
+ /// Returns the objective real UID of the given task.
#[inline]
pub fn uid(&self) -> Kuid {
// SAFETY: It's always safe to call `task_uid` on a valid task.
Kuid::from_raw(unsafe { bindings::task_uid(self.as_ptr()) })
}
- /// Returns the effective UID of the given task.
+ /// Returns the objective effective UID of the given task.
+ ///
+ /// You should probably not be using this; the effective UID is normally
+ /// only relevant in subjective credentials.
#[inline]
pub fn euid(&self) -> Kuid {
// SAFETY: It's always safe to call `task_euid` on a valid task.
@@ -371,7 +374,7 @@ fn eq(&self, other: &Self) -> bool {
impl Eq for Task {}
impl Kuid {
- /// Get the current euid.
+ /// Get the current subjective effective UID.
#[inline]
pub fn current_euid() -> Kuid {
// SAFETY: Just an FFI call.
--
2.54.0.823.g6e5bcc1fc9-goog
^ permalink raw reply related
* [PATCH v4 0/2] Delete task_euid()
From: Alice Ryhl @ 2026-05-29 9:33 UTC (permalink / raw)
To: Paul Moore, Serge Hallyn, Jonathan Corbet, Greg Kroah-Hartman,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu
Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
Jann Horn, linux-security-module, linux-doc, linux-kernel,
rust-for-linux, Alice Ryhl
The task_euid() method is a very weird method, and Binder was the only
user. As of commit 65b672152289 ("binder: use current_euid() for
transaction sender identity") Binder doesn't use task_euid() anymore,
so we can delete this method.
My suggestion would be to merge this through the LSM tree.
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Changes in v4:
- Reword 'euid' -> 'effective UID' in 'Kuid::current_euid()' docs.
- Link to v3: https://lore.kernel.org/r/20260507-remove-task-euid-v3-0-27f22f335c2c@google.com
Changes in v3:
- Include 'task' clarification commit in series.
- Rebase and resend.
- Link to v2: https://lore.kernel.org/r/20260227-remove-task-euid-v2-1-9a9c80a82eb6@google.com
Changes in v2:
- Update translation as per Alex Shi.
- Pick up Reviewed-by Gary.
- Update commit title to use cred: prefix.
- Link to v1: https://lore.kernel.org/r/20260219-remove-task-euid-v1-1-904060826e07@google.com
---
Alice Ryhl (1):
cred: delete task_euid()
Jann Horn (1):
rust: task: clarify comments on task UID accessors
Documentation/security/credentials.rst | 6 ++----
Documentation/translations/zh_CN/security/credentials.rst | 4 +---
include/linux/cred.h | 1 -
rust/helpers/task.c | 5 -----
rust/kernel/task.rs | 11 ++---------
5 files changed, 5 insertions(+), 22 deletions(-)
---
base-commit: 7fd2df204f342fc17d1a0bfcd474b24232fb0f32
change-id: 20260219-remove-task-euid-19e4b00beebe
Best regards,
--
Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply
* Re: [REPORT] landlock: SCOPE_SIGNAL bypass via F_SETOWN to invoker pgid -> SIGIO/SIGKILL to non-sandboxed targets
From: hexlabsecurity @ 2026-05-29 4:43 UTC (permalink / raw)
To: Justin Suess
Cc: mic@digikod.net, gnoack@google.com,
linux-security-module@vger.kernel.org, stable@vger.kernel.org
Thanks Justin -- much appreciated for reproducing on mic/next and for the
Tested-by.
v2 below addresses your review:
- the commit message is trimmed to just the bug and the fix;
- the reproducer and the A/B verification are moved below the --- so
they become git notes, not part of the commit;
- added your Tested-by.
The fix hunk is unchanged. I agree the concise statement of the defect is
"we fail to check the subject on fan-out signal types (PIDTYPE_PGID and
PIDTYPE_SID, i.e. type > PIDTYPE_TGID)". The patch keeps the explicit
PIDTYPE_PGID / PIDTYPE_SID test for readability and to stay robust if the
enum is ever reordered -- happy to switch to "> PIDTYPE_TGID" if you
prefer. I'll follow up separately on the erratum entry and a regression
test, as you suggested.
Independent security researcher. HEXLAB SAS (registration pending) --
Cali, Colombia.
Thanks,
Bryam Vargas
----- v2 patch (inline, plain text) -----
From 75f801309cd64f74d04ef86236bd973314dd7d94 Mon Sep 17 00:00:00 2001
From: Bryam Vargas <hexlabsecurity@proton.me>
Date: Thu, 28 May 2026 23:33:13 -0500
Subject: [PATCH v2] landlock: fix LANDLOCK_SCOPE_SIGNAL bypass via F_SETOWN to
invoker's pgid
A Landlock-restricted process can bypass LANDLOCK_SCOPE_SIGNAL on the
SIGIO delivery path and deliver arbitrary signals (including SIGKILL via
F_SETSIG) to non-Landlocked targets that share its pgid, by exploiting a
producer-side cache-vs-live evaluation gap.
The SIGIO path in hook_file_send_sigiotask() consults a cached subject
stored in landlock_file(file)->fown_subject at fcntl(F_SETOWN) time
(via hook_file_set_fowner()), instead of evaluating the live Landlock
domain of the invoking task at signal-send time. The capture is gated
by control_current_fowner(), which returns false (skipping capture)
when pid_task(fown->pid, fown->pid_type) is in current's thread group.
This is correct for PIDTYPE_TGID / PIDTYPE_PID, where the target is a
single task sharing current's cred. It is unsafe for PIDTYPE_PGID and
PIDTYPE_SID: when current is at the head of its pgid hlist -- the
default placement after fork(), hlist_add_head_rcu() in kernel/fork.c --
pid_task(pgid, PIDTYPE_PGID) resolves to current itself,
same_thread_group(current, current) is true, the capture is skipped, and
fown_subject.domain stays NULL. hook_file_send_sigiotask() then
short-circuits at "if (!subject->domain) return 0;", letting the kernel
fan the signal out to every member of the group, including tasks outside
current's Landlock domain that SCOPE_SIGNAL is supposed to protect.
The direct kill() path (hook_task_kill) is unaffected: it evaluates
current's live domain on every call. Only the cached SIGIO path is
broken.
Tighten control_current_fowner() to apply the thread-group exemption
only when the target identifies a single task whose Landlock cred is
necessarily shared with current (PIDTYPE_TGID, PIDTYPE_PID). For
PIDTYPE_PGID and PIDTYPE_SID, always capture the current Landlock
subject so the consumer's scope check runs against every member of the
group at delivery time.
Reported-by: Bryam Vargas <hexlabsecurity@proton.me>
Tested-by: Justin Suess <utilityemal77@gmail.com>
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
v2: per review, the commit message is trimmed to the bug + the fix; the
reproducer and the A/B verification are moved below the --- so they
stay out of the commit. Added Tested-by. The hunk is unchanged from
v1 (v1 sent to security@kernel.org 2026-05-28, embargoed -- not yet
in a public archive).
Reproducer (ordinary unprivileged user; sandbox active in the child):
int pfd[2]; pipe(pfd);
landlock_create_ruleset(&{.scoped = LANDLOCK_SCOPE_SIGNAL},
sizeof(attr), 0);
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
landlock_restrict_self(rfd, 0);
fcntl(pfd[0], F_SETSIG, SIGKILL);
fcntl(pfd[0], F_SETOWN, -getpgrp()); /* PIDTYPE_PGID */
fcntl(pfd[0], F_SETFL, O_ASYNC);
write(pfd[1], "X", 1); /* trigger SIGIO */
/* every pgid member receives SIGKILL, including the non-sandboxed
* parent / supervisor / sibling workers */
A/B-verified on a 6.12.90 lab kernel (same .config, only this hunk
differs): pre-fix the sandboxed child's SIGKILL reaches the
non-sandboxed parent (SCOPE_SIGNAL bypassed); post-fix it is blocked.
hook_task_kill's direct-kill enforcement and the intra-thread-group
F_SETOWN cases continue to work post-patch.
security/landlock/fs.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index c1ecfe239032..edaa52572cbd 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1909,6 +1909,18 @@ static bool control_current_fowner(struct fown_struct *const fown)
if (!p)
return true;
+ /*
+ * For PIDTYPE_PGID and PIDTYPE_SID, signal delivery fans out to
+ * every member of the group at SIGIO time. Even when pid_task()
+ * resolves to current itself (e.g., current is the pgid hlist
+ * head post-fork), non-current members of the group are still
+ * valid targets that must be checked by hook_file_send_sigiotask().
+ * Always capture the current subject for those types so the
+ * consumer scope check runs against the live fown_subject.
+ */
+ if (fown->pid_type == PIDTYPE_PGID || fown->pid_type == PIDTYPE_SID)
+ return true;
+
return !same_thread_group(p, current);
}
--
2.43.0
^ permalink raw reply related
* [PATCH] KEYS: Use acquire when reading state in keyring search
From: Gui-Dong Han @ 2026-05-29 3:34 UTC (permalink / raw)
To: keyrings, dhowells, jarkko
Cc: ebiggers, linux-security-module, linux-kernel, baijiaju1990,
Gui-Dong Han
The negative-key race fix added release/acquire ordering for key use.
Publish payload before state; read state before payload.
keyring_search_iterator() still uses READ_ONCE() before match callbacks.
An asymmetric match callback calls asymmetric_key_ids(), which reads
key->payload.data[asym_key_ids].
Use key_read_state() there to complete that ordering.
Fixes: 363b02dab09b ("KEYS: Fix race between updating and finding a negative key")
Signed-off-by: Gui-Dong Han <hanguidong02@gmail.com>
---
Found by auditing READ_ONCE() used for synchronization.
A similar fix can be found in 8df672bfe3ec.
---
security/keys/keyring.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index b39038f7dd31..243fb1636f10 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -576,7 +576,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
struct keyring_search_context *ctx = iterator_data;
const struct key *key = keyring_ptr_to_key(object);
unsigned long kflags = READ_ONCE(key->flags);
- short state = READ_ONCE(key->state);
+ short state = key_read_state(key);
kenter("{%d}", key->serial);
--
2.34.1
^ permalink raw reply related
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