]>
Commit | Line | Data |
---|---|---|
a39364b9 FE |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Alexander Bulekov <alxndr@bu.edu> | |
3 | Date: Thu, 27 Apr 2023 17:10:06 -0400 | |
4 | Subject: [PATCH] memory: prevent dma-reentracy issues | |
5 | ||
6 | Add a flag to the DeviceState, when a device is engaged in PIO/MMIO/DMA. | |
7 | This flag is set/checked prior to calling a device's MemoryRegion | |
8 | handlers, and set when device code initiates DMA. The purpose of this | |
9 | flag is to prevent two types of DMA-based reentrancy issues: | |
10 | ||
11 | 1.) mmio -> dma -> mmio case | |
12 | 2.) bh -> dma write -> mmio case | |
13 | ||
14 | These issues have led to problems such as stack-exhaustion and | |
15 | use-after-frees. | |
16 | ||
17 | Summary of the problem from Peter Maydell: | |
18 | https://lore.kernel.org/qemu-devel/CAFEAcA_23vc7hE3iaM-JVA6W38LK4hJoWae5KcknhPRD5fPBZA@mail.gmail.com | |
19 | ||
20 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/62 | |
21 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/540 | |
22 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/541 | |
23 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/556 | |
24 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/557 | |
25 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/827 | |
26 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1282 | |
27 | Resolves: CVE-2023-0330 | |
28 | ||
29 | Signed-off-by: Alexander Bulekov <alxndr@bu.edu> | |
30 | Reviewed-by: Thomas Huth <thuth@redhat.com> | |
31 | Message-Id: <20230427211013.2994127-2-alxndr@bu.edu> | |
32 | [thuth: Replace warn_report() with warn_report_once()] | |
33 | Signed-off-by: Thomas Huth <thuth@redhat.com> | |
34 | (cherry-picked from commit a2e1753b8054344f32cf94f31c6399a58794a380) | |
35 | Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> | |
36 | --- | |
37 | include/exec/memory.h | 5 +++++ | |
38 | include/hw/qdev-core.h | 7 +++++++ | |
39 | softmmu/memory.c | 16 ++++++++++++++++ | |
40 | 3 files changed, 28 insertions(+) | |
41 | ||
42 | diff --git a/include/exec/memory.h b/include/exec/memory.h | |
43 | index 15ade918ba..e45ce6061f 100644 | |
44 | --- a/include/exec/memory.h | |
45 | +++ b/include/exec/memory.h | |
46 | @@ -767,6 +767,8 @@ struct MemoryRegion { | |
47 | bool is_iommu; | |
48 | RAMBlock *ram_block; | |
49 | Object *owner; | |
50 | + /* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */ | |
51 | + DeviceState *dev; | |
52 | ||
53 | const MemoryRegionOps *ops; | |
54 | void *opaque; | |
55 | @@ -791,6 +793,9 @@ struct MemoryRegion { | |
56 | unsigned ioeventfd_nb; | |
57 | MemoryRegionIoeventfd *ioeventfds; | |
58 | RamDiscardManager *rdm; /* Only for RAM */ | |
59 | + | |
60 | + /* For devices designed to perform re-entrant IO into their own IO MRs */ | |
61 | + bool disable_reentrancy_guard; | |
62 | }; | |
63 | ||
64 | struct IOMMUMemoryRegion { | |
65 | diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h | |
66 | index bd50ad5ee1..7623703943 100644 | |
67 | --- a/include/hw/qdev-core.h | |
68 | +++ b/include/hw/qdev-core.h | |
69 | @@ -162,6 +162,10 @@ struct NamedClockList { | |
70 | QLIST_ENTRY(NamedClockList) node; | |
71 | }; | |
72 | ||
73 | +typedef struct { | |
74 | + bool engaged_in_io; | |
75 | +} MemReentrancyGuard; | |
76 | + | |
77 | /** | |
78 | * DeviceState: | |
79 | * @realized: Indicates whether the device has been fully constructed. | |
80 | @@ -194,6 +198,9 @@ struct DeviceState { | |
81 | int alias_required_for_version; | |
82 | ResettableState reset; | |
83 | GSList *unplug_blockers; | |
84 | + | |
85 | + /* Is the device currently in mmio/pio/dma? Used to prevent re-entrancy */ | |
86 | + MemReentrancyGuard mem_reentrancy_guard; | |
87 | }; | |
88 | ||
89 | struct DeviceListener { | |
90 | diff --git a/softmmu/memory.c b/softmmu/memory.c | |
91 | index b1a6cae6f5..b7b3386e9d 100644 | |
92 | --- a/softmmu/memory.c | |
93 | +++ b/softmmu/memory.c | |
94 | @@ -542,6 +542,18 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, | |
95 | access_size_max = 4; | |
96 | } | |
97 | ||
98 | + /* Do not allow more than one simultaneous access to a device's IO Regions */ | |
99 | + if (mr->dev && !mr->disable_reentrancy_guard && | |
100 | + !mr->ram_device && !mr->ram && !mr->rom_device && !mr->readonly) { | |
101 | + if (mr->dev->mem_reentrancy_guard.engaged_in_io) { | |
102 | + warn_report_once("Blocked re-entrant IO on MemoryRegion: " | |
103 | + "%s at addr: 0x%" HWADDR_PRIX, | |
104 | + memory_region_name(mr), addr); | |
105 | + return MEMTX_ACCESS_ERROR; | |
106 | + } | |
107 | + mr->dev->mem_reentrancy_guard.engaged_in_io = true; | |
108 | + } | |
109 | + | |
110 | /* FIXME: support unaligned access? */ | |
111 | access_size = MAX(MIN(size, access_size_max), access_size_min); | |
112 | access_mask = MAKE_64BIT_MASK(0, access_size * 8); | |
113 | @@ -556,6 +568,9 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, | |
114 | access_mask, attrs); | |
115 | } | |
116 | } | |
117 | + if (mr->dev) { | |
118 | + mr->dev->mem_reentrancy_guard.engaged_in_io = false; | |
119 | + } | |
120 | return r; | |
121 | } | |
122 | ||
123 | @@ -1170,6 +1185,7 @@ static void memory_region_do_init(MemoryRegion *mr, | |
124 | } | |
125 | mr->name = g_strdup(name); | |
126 | mr->owner = owner; | |
127 | + mr->dev = (DeviceState *) object_dynamic_cast(mr->owner, TYPE_DEVICE); | |
128 | mr->ram_block = NULL; | |
129 | ||
130 | if (name) { |