Skip to content

Commit af6fc85

Browse files
jbeulichDavid Vrabel
authored andcommitted
xen-pciback: limit guest control of command register
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>
1 parent 85e40b0 commit af6fc85

File tree

3 files changed

+51
-14
lines changed

3 files changed

+51
-14
lines changed

drivers/xen/xen-pciback/conf_space.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include "conf_space.h"
1717
#include "conf_space_quirks.h"
1818

19-
static bool permissive;
19+
bool permissive;
2020
module_param(permissive, bool, 0644);
2121

2222
/* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word,

drivers/xen/xen-pciback/conf_space.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ struct config_field_entry {
6464
void *data;
6565
};
6666

67+
extern bool permissive;
68+
6769
#define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset)
6870

6971
/* Add fields to a device - the add_fields macro expects to get a pointer to

drivers/xen/xen-pciback/conf_space_header.c

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#include "pciback.h"
1212
#include "conf_space.h"
1313

14+
struct pci_cmd_info {
15+
u16 val;
16+
};
17+
1418
struct pci_bar_info {
1519
u32 val;
1620
u32 len_val;
@@ -20,29 +24,45 @@ struct pci_bar_info {
2024
#define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
2125
#define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
2226

23-
static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
27+
/* Bits guests are allowed to control in permissive mode. */
28+
#define PCI_COMMAND_GUEST (PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| \
29+
PCI_COMMAND_INVALIDATE|PCI_COMMAND_VGA_PALETTE| \
30+
PCI_COMMAND_WAIT|PCI_COMMAND_FAST_BACK)
31+
32+
static void *command_init(struct pci_dev *dev, int offset)
2433
{
25-
int i;
26-
int ret;
27-
28-
ret = xen_pcibk_read_config_word(dev, offset, value, data);
29-
if (!pci_is_enabled(dev))
30-
return ret;
31-
32-
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
33-
if (dev->resource[i].flags & IORESOURCE_IO)
34-
*value |= PCI_COMMAND_IO;
35-
if (dev->resource[i].flags & IORESOURCE_MEM)
36-
*value |= PCI_COMMAND_MEMORY;
34+
struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
35+
int err;
36+
37+
if (!cmd)
38+
return ERR_PTR(-ENOMEM);
39+
40+
err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val);
41+
if (err) {
42+
kfree(cmd);
43+
return ERR_PTR(err);
3744
}
3845

46+
return cmd;
47+
}
48+
49+
static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
50+
{
51+
int ret = pci_read_config_word(dev, offset, value);
52+
const struct pci_cmd_info *cmd = data;
53+
54+
*value &= PCI_COMMAND_GUEST;
55+
*value |= cmd->val & ~PCI_COMMAND_GUEST;
56+
3957
return ret;
4058
}
4159

4260
static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
4361
{
4462
struct xen_pcibk_dev_data *dev_data;
4563
int err;
64+
u16 val;
65+
struct pci_cmd_info *cmd = data;
4666

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

106+
cmd->val = value;
107+
108+
if (!permissive && (!dev_data || !dev_data->permissive))
109+
return 0;
110+
111+
/* Only allow the guest to control certain bits. */
112+
err = pci_read_config_word(dev, offset, &val);
113+
if (err || val == value)
114+
return err;
115+
116+
value &= PCI_COMMAND_GUEST;
117+
value |= val & ~PCI_COMMAND_GUEST;
118+
86119
return pci_write_config_word(dev, offset, value);
87120
}
88121

@@ -282,6 +315,8 @@ static const struct config_field header_common[] = {
282315
{
283316
.offset = PCI_COMMAND,
284317
.size = 2,
318+
.init = command_init,
319+
.release = bar_release,
285320
.u.w.read = command_read,
286321
.u.w.write = command_write,
287322
},

0 commit comments

Comments
 (0)