Commit bd4b65ee5e5f750da709ac10c70266876e515c23
Committed by
Anthony Liguori
1 parent
6f4cbd39
qemu/pci: check constant registers on load
Add "cmask" table of constant register masks: if a bit is not writeable and is set in cmask table, this bit is checked on load. An attempt to load an image that would change such a register causes load to fail. Use this table to make sure that load does not modify registers that guest can not change (directly or indirectly). Note: we can't just assume that read-only registers never change, because the guest could change a register indirectly. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing
2 changed files
with
30 additions
and
1 deletions
hw/pci.c
@@ -152,13 +152,19 @@ void pci_device_save(PCIDevice *s, QEMUFile *f) | @@ -152,13 +152,19 @@ void pci_device_save(PCIDevice *s, QEMUFile *f) | ||
152 | 152 | ||
153 | int pci_device_load(PCIDevice *s, QEMUFile *f) | 153 | int pci_device_load(PCIDevice *s, QEMUFile *f) |
154 | { | 154 | { |
155 | + uint8_t config[PCI_CONFIG_SPACE_SIZE]; | ||
155 | uint32_t version_id; | 156 | uint32_t version_id; |
156 | int i; | 157 | int i; |
157 | 158 | ||
158 | version_id = qemu_get_be32(f); | 159 | version_id = qemu_get_be32(f); |
159 | if (version_id > 2) | 160 | if (version_id > 2) |
160 | return -EINVAL; | 161 | return -EINVAL; |
161 | - qemu_get_buffer(f, s->config, 256); | 162 | + qemu_get_buffer(f, config, sizeof config); |
163 | + for (i = 0; i < sizeof config; ++i) | ||
164 | + if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) | ||
165 | + return -EINVAL; | ||
166 | + memcpy(s->config, config, sizeof config); | ||
167 | + | ||
162 | pci_update_mappings(s); | 168 | pci_update_mappings(s); |
163 | 169 | ||
164 | if (version_id >= 2) | 170 | if (version_id >= 2) |
@@ -254,6 +260,18 @@ static PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) | @@ -254,6 +260,18 @@ static PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) | ||
254 | return pci_find_bus(bus); | 260 | return pci_find_bus(bus); |
255 | } | 261 | } |
256 | 262 | ||
263 | +static void pci_init_cmask(PCIDevice *dev) | ||
264 | +{ | ||
265 | + pci_set_word(dev->cmask + PCI_VENDOR_ID, 0xffff); | ||
266 | + pci_set_word(dev->cmask + PCI_DEVICE_ID, 0xffff); | ||
267 | + dev->cmask[PCI_STATUS] = PCI_STATUS_CAP_LIST; | ||
268 | + dev->cmask[PCI_REVISION_ID] = 0xff; | ||
269 | + dev->cmask[PCI_CLASS_PROG] = 0xff; | ||
270 | + pci_set_word(dev->cmask + PCI_CLASS_DEVICE, 0xffff); | ||
271 | + dev->cmask[PCI_HEADER_TYPE] = 0xff; | ||
272 | + dev->cmask[PCI_CAPABILITY_LIST] = 0xff; | ||
273 | +} | ||
274 | + | ||
257 | static void pci_init_wmask(PCIDevice *dev) | 275 | static void pci_init_wmask(PCIDevice *dev) |
258 | { | 276 | { |
259 | int i; | 277 | int i; |
@@ -286,6 +304,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, | @@ -286,6 +304,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, | ||
286 | pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); | 304 | pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); |
287 | memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state)); | 305 | memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state)); |
288 | pci_set_default_subsystem_id(pci_dev); | 306 | pci_set_default_subsystem_id(pci_dev); |
307 | + pci_init_cmask(pci_dev); | ||
289 | pci_init_wmask(pci_dev); | 308 | pci_init_wmask(pci_dev); |
290 | 309 | ||
291 | if (!config_read) | 310 | if (!config_read) |
@@ -385,6 +404,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, | @@ -385,6 +404,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, | ||
385 | } | 404 | } |
386 | *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type); | 405 | *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type); |
387 | *(uint32_t *)(pci_dev->wmask + addr) = cpu_to_le32(wmask); | 406 | *(uint32_t *)(pci_dev->wmask + addr) = cpu_to_le32(wmask); |
407 | + *(uint32_t *)(pci_dev->cmask + addr) = 0xffffffff; | ||
388 | } | 408 | } |
389 | 409 | ||
390 | static void pci_update_mappings(PCIDevice *d) | 410 | static void pci_update_mappings(PCIDevice *d) |
@@ -939,6 +959,8 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) | @@ -939,6 +959,8 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) | ||
939 | memset(pdev->used + offset, 0xFF, size); | 959 | memset(pdev->used + offset, 0xFF, size); |
940 | /* Make capability read-only by default */ | 960 | /* Make capability read-only by default */ |
941 | memset(pdev->wmask + offset, 0, size); | 961 | memset(pdev->wmask + offset, 0, size); |
962 | + /* Check capability by default */ | ||
963 | + memset(pdev->cmask + offset, 0xFF, size); | ||
942 | return offset; | 964 | return offset; |
943 | } | 965 | } |
944 | 966 | ||
@@ -951,6 +973,8 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) | @@ -951,6 +973,8 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) | ||
951 | pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; | 973 | pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; |
952 | /* Make capability writeable again */ | 974 | /* Make capability writeable again */ |
953 | memset(pdev->wmask + offset, 0xff, size); | 975 | memset(pdev->wmask + offset, 0xff, size); |
976 | + /* Clear cmask as device-specific registers can't be checked */ | ||
977 | + memset(pdev->cmask + offset, 0, size); | ||
954 | memset(pdev->used + offset, 0, size); | 978 | memset(pdev->used + offset, 0, size); |
955 | 979 | ||
956 | if (!pdev->config[PCI_CAPABILITY_LIST]) | 980 | if (!pdev->config[PCI_CAPABILITY_LIST]) |
hw/pci.h
@@ -101,6 +101,7 @@ typedef struct PCIIORegion { | @@ -101,6 +101,7 @@ typedef struct PCIIORegion { | ||
101 | #define PCI_COMMAND_MASTER 0x4 /* Enable bus master */ | 101 | #define PCI_COMMAND_MASTER 0x4 /* Enable bus master */ |
102 | #define PCI_STATUS 0x06 /* 16 bits */ | 102 | #define PCI_STATUS 0x06 /* 16 bits */ |
103 | #define PCI_REVISION_ID 0x08 /* 8 bits */ | 103 | #define PCI_REVISION_ID 0x08 /* 8 bits */ |
104 | +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ | ||
104 | #define PCI_CLASS_DEVICE 0x0a /* Device class */ | 105 | #define PCI_CLASS_DEVICE 0x0a /* Device class */ |
105 | #define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ | 106 | #define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ |
106 | #define PCI_LATENCY_TIMER 0x0d /* 8 bits */ | 107 | #define PCI_LATENCY_TIMER 0x0d /* 8 bits */ |
@@ -159,6 +160,10 @@ struct PCIDevice { | @@ -159,6 +160,10 @@ struct PCIDevice { | ||
159 | /* PCI config space */ | 160 | /* PCI config space */ |
160 | uint8_t config[PCI_CONFIG_SPACE_SIZE]; | 161 | uint8_t config[PCI_CONFIG_SPACE_SIZE]; |
161 | 162 | ||
163 | + /* Used to enable config checks on load. Note that writeable bits are | ||
164 | + * never checked even if set in cmask. */ | ||
165 | + uint8_t cmask[PCI_CONFIG_SPACE_SIZE]; | ||
166 | + | ||
162 | /* Used to implement R/W bytes */ | 167 | /* Used to implement R/W bytes */ |
163 | uint8_t wmask[PCI_CONFIG_SPACE_SIZE]; | 168 | uint8_t wmask[PCI_CONFIG_SPACE_SIZE]; |
164 | 169 |