Sean Christopherson [off-list ref] writes:
On Mon, Aug 21, 2023, Ackerley Tng wrote:
quoted
Sean Christopherson [off-list ref] writes:
quoted
On Tue, Aug 15, 2023, Ackerley Tng wrote:
quoted
Sean Christopherson [off-list ref] writes:
quoted
Nullifying the KVM pointer isn't sufficient, because without additional actions
userspace could extract data from a VM by deleting its memslots and then binding
the guest_memfd to an attacker controlled VM. Or more likely with TDX and SNP,
induce badness by coercing KVM into mapping memory into a guest with the wrong
ASID/HKID.
I can think of three ways to handle that:
(a) prevent a different VM from *ever* binding to the gmem instance
(b) free/zero physical pages when unbinding
(c) free/zero when binding to a different VM
Option (a) is easy, but that pretty much defeats the purpose of decopuling
guest_memfd from a VM.
Option (b) isn't hard to implement, but it screws up the lifecycle of the memory,
e.g. would require memory when a memslot is deleted. That isn't necessarily a
deal-breaker, but it runs counter to how KVM memlots currently operate. Memslots
are basically just weird page tables, e.g. deleting a memslot doesn't have any
impact on the underlying data in memory. TDX throws a wrench in this as removing
a page from the Secure EPT is effectively destructive to the data (can't be mapped
back in to the VM without zeroing the data), but IMO that's an oddity with TDX and
not necessarily something we want to carry over to other VM types.
There would also be performance implications (probably a non-issue in practice),
and weirdness if/when we get to sharing, linking and/or mmap()ing gmem. E.g. what
should happen if the last memslot (binding) is deleted, but there outstanding userspace
mappings?
Option (c) is better from a lifecycle perspective, but it adds its own flavor of
complexity, e.g. the performant way to reclaim TDX memory requires the TDMR
(effectively the VM pointer), and so a deferred relcaim doesn't really work for
TDX. And I'm pretty sure it *can't* work for SNP, because RMP entries must not
outlive the VM; KVM can't reuse an ASID if there are pages assigned to that ASID
in the RMP, i.e. until all memory belonging to the VM has been fully freed.
...
quoted
I agree with you that nulling the KVM pointer is insufficient to keep
host userspace out of the TCB. Among the three options (a) preventing a
different VM (HKID/ASID) from binding to the gmem instance, or zeroing
the memory either (b) on unbinding, or (c) on binding to another VM
(HKID/ASID),
(a) sounds like adding a check issued to TDX/SNP upon binding and this
check would just return OK for software-protected VMs (line of sight
to removing host userspace from TCB).
Or, we could go further for software-protected VMs and add tracking in
the inode to prevent the same inode from being bound to different
"HKID/ASID"s, perhaps like this:
+ On first binding, store the KVM pointer in the inode - not file (but
not hold a refcount)
+ On rebinding, check that the KVM matches the pointer in the inode
+ On intra-host migration, update the KVM pointer in the inode to allow
binding to the new struct kvm
I think you meant associating the file with a struct kvm at creation
time as an implementation for (a), but technically since the inode is
the representation of memory, tracking of struct kvm should be with the
inode instead of the file.
(b) You're right that this messes up the lifecycle of the memory and
wouldn't work with intra-host migration.
(c) sounds like doing the clearing on a check similar to that of (a)
Sort of, though it's much nastier, because it requires the "old" KVM instance to
be alive enough to support various operations. I.e. we'd have to make stronger
guarantees about exactly when the handoff/transition could happen.
Good point!
quoted
If we track struct kvm with the inode, then I think (a), (b) and (c) can
be independent of the refcounting method. What do you think?
No go. Because again, the inode (physical memory) is coupled to the virtual machine
as a thing, not to a "struct kvm". Or more concretely, the inode is coupled to an
ASID or an HKID, and there can be multiple "struct kvm" objects associated with a
single ASID. And at some point in the future, I suspect we'll have multiple KVM
objects per HKID too.
The current SEV use case is for the migration helper, where two KVM objects share
a single ASID (the "real" VM and the helper). I suspect TDX will end up with
similar behavior where helper "VMs" can use the HKID of the "real" VM. For KVM,
that means multiple struct kvm objects being associated with a single HKID.
To prevent use-after-free, KVM "just" needs to ensure the helper instances can't
outlive the real instance, i.e. can't use the HKID/ASID after the owning virtual
machine has been destroyed.
To put it differently, "struct kvm" is a KVM software construct that _usually_,
but not always, is associated 1:1 with a virtual machine.
And FWIW, stashing the pointer without holding a reference would not be a complete
solution, because it couldn't guard against KVM reusing a pointer. E.g. if a
struct kvm was unbound and then freed, KVM could reuse the same memory for a new
struct kvm, with a different ASID/HKID, and get a false negative on the rebinding
check.
I agree that inode (physical memory) is coupled to the virtual machine
as a more generic concept.
I was hoping that in the absence of CC hardware providing a HKID/ASID,
the struct kvm pointer could act as a representation of the "virtual
machine". You're definitely right that KVM could reuse a pointer and so
that idea doesn't stand.
I thought about generating UUIDs to represent "virtual machines" in the
absence of CC hardware, and this UUID could be transferred during
intra-host migration, but this still doesn't take host userspace out of
the TCB. A malicious host VMM could just use the migration ioctl to copy
the UUID to a malicious dumper VM, which would then pass checks with a
gmem file linked to the malicious dumper VM. This is fine for HKID/ASIDs
because the memory is encrypted; with UUIDs there's no memory
encryption.
Circling back to the original topic, was associating the file with
struct kvm at gmem file creation time meant to constrain the use of the
gmem file to one struct kvm, or one virtual machine, or something else?
Follow up questions:
1. Since the physical memory's representation is the inode and should be
coupled to the virtual machine (as a concept, not struct kvm), should
the binding/coupling be with the file, or the inode?
2. Should struct kvm still be bound to the file/inode at gmem file
creation time, since
+ struct kvm isn't a good representation of a "virtual machine"
+ we currently don't have anything that really represents a "virtual
machine" without hardware support
I'd also like to bring up another userspace use case that Google has:
re-use of gmem files for rebooting guests when the KVM instance is
destroyed and rebuilt.
When rebooting a VM there are some steps relating to gmem that are
performance-sensitive:
a. Zeroing pages from the old VM when we close a gmem file/inode
b. Deallocating pages from the old VM when we close a gmem file/inode
c. Allocating pages for the new VM from the new gmem file/inode
d. Zeroing pages on page allocation
We want to reuse the gmem file to save re-allocating pages (b. and c.),
and one of the two page zeroing allocations (a. or d.).
Binding the gmem file to a struct kvm on creation time means the gmem
file can't be reused with another VM on reboot. Also, host userspace is
forced to close the gmem file to allow the old VM to be freed.
For other places where files pin KVM, like the stats fd pinning vCPUs, I
guess that matters less since there isn't much of a penalty to close and
re-open the stats fd.
On 8/28/2023 3:56 PM, Ackerley Tng wrote:
> 1. Since the physical memory's representation is the inode and should be
> coupled to the virtual machine (as a concept, not struct kvm), should
> the binding/coupling be with the file, or the inode?
>
I've been working on Gunyah's implementation in parallel (not yet posted
anywhere). Thus far, I've coupled the virtual machine struct to the
struct file so that I can increment the file refcount when mapping the
gmem to the virtual machine.
> 2. Should struct kvm still be bound to the file/inode at gmem file
> creation time, since
>
> + struct kvm isn't a good representation of a "virtual machine"
> + we currently don't have anything that really represents a "virtual
> machine" without hardware support
>
>
> I'd also like to bring up another userspace use case that Google has:
> re-use of gmem files for rebooting guests when the KVM instance is
> destroyed and rebuilt.
>
> When rebooting a VM there are some steps relating to gmem that are
> performance-sensitive:
>
> a. Zeroing pages from the old VM when we close a gmem file/inode
> b. Deallocating pages from the old VM when we close a gmem file/inode
> c. Allocating pages for the new VM from the new gmem file/inode
> d. Zeroing pages on page allocation
>
> We want to reuse the gmem file to save re-allocating pages (b. and c.),
> and one of the two page zeroing allocations (a. or d.).
>
> Binding the gmem file to a struct kvm on creation time means the gmem
> file can't be reused with another VM on reboot. Also, host userspace is
> forced to close the gmem file to allow the old VM to be freed.
>
> For other places where files pin KVM, like the stats fd pinning vCPUs, I
> guess that matters less since there isn't much of a penalty to close and
> re-open the stats fd.
I had a 3rd question that's related to how to wire the gmem up to a
virtual machine:
I learned of a usecase to implement copy-on-write for gmem. The premise
would be to have a "golden copy" of the memory that multiple virtual
machines can map in as RO. If a virtual machine tries to write to those
pages, they get copied to a virtual machine-specific page that isn't
shared with other VMs. How do we track those pages?
Thanks,
Elliot
On Mon, Aug 28, 2023, Ackerley Tng wrote:
Sean Christopherson [off-list ref] writes:
quoted
quoted
If we track struct kvm with the inode, then I think (a), (b) and (c) can
be independent of the refcounting method. What do you think?
No go. Because again, the inode (physical memory) is coupled to the virtual machine
as a thing, not to a "struct kvm". Or more concretely, the inode is coupled to an
ASID or an HKID, and there can be multiple "struct kvm" objects associated with a
single ASID. And at some point in the future, I suspect we'll have multiple KVM
objects per HKID too.
The current SEV use case is for the migration helper, where two KVM objects share
a single ASID (the "real" VM and the helper). I suspect TDX will end up with
similar behavior where helper "VMs" can use the HKID of the "real" VM. For KVM,
that means multiple struct kvm objects being associated with a single HKID.
To prevent use-after-free, KVM "just" needs to ensure the helper instances can't
outlive the real instance, i.e. can't use the HKID/ASID after the owning virtual
machine has been destroyed.
To put it differently, "struct kvm" is a KVM software construct that _usually_,
but not always, is associated 1:1 with a virtual machine.
And FWIW, stashing the pointer without holding a reference would not be a complete
solution, because it couldn't guard against KVM reusing a pointer. E.g. if a
struct kvm was unbound and then freed, KVM could reuse the same memory for a new
struct kvm, with a different ASID/HKID, and get a false negative on the rebinding
check.
I agree that inode (physical memory) is coupled to the virtual machine
as a more generic concept.
I was hoping that in the absence of CC hardware providing a HKID/ASID,
the struct kvm pointer could act as a representation of the "virtual
machine". You're definitely right that KVM could reuse a pointer and so
that idea doesn't stand.
I thought about generating UUIDs to represent "virtual machines" in the
absence of CC hardware, and this UUID could be transferred during
intra-host migration, but this still doesn't take host userspace out of
the TCB. A malicious host VMM could just use the migration ioctl to copy
the UUID to a malicious dumper VM, which would then pass checks with a
gmem file linked to the malicious dumper VM. This is fine for HKID/ASIDs
because the memory is encrypted; with UUIDs there's no memory
encryption.
I don't understand what problem you're trying to solve. I don't see a need to
provide a single concrete representation/definition of a "virtual machine". E.g.
there's no need for a formal definition to securely perform intrahost migration,
KVM just needs to ensure that the migration doesn't compromise guest security,
functionality, etc.
That gets a lot more complex if the target KVM instance (module, not "struct kvm")
is a different KVM, e.g. when migrating to a different host. Then there needs to
be a way to attest that the target is trusted and whatnot, but that still doesn't
require there to be a formal definition of a "virtual machine".
Circling back to the original topic, was associating the file with
struct kvm at gmem file creation time meant to constrain the use of the
gmem file to one struct kvm, or one virtual machine, or something else?
It's meant to keep things as simple as possible (relatively speaking). A 1:1
association between a KVM instance and a gmem instance means we don't have to
worry about the edge cases and oddities I pointed out earlier in this thread.
Follow up questions:
1. Since the physical memory's representation is the inode and should be
coupled to the virtual machine (as a concept, not struct kvm), should
the binding/coupling be with the file, or the inode?
Both. The @kvm instance is bound to a file, because the file is that @kvm's view
of the underlying memory, e.g. effectively provides the translation of guest
addresses to host memory. The @kvm instance is indirectly bound to the inode
because the file is bound to the inode.
2. Should struct kvm still be bound to the file/inode at gmem file
creation time, since
Yes.
+ struct kvm isn't a good representation of a "virtual machine"
I don't see how this is relevant, because as above, I don't see why we need a
canonical represenation of a virtual machine.
+ we currently don't have anything that really represents a "virtual
machine" without hardware support
HKIDs and ASIDs don't provide a "virtual machine" representation either. E.g. if
a TDX guest is live migrated to a different host, it will likely have a different
HKID, and definitely have a different encryption key, but it's still the same
virtual machine.
I'd also like to bring up another userspace use case that Google has:
re-use of gmem files for rebooting guests when the KVM instance is
destroyed and rebuilt.
When rebooting a VM there are some steps relating to gmem that are
performance-sensitive:
If we (Google) really cared about performance, then we shouldn't destroy and recreate
the VM in the first place. E.g. the cost of zapping, freeing, re-allocating and
re-populating SPTEs is far from trivial. Pulling RESET shouldn't change what
memory that is assigned to a VM, and reseting stats is downright bizarre IMO.
In other words, I think Google's approach of destroying the VM to emulate a reboot
is asinine. I'm not totally against extending KVM's uAPI to play nice with such
an approach, but I'm not exactly sympathetic either.
a. Zeroing pages from the old VM when we close a gmem file/inode
b. Deallocating pages from the old VM when we close a gmem file/inode
c. Allocating pages for the new VM from the new gmem file/inode
d. Zeroing pages on page allocation
We want to reuse the gmem file to save re-allocating pages (b. and c.),
and one of the two page zeroing allocations (a. or d.).
Binding the gmem file to a struct kvm on creation time means the gmem
file can't be reused with another VM on reboot.
Not without KVM's assistance, which userspace will need for TDX and SNP VMs no
matter what, e.g. to ensure the new and old KVM instance get the same HKID/ASID.
And we've already mapped out the more complex case of intrahost migration, so I
don't expect this to be at all challenging to implement.
Also, host userspace is forced to close the gmem file to allow the old VM to
be freed.
Yes, but that can happen after the "new" VM has instantiated its file/view of
guest memory.
For other places where files pin KVM, like the stats fd pinning vCPUs, I
guess that matters less since there isn't much of a penalty to close and
re-open the stats fd.
On Mon, Aug 28, 2023, Elliot Berman wrote:
I had a 3rd question that's related to how to wire the gmem up to a virtual
machine:
I learned of a usecase to implement copy-on-write for gmem. The premise
would be to have a "golden copy" of the memory that multiple virtual
machines can map in as RO. If a virtual machine tries to write to those
pages, they get copied to a virtual machine-specific page that isn't shared
with other VMs. How do we track those pages?
The answer is going to be gunyah specific, because gmem itself isn't designed to
provide a virtualization layer ("virtual" in the virtual memory sense, not in the
virtual machine sense). Like any other CoW implementation, the RO page would need
to be copied to a different physical page, and whatever layer translates gfns
to physical pages would need to be updated. E.g. in gmem terms, allocate a new
gmem page/instance and update the gfn=>gmem[offset] translation in KVM/gunyah.
For VMA-based memory, that translation happens in the primary MMU, and is largely
transparent to KVM (or any other secondary MMU). E.g. the primary MMU works with
the backing store (if necessary) to allocate a new page and do the copy, notifies
secondary MMUs, zaps the old PTE(s), and then installs the new PTE(s). KVM/gunyah
just needs to react to the mmu_notifier event, e.g. zap secondary MMU PTEs, and
then KVM/gunyah naturally gets the new, writable page/PTE when following the host
virtual address, e.g. via gup().
The downside of eliminating the middle-man (primary MMU) from gmem is that the
"owner" (KVM or gunyah) is now responsible for these types of operations. For some
things, e.g. page migration, it's actually easier in some ways, but for CoW it's
quite a bit more work for KVM/gunyah because KVM/gunyah now needs to do things
that were previously handled by the primary MMU.
In KVM, assuming no additional support in KVM, doing CoW would mean modifying
memslots to redirect the gfn from the RO page to the writable page. For a variety
of reasons, that would be _extremely_ expensive in KVM, but still possible. If
there were a strong use case for supporting CoW with KVM+gmem, then I suspect that
we'd probably implement new KVM uAPI of some form to provide reasonable performance.
But I highly doubt we'll ever do that, because one of core tenets of KVM+gmem is
to isolate guest memory from the rest of the world, and especially from host
userspace, and that just doesn't mesh well with CoW'd memory being shared across
multiple VMs.
Sean Christopherson [off-list ref] writes:
On Mon, Aug 28, 2023, Ackerley Tng wrote:
quoted
Sean Christopherson [off-list ref] writes:
quoted
quoted
If we track struct kvm with the inode, then I think (a), (b) and (c) can
be independent of the refcounting method. What do you think?
No go. Because again, the inode (physical memory) is coupled to the virtual machine
as a thing, not to a "struct kvm". Or more concretely, the inode is coupled to an
ASID or an HKID, and there can be multiple "struct kvm" objects associated with a
single ASID. And at some point in the future, I suspect we'll have multiple KVM
objects per HKID too.
The current SEV use case is for the migration helper, where two KVM objects share
a single ASID (the "real" VM and the helper). I suspect TDX will end up with
similar behavior where helper "VMs" can use the HKID of the "real" VM. For KVM,
that means multiple struct kvm objects being associated with a single HKID.
To prevent use-after-free, KVM "just" needs to ensure the helper instances can't
outlive the real instance, i.e. can't use the HKID/ASID after the owning virtual
machine has been destroyed.
To put it differently, "struct kvm" is a KVM software construct that _usually_,
but not always, is associated 1:1 with a virtual machine.
And FWIW, stashing the pointer without holding a reference would not be a complete
solution, because it couldn't guard against KVM reusing a pointer. E.g. if a
struct kvm was unbound and then freed, KVM could reuse the same memory for a new
struct kvm, with a different ASID/HKID, and get a false negative on the rebinding
check.
I agree that inode (physical memory) is coupled to the virtual machine
as a more generic concept.
I was hoping that in the absence of CC hardware providing a HKID/ASID,
the struct kvm pointer could act as a representation of the "virtual
machine". You're definitely right that KVM could reuse a pointer and so
that idea doesn't stand.
I thought about generating UUIDs to represent "virtual machines" in the
absence of CC hardware, and this UUID could be transferred during
intra-host migration, but this still doesn't take host userspace out of
the TCB. A malicious host VMM could just use the migration ioctl to copy
the UUID to a malicious dumper VM, which would then pass checks with a
gmem file linked to the malicious dumper VM. This is fine for HKID/ASIDs
because the memory is encrypted; with UUIDs there's no memory
encryption.
I don't understand what problem you're trying to solve. I don't see a need to
provide a single concrete representation/definition of a "virtual machine". E.g.
there's no need for a formal definition to securely perform intrahost migration,
KVM just needs to ensure that the migration doesn't compromise guest security,
functionality, etc.
That gets a lot more complex if the target KVM instance (module, not "struct kvm")
is a different KVM, e.g. when migrating to a different host. Then there needs to
be a way to attest that the target is trusted and whatnot, but that still doesn't
require there to be a formal definition of a "virtual machine".
quoted
Circling back to the original topic, was associating the file with
struct kvm at gmem file creation time meant to constrain the use of the
gmem file to one struct kvm, or one virtual machine, or something else?
It's meant to keep things as simple as possible (relatively speaking). A 1:1
association between a KVM instance and a gmem instance means we don't have to
worry about the edge cases and oddities I pointed out earlier in this thread.
I looked through this thread again and re-read the edge cases and
oddities that was pointed out earlier (last paragraph at [1]) and I
think I understand better, and I have just one last clarification.
It was previously mentioned that binding on creation time simplifies the
lifecycle of memory:
"(a) prevent a different VM from *ever* binding to the gmem instance" [1]
Does this actually mean
"prevent a different struct kvm from *ever* binding to this gmem file"
?
If so, then binding on creation
+ Makes the gmem *file* (and just not the bindings xarray) the binding
between struct kvm and the file.
+ Simplifies the KVM-userspace contract to "this gmem file can only be
used with this struct kvm"
Binding on creation doesn't offer any way to block the contents of the
inode from being used with another "virtual machine" though, since we
can have more than one gmem file pointing to the same inode, and the
other gmem file is associated with another struct kvm. (And a strut kvm
isn't associated 1:1 with a virtual machine [2])
The point about an inode needing to be coupled to a virtual machine as a
thing [2] led me to try to find a single concrete representation of a
"virtual machine".
Is locking inode contents to a "virtual machine" outside the scope of
gmem? If so, then it is fine to bind on creation time, use a VM ioctl
over a system ioctl, and the method of refcounting in gmem v12 is okay.
[1] https://lore.kernel.org/lkml/ZNKv9ul2I7A4V7IF@google.com/
[2] https://lore.kernel.org/lkml/ZOO782YGRY0YMuPu@google.com/
<snip>
On Thu, Sep 14, 2023, Ackerley Tng wrote:
Sean Christopherson [off-list ref] writes:
quoted
On Mon, Aug 28, 2023, Ackerley Tng wrote:
quoted
Sean Christopherson [off-list ref] writes:
quoted
quoted
If we track struct kvm with the inode, then I think (a), (b) and (c) can
be independent of the refcounting method. What do you think?
No go. Because again, the inode (physical memory) is coupled to the virtual machine
as a thing, not to a "struct kvm". Or more concretely, the inode is coupled to an
ASID or an HKID, and there can be multiple "struct kvm" objects associated with a
single ASID. And at some point in the future, I suspect we'll have multiple KVM
objects per HKID too.
The current SEV use case is for the migration helper, where two KVM objects share
a single ASID (the "real" VM and the helper). I suspect TDX will end up with
similar behavior where helper "VMs" can use the HKID of the "real" VM. For KVM,
that means multiple struct kvm objects being associated with a single HKID.
To prevent use-after-free, KVM "just" needs to ensure the helper instances can't
outlive the real instance, i.e. can't use the HKID/ASID after the owning virtual
machine has been destroyed.
To put it differently, "struct kvm" is a KVM software construct that _usually_,
but not always, is associated 1:1 with a virtual machine.
And FWIW, stashing the pointer without holding a reference would not be a complete
solution, because it couldn't guard against KVM reusing a pointer. E.g. if a
struct kvm was unbound and then freed, KVM could reuse the same memory for a new
struct kvm, with a different ASID/HKID, and get a false negative on the rebinding
check.
I agree that inode (physical memory) is coupled to the virtual machine
as a more generic concept.
I was hoping that in the absence of CC hardware providing a HKID/ASID,
the struct kvm pointer could act as a representation of the "virtual
machine". You're definitely right that KVM could reuse a pointer and so
that idea doesn't stand.
I thought about generating UUIDs to represent "virtual machines" in the
absence of CC hardware, and this UUID could be transferred during
intra-host migration, but this still doesn't take host userspace out of
the TCB. A malicious host VMM could just use the migration ioctl to copy
the UUID to a malicious dumper VM, which would then pass checks with a
gmem file linked to the malicious dumper VM. This is fine for HKID/ASIDs
because the memory is encrypted; with UUIDs there's no memory
encryption.
I don't understand what problem you're trying to solve. I don't see a need to
provide a single concrete representation/definition of a "virtual machine". E.g.
there's no need for a formal definition to securely perform intrahost migration,
KVM just needs to ensure that the migration doesn't compromise guest security,
functionality, etc.
That gets a lot more complex if the target KVM instance (module, not "struct kvm")
is a different KVM, e.g. when migrating to a different host. Then there needs to
be a way to attest that the target is trusted and whatnot, but that still doesn't
require there to be a formal definition of a "virtual machine".
quoted
Circling back to the original topic, was associating the file with
struct kvm at gmem file creation time meant to constrain the use of the
gmem file to one struct kvm, or one virtual machine, or something else?
It's meant to keep things as simple as possible (relatively speaking). A 1:1
association between a KVM instance and a gmem instance means we don't have to
worry about the edge cases and oddities I pointed out earlier in this thread.
I looked through this thread again and re-read the edge cases and
oddities that was pointed out earlier (last paragraph at [1]) and I
think I understand better, and I have just one last clarification.
It was previously mentioned that binding on creation time simplifies the
lifecycle of memory:
"(a) prevent a different VM from *ever* binding to the gmem instance" [1]
Does this actually mean
"prevent a different struct kvm from *ever* binding to this gmem file"
?
Yes.
If so, then binding on creation
+ Makes the gmem *file* (and just not the bindings xarray) the binding
between struct kvm and the file.
Yep.
+ Simplifies the KVM-userspace contract to "this gmem file can only be
used with this struct kvm"
Yep.
Binding on creation doesn't offer any way to block the contents of the
inode from being used with another "virtual machine" though, since we
can have more than one gmem file pointing to the same inode, and the
other gmem file is associated with another struct kvm. (And a strut kvm
isn't associated 1:1 with a virtual machine [2])
Yep.
The point about an inode needing to be coupled to a virtual machine as a
thing [2] led me to try to find a single concrete representation of a
"virtual machine".
Is locking inode contents to a "virtual machine" outside the scope of
gmem?
Yes, because it's not gmem's responsibility to define "secure" (from a guest
perspective) or "safe" (from a platform stability and correctness perspective).
E.g. inserting additional vCPUs into the VM a la the SEV migration helper thing
is comically insecure without some way to attest the helper code. Building policy
into the host kernel/KVM to do that attestation or otherwise determine what code
is/isn't safe for the guest to run is firmly out-of-scope. KVM can certainly
provide the tools and help with enforcement, but the policy needs to be defined
elsewhere. Even for something like pKVM, where KVM is in the TCB, KVM still doesn't
define who/what to trust (though KVM is heavily involved in enforcing security
stuff).
And for platform safety, e.g. not allowing two VMs to use the same HKID (ignoring
helpers for the moment), that's a KVM problem but NOT a gmem problem. The point
I raised in link[2] about a gmem inode and thus the HKID/ASID associated with the
inode being bound to the "virtual machine" still holds true, but (a) it's not a
1:1 correlation, e.g. a VM could utilize multiple gmem inodes (all with the same
HKID/ASID), and (b) the safety and functional correctness aspects aren't unique
to gmem, e.g. even when when gmem isn't in the picture, KVM needs to make sure it
manages ASIDs correctly. The only difference with SNP in the picture is that if
KVM screws up ASID management, bad things happen to the host, not (just) the guest.
If so, then it is fine to bind on creation time, use a VM ioctl
over a system ioctl, and the method of refcounting in gmem v12 is okay.
[1] https://lore.kernel.org/lkml/ZNKv9ul2I7A4V7IF@google.com/
[2] https://lore.kernel.org/lkml/ZOO782YGRY0YMuPu@google.com/
quoted
<snip>