On 09/10/2018 05:36 AM, Michal Privoznik wrote:
And by "more frequently" I mean always. This is needed so
that we
have a single place where all the paths a thread wants to relabel
are stored. This enables us to lock them all at once (for
metadata), do the relabel and unlock at once again.
Perhaps the first sentence or so "differently said":
Now that committing transactions using pid == -1 means that we're not
fork()-ing to run the transaction in a specific namespace, we can
utilize the transaction processing semantics in order to start, run a or
multiple commands, and then commit the transaction without being
concerned with other interactions or transactions interrupting the
processing. This will eventually allow us to have a single place where
all the paths... (as you've already written).
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/qemu/qemu_security.c | 216 ++++++++++++++++++++++++++++-------------------
1 file changed, 129 insertions(+), 87 deletions(-)
This patch made Coverity unhappy because some of the void callers to
virSecurityManagerTransactionStart or
virSecurityManagerTransactionCommit don't check status:
qemuSecurityStartTPMEmulator (cleanup: processing)
qemuSecurityCleanupTPMEmulator
qemuSecurityRestoreAllLabel
diff --git a/src/qemu/qemu_security.c b/src/qemu/qemu_security.c
index c64fbdda38..b538e08616 100644
--- a/src/qemu/qemu_security.c
+++ b/src/qemu/qemu_security.c
@@ -39,9 +39,12 @@ qemuSecuritySetAllLabel(virQEMUDriverPtr driver,
{
int ret = -1;
qemuDomainObjPrivatePtr priv = vm->privateData;
+ pid_t pid = -1;
- if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT) &&
- virSecurityManagerTransactionStart(driver->securityManager) < 0)
+ if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT))
+ pid = vm->pid;
+
+ if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
goto cleanup;
I know existing, if a transaction fails to start does it need to be
aborted... I know it doesn't matter by looking at the code, but that
thought could be important later on.
if (virSecurityManagerSetAllLabel(driver->securityManager,
@@ -50,9 +53,7 @@ qemuSecuritySetAllLabel(virQEMUDriverPtr driver,
priv->chardevStdioLogd) < 0)
goto cleanup;
- if (qemuDomainNamespaceEnabled(vm, QEMU_DOMAIN_NS_MOUNT) &&
- virSecurityManagerTransactionCommit(driver->securityManager,
- vm->pid) < 0)
+ if (virSecurityManagerTransactionCommit(driver->securityManager, pid) < 0)
goto cleanup;
ret = 0;
@@ -69,16 +70,21 @@ qemuSecurityRestoreAllLabel(virQEMUDriverPtr driver,
{
qemuDomainObjPrivatePtr priv = vm->privateData;
- /* In contrast to qemuSecuritySetAllLabel, do not use
- * secdriver transactions here. This function is called from
- * qemuProcessStop() which is meant to do cleanup after qemu
- * process died. If it did do, the namespace is gone as qemu
- * was the only process running there. We would not succeed
- * in entering the namespace then. */
+ /* In contrast to qemuSecuritySetAllLabel, do not use vm->pid
+ * here. This function is called from qemuProcessStop() which
+ * is meant to do cleanup after qemu process died. The
+ * domain's namespace is gone as qemu was the only process
+ * running there. We would not succeed in entering the
+ * namespace then. */
+ if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
+ return;
+
Interesting that in this instance you choose to honor the status, but
for another void function qemuSecurityCleanupTPMEmulator the choice was
to not care. IDC which way this is done, but it should be consistent.
I can also see a reason that if a TransactionStart fails, that we at
least attempt the following rather than just returning. In that case,
I'd say to use the < 0 status as a way to not call TransactionCommit and
perhaps throw a VIR_WARN to indicate that transactions weren't being used.
Probably also want to save/restore LastError around all this so that a
failure in TransactionStart or TransactionCommit doesn't overwrite an
error we already have.
virSecurityManagerRestoreAllLabel(driver->securityManager,
vm->def,
migrated,
priv->chardevStdioLogd);
+
+ virSecurityManagerTransactionCommit(driver->securityManager, -1);
If we wrap this in ignore_value, then Coverity will be "happy". Since
the only consumer is ProcessStop, at least we don't have the fear that a
Start succeeds and a Commit fails that we'll be "stuck" the next time we
go to do a Start...
I guess to some degree I wonder if the transaction even matters -
meaning - I think you can stick with the original code so that
Start/Commit failure doesn't mean anything.
I also think this function should be separated from the others since its
adding transaction semantics where they weren't present previously. That
way we can debate need or decide later to revert if necessary...
}
[...]
@@ -454,10 +472,21 @@ qemuSecurityStartTPMEmulator(virQEMUDriverPtr
driver,
int *cmdret)
I almost want to say this is "different" than any of the other functions
which you are converting the existing transaction model. In this
instance you are introducing a transaction model. I think this should be
a separate patch to make it easier for a later decision to revert just
in case that decision needs to be made.
The only current consumer is virSecuritySELinuxSetTPMLabels and it
already has the multiple operation type semantics, so I'm wondering if
transaction semantics are actually necessary.
{
int ret = -1;
+ bool transactionStarted = false;
+
+ if (virSecurityManagerTransactionStart(driver->securityManager) < 0)
+ return -1;
+ transactionStarted = true;
if (virSecurityManagerSetTPMLabels(driver->securityManager,
- def) < 0)
+ def) < 0) {
+ virSecurityManagerTransactionAbort(driver->securityManager);
+ return -1;
+ }
+
+ if (virSecurityManagerTransactionCommit(driver->securityManager, -1) < 0)
goto cleanup;
+ transactionStarted = false;
if (virSecurityManagerSetChildProcessLabel(driver->securityManager,
def, cmd) < 0)
@@ -481,8 +510,13 @@ qemuSecurityStartTPMEmulator(virQEMUDriverPtr driver,
return 0;
cleanup:
We should change this to "error:" because it's not cleanup in normal
path...
+ if (!transactionStarted)
+ virSecurityManagerTransactionStart(driver->securityManager);
I think this should be:
if (!transactionStarted) {
if (virSecurityManagerTransactionStart(...) == 0)
transactionStarted = true;
}
+
virSecurityManagerRestoreTPMLabels(driver->securityManager, def);
Making the subsequent be:
if (transactionStarted) {
ignore_value(virSecurityManagerTransactionCommit(...);
virSecurityManagerTransactionAbort(...);
}
Because if the "second" Start fails, then next ones aren't necessary.
I'd also be inclined to say, why bother with a transaction. If something
in the Set processing failed, I'd assume we're going to get a similar
failure in Restore processing - so let the TPM specific code handle
that. We can document the crap out of it^w^w^w^w, err, ah decision made.
+ virSecurityManagerTransactionCommit(driver->securityManager,
-1);
+ virSecurityManagerTransactionAbort(driver->securityManager);
return ret;
}
@@ -491,7 +525,12 @@ void
qemuSecurityCleanupTPMEmulator(virQEMUDriverPtr driver,
virDomainDefPtr def)
{
+ virSecurityManagerTransactionStart(driver->securityManager);
So this is different than qemuSecurityRestoreAllLabel...
Similar to above - we should know if Start fails, because if it does,
then commit and abort won't be happy either...
This should be a separate patch with the StartTPMEmulator...
> +
> virSecurityManagerRestoreTPMLabels(driver->securityManager, def);
> +
> + virSecurityManagerTransactionCommit(driver->securityManager, -1);
> + virSecurityManagerTransactionAbort(driver->securityManager);
}
[...]
So, let's do this for any function that is not adding transaction
semantics, but merely modifying existing semantics,
Reviewed-by: John Ferlan <jferlan(a)redhat.com>
But for qemuSecurityRestoreAllLabel, qemuSecurityStartTPMEmulator, and
qemuSecurityCleanupTPMEmulator - we need to extract those and consider
them separately.
Whether you alter the commit message to my suggestion above is your choice.
John