Commit 8ece53ef authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Borislav Petkov
Browse files

x86/vm86/32: Remove VM86_SCREEN_BITMAP support



The implementation was rather buggy.  It unconditionally marked PTEs
read-only, even for VM_SHARED mappings.  I'm not sure whether this is
actually a problem, but it certainly seems unwise.  More importantly, it
released the mmap lock before flushing the TLB, which could allow a racing
CoW operation to falsely believe that the underlying memory was not
writable.

I can't find any users at all of this mechanism, so just remove it.

Signed-off-by: default avatarAndy Lutomirski <luto@kernel.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Acked-by: default avatarStas Sergeev <stsp2@yandex.ru>
Link: https://lkml.kernel.org/r/f3086de0babcab36f69949b5780bde851f719bc8.1611078018.git.luto@kernel.org
parent b86cb292
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ struct vm86 {
	unsigned long saved_sp0;

	unsigned long flags;
	unsigned long screen_bitmap;
	unsigned long cpu_type;
	struct revectored_struct int_revectored;
	struct revectored_struct int21_revectored;
+2 −2
Original line number Diff line number Diff line
@@ -97,7 +97,7 @@ struct revectored_struct {
struct vm86_struct {
	struct vm86_regs regs;
	unsigned long flags;
	unsigned long screen_bitmap;
	unsigned long screen_bitmap;		/* unused, preserved by vm86() */
	unsigned long cpu_type;
	struct revectored_struct int_revectored;
	struct revectored_struct int21_revectored;
@@ -106,7 +106,7 @@ struct vm86_struct {
/*
 * flags masks
 */
#define VM86_SCREEN_BITMAP	0x0001
#define VM86_SCREEN_BITMAP	0x0001        /* no longer supported */

struct vm86plus_info_struct {
	unsigned long force_return_for_pic:1;
+14 −48
Original line number Diff line number Diff line
@@ -134,7 +134,11 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)
	unsafe_put_user(regs->ds, &user->regs.ds, Efault_end);
	unsafe_put_user(regs->fs, &user->regs.fs, Efault_end);
	unsafe_put_user(regs->gs, &user->regs.gs, Efault_end);
	unsafe_put_user(vm86->screen_bitmap, &user->screen_bitmap, Efault_end);

	/*
	 * Don't write screen_bitmap in case some user had a value there
	 * and expected it to remain unchanged.
	 */

	user_access_end();

@@ -160,49 +164,6 @@ void save_v86_state(struct kernel_vm86_regs *regs, int retval)
	do_exit(SIGSEGV);
}

static void mark_screen_rdonly(struct mm_struct *mm)
{
	struct vm_area_struct *vma;
	spinlock_t *ptl;
	pgd_t *pgd;
	p4d_t *p4d;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	int i;

	mmap_write_lock(mm);
	pgd = pgd_offset(mm, 0xA0000);
	if (pgd_none_or_clear_bad(pgd))
		goto out;
	p4d = p4d_offset(pgd, 0xA0000);
	if (p4d_none_or_clear_bad(p4d))
		goto out;
	pud = pud_offset(p4d, 0xA0000);
	if (pud_none_or_clear_bad(pud))
		goto out;
	pmd = pmd_offset(pud, 0xA0000);

	if (pmd_trans_huge(*pmd)) {
		vma = find_vma(mm, 0xA0000);
		split_huge_pmd(vma, pmd, 0xA0000);
	}
	if (pmd_none_or_clear_bad(pmd))
		goto out;
	pte = pte_offset_map_lock(mm, pmd, 0xA0000, &ptl);
	for (i = 0; i < 32; i++) {
		if (pte_present(*pte))
			set_pte(pte, pte_wrprotect(*pte));
		pte++;
	}
	pte_unmap_unlock(pte, ptl);
out:
	mmap_write_unlock(mm);
	flush_tlb_mm_range(mm, 0xA0000, 0xA0000 + 32*PAGE_SIZE, PAGE_SHIFT, false);
}



static int do_vm86_irq_handling(int subfunction, int irqnumber);
static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus);

@@ -282,6 +243,15 @@ static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus)
			offsetof(struct vm86_struct, int_revectored)))
		return -EFAULT;


	/* VM86_SCREEN_BITMAP had numerous bugs and appears to have no users. */
	if (v.flags & VM86_SCREEN_BITMAP) {
		char comm[TASK_COMM_LEN];

		pr_info_once("vm86: '%s' uses VM86_SCREEN_BITMAP, which is no longer supported\n", get_task_comm(comm, current));
		return -EINVAL;
	}

	memset(&vm86regs, 0, sizeof(vm86regs));

	vm86regs.pt.bx = v.regs.ebx;
@@ -302,7 +272,6 @@ static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus)
	vm86regs.gs = v.regs.gs;

	vm86->flags = v.flags;
	vm86->screen_bitmap = v.screen_bitmap;
	vm86->cpu_type = v.cpu_type;

	if (copy_from_user(&vm86->int_revectored,
@@ -370,9 +339,6 @@ static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus)
	update_task_stack(tsk);
	preempt_enable();

	if (vm86->flags & VM86_SCREEN_BITMAP)
		mark_screen_rdonly(tsk->mm);

	memcpy((struct kernel_vm86_regs *)regs, &vm86regs, sizeof(vm86regs));
	return regs->ax;
}
+0 −30
Original line number Diff line number Diff line
@@ -262,25 +262,6 @@ void arch_sync_kernel_mappings(unsigned long start, unsigned long end)
	}
}

/*
 * Did it hit the DOS screen memory VA from vm86 mode?
 */
static inline void
check_v8086_mode(struct pt_regs *regs, unsigned long address,
		 struct task_struct *tsk)
{
#ifdef CONFIG_VM86
	unsigned long bit;

	if (!v8086_mode(regs) || !tsk->thread.vm86)
		return;

	bit = (address - 0xA0000) >> PAGE_SHIFT;
	if (bit < 32)
		tsk->thread.vm86->screen_bitmap |= 1 << bit;
#endif
}

static bool low_pfn(unsigned long pfn)
{
	return pfn < max_low_pfn;
@@ -335,15 +316,6 @@ KERN_ERR
"******* Disabling USB legacy in the BIOS may also help.\n";
#endif

/*
 * No vm86 mode in 64-bit mode:
 */
static inline void
check_v8086_mode(struct pt_regs *regs, unsigned long address,
		 struct task_struct *tsk)
{
}

static int bad_address(void *p)
{
	unsigned long dummy;
@@ -1416,8 +1388,6 @@ void do_user_addr_fault(struct pt_regs *regs,
		mm_fault_error(regs, hw_error_code, address, fault);
		return;
	}

	check_v8086_mode(regs, address, tsk);
}
NOKPROBE_SYMBOL(do_user_addr_fault);