]> git.ipfire.org Git - thirdparty/libvirt.git/commit
security_manager: Ensure top lock is acquired before nested locks
authorhongmianquan <hongmianquan@bytedance.com>
Fri, 5 Jul 2024 08:01:57 +0000 (16:01 +0800)
committerMichal Privoznik <mprivozn@redhat.com>
Tue, 9 Jul 2024 11:22:26 +0000 (13:22 +0200)
commit790b4d80672797f6f08453f00d22901a7178faf4
treeeec51fc3053cf16d297cccc4a251e28e2d6493e3
parent8515a178f8499e3420b430086844f7d1613e4a69
security_manager: Ensure top lock is acquired before nested locks

Fix libvirtd hang since fork() was called while another thread had
security manager locked.

We have the stack security driver, which internally manages other security drivers,
just call them "top" and "nested".

We call virSecurityStackPreFork() to lock the top one, and it also locks
and then unlocks the nested drivers prior to fork. Then in qemuSecurityPostFork(),
it unlocks the top one, but not the nested ones. Thus, if one of the nested
drivers ("dac" or "selinux") is still locked, it will cause a deadlock. If we always
surround nested locks with top lock, it is always secure. Because we have got top lock
before fork child libvirtd.

However, it is not always the case in the current code, We discovered this case:
the nested list obtained through the qemuSecurityGetNested() will be locked directly
for subsequent use, such as in virQEMUDriverCreateCapabilities(), where the nested list
is locked using qemuSecurityGetDOI, but the top one is not locked beforehand.

The problem stack is as follows:

libvirtd thread1          libvirtd thread2          child libvirtd
        |                           |                       |
        |                           |                       |
virsh capabilities      qemuProcessLanuch                   |
        |                           |                       |
        |                       lock top                    |
        |                           |                       |
    lock nested                     |                       |
        |                           |                       |
        |                           fork------------------->|(nested lock held by thread1)
        |                           |                       |
        |                           |                       |
    unlock nested               unlock top              unlock top
                                                            |
                                                            |
                                                qemuSecuritySetSocketLabel
                                                            |
                                                            |
                                                    lock nested (deadlock)

In this commit, we ensure that the top lock is acquired before the nested lock,
so during fork, it's not possible for another task to acquire the nested lock.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1303031

Signed-off-by: hongmianquan <hongmianquan@bytedance.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
src/libvirt_private.syms
src/qemu/qemu_conf.c
src/qemu/qemu_driver.c
src/qemu/qemu_security.h
src/security/security_manager.c
src/security/security_manager.h