Project

General

Profile

Actions

Bug #13170

closed

fix SVM instruction intercepts in bhyve

Added by Patrick Mooney about 3 years ago. Updated about 3 years ago.

Status:
Closed
Priority:
Normal
Category:
bhyve
Start date:
Due date:
% Done:

100%

Estimated time:
Difficulty:
Medium
Tags:
bhyve
Gerrit CR:
External Bug:

Description

It was reported that bhyve was not establishing the proper interception for SVM-related instructions. This meant that a guest could issue instructions like vmsave, which take a physical memory address as their argument, and result in writes to that physical host address. Given that the SVM capability not reported as available for guests (via CPUID), it would be appropriate for all of these instructions to result in #UD. This was reported to FreeBSD by Maxime Villard.

Actions #1

Updated by Electric Monk about 3 years ago

  • Gerrit CR set to 942
Actions #2

Updated by Electric Monk about 3 years ago

  • Status changed from New to Closed
  • % Done changed from 0 to 100

git commit 8c2fd2ffa72935b73b4236eeb5745a22f782f780

commit  8c2fd2ffa72935b73b4236eeb5745a22f782f780
Author: Patrick Mooney <pmooney@pfmooney.com>
Date:   2020-09-15T21:32:31.000Z

    13170 fix SVM instruction intercepts in bhyve
    Reviewed by: Robert Mustacchi <rm@fingolfin.org>
    Reviewed by: Joshua M. Clulow <josh@sysmgr.org>
    Approved by: Dan McDonald <danmcd@joyent.com>

Actions #3

Updated by Patrick Mooney about 3 years ago

  • Description updated (diff)
Actions #4

Updated by Patrick Mooney about 3 years ago

To test this fix, I drew up a small kernel module so I could issue the instructions from ring 0 in an OmniOSCE guest. I verified that all of the newly-intercepted privileged instructions resulted in a #UD being raised on that CPU. Without the fix, I confirmed that all the instructions in question did complete successfully, and in the case of vmsave, I could see the changes written to host-physical memory.

Actions #5

Updated by Patrick Mooney about 3 years ago

Source of that test module I used to exercise those instructions:

amdvuln.c

/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 *
 * Copyright 2020 Oxide Computer Company
 */

#include <sys/types.h>
#include <sys/errno.h>
#include <sys/modctl.h>

void amdvuln_vmsave(uintptr_t pa);
void amdvuln_vmload(uintptr_t pa);
void amdvuln_clgi(void);
void amdvuln_stgi(void);
void amdvuln_invd(void);
void amdvuln_invlpga(uintptr_t pa, uint_t asid);
void amdvuln_skinit(uint_t base);

#define TEST_PA         0x2000
#define TEST_ASID       1
#define TEST_BASE       0xffff0000

int
_init(void)
{
        /* Choose your own adventure */

        //amdvuln_vmsave(TEST_PA);
        //amdvuln_vmload(TEST_PA);
        //amdvuln_clgi();
        //amdvuln_stgi();
        //amdvuln_invd();
        //amdvuln_invlpga(TEST_PA, TEST_ASID);
        //amdvuln_skinit(TEST_BASE);

        return (EINVAL);
}

int
_fini(void)
{
        return (0);
}

int
_info(struct modinfo *modinfop)
{
        return (EINVAL);
}

amdvuln_ml.s

/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 *
 * Copyright 2020 Oxide Computer Company
 */

#include <sys/asm_linkage.h>

ENTRY_NP(amdvuln_vmsave)
        movq    %rdi, %rax
        vmsave
        ret
SET_SIZE(amdvuln_vmsave)

ENTRY_NP(amdvuln_vmload)
        movq    %rdi, %rax
        vmload
        ret
SET_SIZE(amdvuln_vmload)

ENTRY_NP(amdvuln_clgi)
        clgi
        ret
SET_SIZE(amdvuln_clgi)

ENTRY_NP(amdvuln_stgi)
        stgi
        ret
SET_SIZE(amdvuln_stgi)

ENTRY_NP(amdvuln_invd)
        invd
        ret
SET_SIZE(amdvuln_invd)

ENTRY_NP(amdvuln_invlpga)
        movq    %rdi, %rax
        movl    %esi, %ecx
        invlpga
        ret
SET_SIZE(amdvuln_invlpga)

ENTRY_NP(amdvuln_skinit)
        movl    %edi, %eax
        skinit
        ret
SET_SIZE(amdvuln_skinit)

Makefile

#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source.  A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# Copyright 2020 Oxide Computer Company
#

#
#       Path to the base of the uts directory tree (usually /usr/src/uts).
#
UTSBASE = ../..

#
#       Define the module and object file sets.
#
MODULE          = amdvuln
AMDVULN_OBJS    = amdvuln.o amdvuln_ml.o
OBJECTS         = $(AMDVULN_OBJS:%=$(OBJS_DIR)/%)
ROOTMODULE      = $(USR_DRV_DIR)/$(MODULE)
CONF_SRCDIR     = $(UTSBASE)/i86pc/io/amdvuln

#
#       Include common rules.
#
include $(UTSBASE)/i86pc/Makefile.i86pc

#
#       Define targets
#
ALL_TARGET      = $(BINARY)
INSTALL_TARGET  = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)

#
#       Overrides and additions
#
ALL_BUILDS      = $(ALL_BUILDSONLY64)
DEF_BUILDS      = $(DEF_BUILDSONLY64)

LDFLAGS         += -dy

#
#       Default build targets.
#
.KEEP_STATE:

def:            $(DEF_DEPS)

all:            $(ALL_DEPS)

clean:          $(CLEAN_DEPS)

clobber:        $(CLOBBER_DEPS)

install:        $(INSTALL_DEPS)

#
#       Include common targets.
#
include $(UTSBASE)/i86pc/Makefile.targ

Actions

Also available in: Atom PDF