Re: [BUG] tpm: prolonged stall in vtpm_proxy_fops_release() on Linux 7.3

From: 铮

Date: Tue Nov 10 2026 - 01:58:57 EST


Hi Stefan,

Thank you for your question about closing the server-side fd.

Short answer: yes, the server-side fds are released when the
reproducer exits, and the hang occurs during that release. The hang
reproduced in 4/4 recorded exit-time release attempts (three with N=32
and one with N=4). However, our separate tests that explicitly call
close() on every ioctl-returned fd did NOT reproduce the hang.

Since the .prog files are hard to read, we replaced them with two
small self-contained C test cases. Everything relevant to these new
tests is now in ONE folder in the repository: minimal-c-repro/

1. What is in minimal-c-repro/ and what each part shows

vtpm-hang-minimal.c the hang reproducer (default N=32):
opens /dev/vtpmx, creates N vTPM proxy
devices via VTPM_PROXY_IOC_NEW_DEV
(flags=0, TPM 1.2 mode), and exits
WITHOUT explicitly closing the server-side
fds. The kernel then releases them during
process exit, entering
vtpm_proxy_fops_release().

vtpm-close-minimal.c the explicit-close control experiment:
same creation, then close() each
ioctl-returned server-side fd explicitly,
printing a timestamped line before and
after every close. This directly addresses
your question.

n32-auto-hang/ the N=32 hang runs (3/3 recorded attempts;
some performed on the same boot):
serial-N32-run1.log INFO: task vtpm-hang-minim:9397 blocked
for more than 143 seconds; a follow-up
report shows the task STILL blocked at
286 s (the log ends there, so the stall
lasted at least that long);
serial-N32-run2.log additional hung-task observations;
serial-N32-run3.log further N=32 reproduction evidence;
N32-run2-sysrq.txt,
N32-run3-sysrq.txt sysrq-t captures while blocked: workqueue
tpm-vtpm shows numerous queued
vtpm_proxy_work instances; the serial
logs repeat "tpm tpmN: Operation Timed out"

n4-auto-hang/ the first hit (N=4, 1/1):
serial-minimal-c.log identical signature — the hung task IS
sysrq-t-minimal.txt the reproducer process itself, blocked in
vtpm_proxy_fops_release() ->
vtpm_proxy_work_stop() -> flush_work()

explicit-close/ the explicit-close runs (your question):
explicit-close-N4-log.txt N=4: all four closes returned promptly;
explicit-close-N32-log.txt N=32: all 32 closes returned promptly;
neither test reproduced the hang.
serial-explicit-close.log,
serial-explicit-close-N32.log
the matching serial logs

small-n-miss/ negative results: N=1 and N=2 attempts
serial-small-N1.log did NOT hang within the monitoring
serial-small-N2.log window (no INFO, at most one
"Operation Timed out");
campaign2-console.log automated-run log with the same misses

2. What this means

The hang occurs during the release of the server-side fd: the
blocked task is inside vtpm_proxy_fops_release() itself. In the
reproducing cases, the fd release is initiated automatically during
process exit. Explicit close() in our control experiments did not
reproduce the hang.

vtpm_proxy_work_stop() calls vtpm_proxy_fops_undo_open() BEFORE
flush_work(). Therefore, STATE_OPENED_FLAG is cleared and
wake_up_interruptible(&wq) runs before the wait. Yet in the hanging
runs, the wait persisted for more than 143 s (and at least 286 s
in one run).

Earlier sysrq captures also showed chip-registration work inside
tpm_transmit(), reached through vtpm_proxy_request_locality(),
while the serial logs repeatedly reported TPM timeouts.

Since req_canceled() checks STATE_OPENED_FLAG, we are unsure why
the pending work can remain active for so long after the release
path has cleared the flag. The captured stacks do not establish
whether or when the cancellation callback is evaluated. This is
where we would particularly value your analysis.

The hang is not unconditional. Explicit-close runs returned
promptly, and N=1/N=2 did not reproduce the hang. With N=32,
exit-time release repeatedly produced the prolonged stall.

These differences suggest that the timing or ordering of fd
release relative to chip-registration work may be important,
although we have not established the precise cause.

We understand that TPM timeouts are expected when no userspace TPM
server responds. Our main question is whether vtpm_proxy_fops_release()
is expected to remain blocked in flush_work() for several minutes
under these conditions, even after STATE_OPENED_FLAG has been cleared.

3. Build and run (guest needs CONFIG_TCG_VTPM_PROXY=y, root)

gcc -O2 -static -o vtpm-hang-minimal vtpm-hang-minimal.c
gcc -O2 -static -o vtpm-close-minimal vtpm-close-minimal.c

./vtpm-hang-minimal # exit-time fd release; reproduces hang
./vtpm-close-minimal # explicit close; no hang observed

4. Repository

All the C test cases, corresponding serial logs, sysrq-t captures,
and results summary are available in:

minimal-c-repro/

The original syzkaller programs, kernel configurations, QEMU command
lines, and earlier reproduction evidence remain available under:

repro/ cmdline/ kernel/ logs/

Repository:

https://github.com/Zheng-zheng-codes/vtpm-proxy-release-hang-repro

Best regards,
Zheng Zheng

On Tue, Nov 10, 2026 at 2:15 AM Stefan Berger <stefanb@xxxxxxxxxxxxx> wrote:
>
>
>
> On 11/8/26 12:15 PM, 铮 wrote:
> > Hi Stefan,
> >
> > Thank you for the patch
> > ("tpm: tpm_vtpm_proxy: Set STATE_OPENED_FLAG before inode is created").
> > We applied and tested it, and we have also reproduced the boot warning
> > Stefan reported and identified a sufficient condition for
> > it (running without KVM acceleration). Two findings first; "Where to
> > look" at the end points to the exact files in our repository.
> >
> > 1. Your patch does not change the outcome of the hang
> >
> [...]
>
> >
> > v7.4-rc1 is affected as well: drivers/char/tpm/tpm_vtpm_proxy.c is
> > unchanged between v7.3.0 and v7.4-rc1 (empty diff), and the same
> > reproduction hangs there 1/1 (t=402s, same signature).
>
> I have a hard time reading these .prog files. Are you closing the server
> side file descriptor you are getting back via the ioctl to /dev/tpm?
> Have a look below. If not, that could be the reason why its hanging. If
> you close it, it should clear the STATE_OPENED_FLAG and wake up any
> client waiting for a response.
>
> /**
> * struct vtpm_proxy_new_dev - parameter structure for the
> * %VTPM_PROXY_IOC_NEW_DEV ioctl
> * @flags: flags for the proxy TPM
> * @tpm_num: index of the TPM device
> * @fd: the file descriptor used by the proxy TPM
> * @major: the major number of the TPM device
> * @minor: the minor number of the TPM device
> */
> struct vtpm_proxy_new_dev {
> __u32 flags; /* input */
> __u32 tpm_num; /* output */
> __u32 fd; /* output */ <--- this one here
> __u32 major; /* output */
> __u32 minor; /* output */
> };
>
>
> Stefan
>