Skip to content

Commit

Permalink
xen-pciback: limit guest control of command register
Browse files Browse the repository at this point in the history
Otherwise the guest can abuse that control to cause e.g. PCIe
Unsupported Request responses by disabling memory and/or I/O decoding
and subsequently causing (CPU side) accesses to the respective address
ranges, which (depending on system configuration) may be fatal to the
host.

Note that to alter any of the bits collected together as
PCI_COMMAND_GUEST permissive mode is now required to be enabled
globally or on the specific device.

This is CVE-2015-2150 / XSA-120.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
  • Loading branch information
jbeulich authored and David Vrabel committed Mar 11, 2015
1 parent 85e40b0 commit af6fc85
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 14 deletions.
2 changes: 1 addition & 1 deletion drivers/xen/xen-pciback/conf_space.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "conf_space.h"
#include "conf_space_quirks.h"

static bool permissive;
bool permissive;
module_param(permissive, bool, 0644);

/* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word,
Expand Down
2 changes: 2 additions & 0 deletions drivers/xen/xen-pciback/conf_space.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ struct config_field_entry {
void *data;
};

extern bool permissive;

#define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset)

/* Add fields to a device - the add_fields macro expects to get a pointer to
Expand Down
61 changes: 48 additions & 13 deletions drivers/xen/xen-pciback/conf_space_header.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
#include "pciback.h"
#include "conf_space.h"

struct pci_cmd_info {
u16 val;
};

struct pci_bar_info {
u32 val;
u32 len_val;
Expand All @@ -20,29 +24,45 @@ struct pci_bar_info {
#define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
#define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)

static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
/* Bits guests are allowed to control in permissive mode. */
#define PCI_COMMAND_GUEST (PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| \
PCI_COMMAND_INVALIDATE|PCI_COMMAND_VGA_PALETTE| \
PCI_COMMAND_WAIT|PCI_COMMAND_FAST_BACK)

static void *command_init(struct pci_dev *dev, int offset)
{
int i;
int ret;

ret = xen_pcibk_read_config_word(dev, offset, value, data);
if (!pci_is_enabled(dev))
return ret;

for (i = 0; i < PCI_ROM_RESOURCE; i++) {
if (dev->resource[i].flags & IORESOURCE_IO)
*value |= PCI_COMMAND_IO;
if (dev->resource[i].flags & IORESOURCE_MEM)
*value |= PCI_COMMAND_MEMORY;
struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
int err;

if (!cmd)
return ERR_PTR(-ENOMEM);

err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val);
if (err) {
kfree(cmd);
return ERR_PTR(err);
}

return cmd;
}

static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
{
int ret = pci_read_config_word(dev, offset, value);
const struct pci_cmd_info *cmd = data;

*value &= PCI_COMMAND_GUEST;
*value |= cmd->val & ~PCI_COMMAND_GUEST;

return ret;
}

static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
{
struct xen_pcibk_dev_data *dev_data;
int err;
u16 val;
struct pci_cmd_info *cmd = data;

dev_data = pci_get_drvdata(dev);
if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
Expand Down Expand Up @@ -83,6 +103,19 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
}
}

cmd->val = value;

if (!permissive && (!dev_data || !dev_data->permissive))
return 0;

/* Only allow the guest to control certain bits. */
err = pci_read_config_word(dev, offset, &val);
if (err || val == value)
return err;

value &= PCI_COMMAND_GUEST;
value |= val & ~PCI_COMMAND_GUEST;

return pci_write_config_word(dev, offset, value);
}

Expand Down Expand Up @@ -282,6 +315,8 @@ static const struct config_field header_common[] = {
{
.offset = PCI_COMMAND,
.size = 2,
.init = command_init,
.release = bar_release,
.u.w.read = command_read,
.u.w.write = command_write,
},
Expand Down

0 comments on commit af6fc85

Please sign in to comment.