[libvirt] [PATCH v3 03/13] security: Always spawn process for transactions

Michal Privoznik mprivozn at redhat.com
Fri Nov 9 12:42:03 UTC 2018


On 11/08/2018 11:45 PM, John Ferlan wrote:
> 
> 
> On 10/17/18 5:06 AM, Michal Privoznik wrote:
>> In the next commit the virSecurityManagerMetadataLock() is going
>> to be turned thread unsafe. Therefore, we have to spawn a
>> separate process for it. Always.
>>
>> Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
>> ---
>>  src/security/security_dac.c     | 2 +-
>>  src/security/security_selinux.c | 2 +-
>>  2 files changed, 2 insertions(+), 2 deletions(-)
>>
> 
> Based slightly upon the KVM Forum presentation detailing some
> performance issues related to the usage of virFork for
> virQEMUCapsIsValid and calls to virFileAccessibleAs:
> 
> https://www.redhat.com/archives/libvir-list/2018-October/msg01333.html
> 
> Given that this patch would thus virFork "always", what kind of impact
> could that have? Have you been able to do any performance profiling?
> What would cause a single round of SELinux & DAC settings?

This is what I was discussing with Daniel. Although I can't recall
where. Anyway, fork() is expensive sure, but my argumentation in
previous versions was that we are doing it already anyway. Namespaces
are enabled by default and unless users turned them off this adds no new
fork(). Only if namespaces are turned off then there is new fork(). At
any rate, this is one fork per one secdriver call. It's not one fork per
path (which would be horrible).

> 
> I know in an earlier patch I wasn't including performance of virFork
> because I believed that the only time it would be used would be for
> metadata locking when (re)labeling would be batched and for that case
> the "one off" virFork would seem reasonable.

This is still true. There is no extra fork() if you have namespaces
enabled. Unfortunately, POSIX file locking is fubared and doing fork is
the least painful option.

> 
> Since it is possible to turn off the NS code and thus go through the
> direct call, I think there's "room for it" here too for those singular
> cases we could use "pid == -1" to indicate direct calls without virFork
> and "pid == 0" to batch together calls using virProcessRunInFork.
> 
> That way when you *know* you are batching together more than singular
> changes, then the caller can choose to run in a fork knowing the
> possible performance penalty, but with the benefits. For those that are
> batched we're stuck, but my memory on all the metadata locking paths is
> fuzzy already.
> 
> What's here does work, but after that KVM Forum preso I guess we
> definitely need to pay attention to areas that can be perf bottlenecks
> for paths that may be really common.
> 
> Having a way to disable or avoid virFork is something we may just need
> to have. I'm willing to be convinced otherwise - I'm just "wary" now.

The metadata locking needs to be there so that the setting seclabels is
atomic. I mean, so far chown() and setfcon() are atomic. However, there
will be some xattr related calls around those. Therefore to make the
whole critical section atomic there has to be a lock:

lock(file);
xattr(file);
chown(file);
xattr(file);
unlock(file);

The xattr() calls set/recall the original owner of the file. I can make
this configurable, but if there is no locking the only thing libvirt can
do is chown(), not the xattr() because that wouldn't be atomic. (I'm
saying only chown(), but it is the same story for setfcon().) Therefore,
if users disable metadata locking the original owner of the file can't
be preserved. On the other hand, we can enable it by default and have an
opt out for cases where it doesn't work - just like we have for
namespaces. And I believe people did disable them in their early days
(even though I don't understand why, they were bugfree :-P)

> 
> 
>> diff --git a/src/security/security_dac.c b/src/security/security_dac.c
>> index da4a6c72fe..580d800bb1 100644
>> --- a/src/security/security_dac.c
>> +++ b/src/security/security_dac.c
> 
> The function description would need an update way to describe this @pid
> usage which differs from the current description.
> 
>> @@ -563,7 +563,7 @@ virSecurityDACTransactionCommit(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
>>      }
>>  
>>      if ((pid == -1 &&
>> -         virSecurityDACTransactionRun(pid, list) < 0) ||
>> +         virProcessRunInFork(virSecurityDACTransactionRun, list) < 0) ||
>>          (pid != -1 &&
>>           virProcessRunInMountNamespace(pid,
>>                                         virSecurityDACTransactionRun,
> 
> I think I've previously disclosed my dislike of the format, why not:
> 
>     if (pid > 0) {
>         rc = virProcessRunInMountNamespace(pid, ...);
>     } else {
>         if (pid == -1)
>             rc = virSecurityDACTransactionRun(-pid, list);
>         else
>             rc = virProcessRunInFork(virSecurityDACTransactionRun, list));
>     }
>     if (rc < 0)
>         goto cleanup;
> 
> to me that's a lot cleaner and doesn't involve trying to look at
> multiple if statements with ||'s.

Okay, I'll change this.

> 
>> diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
>> index 467d1e6bfe..0e24b9b3ca 100644
>> --- a/src/security/security_selinux.c
>> +++ b/src/security/security_selinux.c
>> @@ -1104,7 +1104,7 @@ virSecuritySELinuxTransactionCommit(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
>>      }
>>  
>>      if ((pid == -1 &&
>> -         virSecuritySELinuxTransactionRun(pid, list) < 0) ||
>> +         virProcessRunInFork(virSecuritySELinuxTransactionRun, list) < 0) ||
>>          (pid != -1 &&
>>           virProcessRunInMountNamespace(pid,
>>                                         virSecuritySELinuxTransactionRun,
>>
> 
> ditto.
> 
> John
> 

Michal




More information about the libvir-list mailing list