KVM: MMU: always terminate page walks at level 1
is_last_gpte() is not equivalent to the pseudo-code given in commit6bb69c9b69
("KVM: MMU: simplify last_pte_bitmap") because an incorrect value of last_nonleaf_level may override the result even if level == 1. It is critical for is_last_gpte() to return true on level == 1 to terminate page walks. Otherwise memory corruption may occur as level is used as an index to various data structures throughout the page walking code. Even though the actual bug would be wherever the MMU is initialized (as in the previous patch), be defensive and ensure here that is_last_gpte() returns the correct value. This patch is also enough to fix CVE-2017-12188. Fixes:6bb69c9b69
Cc: stable@vger.kernel.org Cc: Andy Honig <ahonig@google.com> Signed-off-by: Ladi Prosek <lprosek@redhat.com> [Panic if walk_addr_generic gets an incorrect level; this is a serious bug and it's not worth a WARN_ON where the recovery path might hide further exploitable issues; suggested by Andrew Honig. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Родитель
fd19d3b451
Коммит
829ee279ae
|
@ -3973,13 +3973,6 @@ static bool sync_mmio_spte(struct kvm_vcpu *vcpu, u64 *sptep, gfn_t gfn,
|
|||
static inline bool is_last_gpte(struct kvm_mmu *mmu,
|
||||
unsigned level, unsigned gpte)
|
||||
{
|
||||
/*
|
||||
* PT_PAGE_TABLE_LEVEL always terminates. The RHS has bit 7 set
|
||||
* iff level <= PT_PAGE_TABLE_LEVEL, which for our purpose means
|
||||
* level == PT_PAGE_TABLE_LEVEL; set PT_PAGE_SIZE_MASK in gpte then.
|
||||
*/
|
||||
gpte |= level - PT_PAGE_TABLE_LEVEL - 1;
|
||||
|
||||
/*
|
||||
* The RHS has bit 7 set iff level < mmu->last_nonleaf_level.
|
||||
* If it is clear, there are no large pages at this level, so clear
|
||||
|
@ -3987,6 +3980,13 @@ static inline bool is_last_gpte(struct kvm_mmu *mmu,
|
|||
*/
|
||||
gpte &= level - mmu->last_nonleaf_level;
|
||||
|
||||
/*
|
||||
* PT_PAGE_TABLE_LEVEL always terminates. The RHS has bit 7 set
|
||||
* iff level <= PT_PAGE_TABLE_LEVEL, which for our purpose means
|
||||
* level == PT_PAGE_TABLE_LEVEL; set PT_PAGE_SIZE_MASK in gpte then.
|
||||
*/
|
||||
gpte |= level - PT_PAGE_TABLE_LEVEL - 1;
|
||||
|
||||
return gpte & PT_PAGE_SIZE_MASK;
|
||||
}
|
||||
|
||||
|
|
|
@ -334,10 +334,11 @@ retry_walk:
|
|||
--walker->level;
|
||||
|
||||
index = PT_INDEX(addr, walker->level);
|
||||
|
||||
table_gfn = gpte_to_gfn(pte);
|
||||
offset = index * sizeof(pt_element_t);
|
||||
pte_gpa = gfn_to_gpa(table_gfn) + offset;
|
||||
|
||||
BUG_ON(walker->level < 1);
|
||||
walker->table_gfn[walker->level - 1] = table_gfn;
|
||||
walker->pte_gpa[walker->level - 1] = pte_gpa;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче