Re: [PATCH v3 1/2] security_manager: Ensure top lock is acquired before nested locks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 7/5/24 10:01, hongmianquan wrote:
> 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@xxxxxxxxxxxxx>
> ---
>  src/libvirt_private.syms        |  2 ++
>  src/qemu/qemu_conf.c            | 13 +++++++++++--
>  src/qemu/qemu_driver.c          | 13 +++++++++++--
>  src/qemu/qemu_security.h        |  2 ++
>  src/security/security_manager.c | 22 ++++++++++++++++++++++
>  src/security/security_manager.h |  2 ++
>  6 files changed, 50 insertions(+), 4 deletions(-)
> 


> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 124128b3c4..9d4958789c 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -6216,9 +6216,16 @@ static int qemuDomainGetSecurityLabelList(virDomainPtr dom,
>          ret = 0;
>      } else {
>          int len = 0;
> -        virSecurityManager ** mgrs = qemuSecurityGetNested(driver->securityManager);
> -        if (!mgrs)
> +        virSecurityManager ** mgrs = NULL;
> +
> +        /* Ensure top lock is acquired before nested locks */
> +        qemuSecurityStackLock(driver->securityManager);
> +
> +        mgrs = qemuSecurityGetNested(driver->securityManager);
> +        if (!mgrs) {
> +            qemuSecurityStackUnlock(driver->securityManager);
>              goto cleanup;
> +        }
>  
>          /* Allocate seclabels array */
>          for (i = 0; mgrs[i]; i++)
> @@ -6233,11 +6240,13 @@ static int qemuDomainGetSecurityLabelList(virDomainPtr dom,
>                                              &(*seclabels)[i]) < 0) {
>                  VIR_FREE(mgrs);
>                  VIR_FREE(*seclabels);
> +                qemuSecurityStackUnlock(driver->securityManager);
>                  goto cleanup;

Nitpick, the unlock can be done before VIR_FREE()-s so that the critical
section is a bit shorter.

>              }
>          }
>          ret = len;
>          VIR_FREE(mgrs);
> +        qemuSecurityStackUnlock(driver->securityManager);

Same here.

Michal



[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux