mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	KVM: VMX: Fix ds/es corruption on i386 with preemption
Commit b2da15ac26 ("KVM: VMX: Optimize %ds, %es reload") broke i386
in the following scenario:
  vcpu_load
  ...
  vmx_save_host_state
  vmx_vcpu_run
  (ds.rpl, es.rpl cleared by hardware)
  interrupt
    push ds, es  # pushes bad ds, es
    schedule
      vmx_vcpu_put
        vmx_load_host_state
          reload ds, es (with __USER_DS)
    pop ds, es  # of other thread's stack
    iret
  # other thread runs
  interrupt
    push ds, es
    schedule  # back in vcpu thread
    pop ds, es  # now with rpl=0
    iret
  ...
  vcpu_put
  resume_userspace
  iret  # clears ds, es due to mismatched rpl
(instead of resume_userspace, we might return with SYSEXIT and then
take an exception; when the exception IRETs we end up with cleared
ds, es)
Fix by avoiding the optimization on i386 and reloading ds, es on the
lightweight exit path.
Reported-by: Chris Clayron <chris2553@googlemail.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									4b6486659a
								
							
						
					
					
						commit
						aa67f6096c
					
				
					 1 changed files with 13 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -1488,13 +1488,6 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx)
 | 
			
		|||
		loadsegment(ds, vmx->host_state.ds_sel);
 | 
			
		||||
		loadsegment(es, vmx->host_state.es_sel);
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
	/*
 | 
			
		||||
	 * The sysexit path does not restore ds/es, so we must set them to
 | 
			
		||||
	 * a reasonable value ourselves.
 | 
			
		||||
	 */
 | 
			
		||||
	loadsegment(ds, __USER_DS);
 | 
			
		||||
	loadsegment(es, __USER_DS);
 | 
			
		||||
#endif
 | 
			
		||||
	reload_tss();
 | 
			
		||||
#ifdef CONFIG_X86_64
 | 
			
		||||
| 
						 | 
				
			
			@ -6370,6 +6363,19 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
 | 
			
		|||
#endif
 | 
			
		||||
	      );
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_X86_64
 | 
			
		||||
	/*
 | 
			
		||||
	 * The sysexit path does not restore ds/es, so we must set them to
 | 
			
		||||
	 * a reasonable value ourselves.
 | 
			
		||||
	 *
 | 
			
		||||
	 * We can't defer this to vmx_load_host_state() since that function
 | 
			
		||||
	 * may be executed in interrupt context, which saves and restore segments
 | 
			
		||||
	 * around it, nullifying its effect.
 | 
			
		||||
	 */
 | 
			
		||||
	loadsegment(ds, __USER_DS);
 | 
			
		||||
	loadsegment(es, __USER_DS);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
 | 
			
		||||
				  | (1 << VCPU_EXREG_RFLAGS)
 | 
			
		||||
				  | (1 << VCPU_EXREG_CPL)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue