]> git.lizzy.rs Git - rust.git/blob - src/etc/gdb_providers.py
Rollup merge of #76858 - rcvalle:rust-lang-exploit-mitigations, r=steveklabnik
[rust.git] / src / etc / gdb_providers.py
1 from sys import version_info
2
3 import gdb
4 from gdb import lookup_type
5
6 if version_info[0] >= 3:
7     xrange = range
8
9 ZERO_FIELD = "__0"
10 FIRST_FIELD = "__1"
11
12
13 def unwrap_unique_or_non_null(unique_or_nonnull):
14     # BACKCOMPAT: rust 1.32
15     # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067
16     ptr = unique_or_nonnull["pointer"]
17     return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ZERO_FIELD]
18
19
20 class EnumProvider:
21     def __init__(self, valobj):
22         content = valobj[valobj.type.fields()[0]]
23         fields = content.type.fields()
24         self.empty = len(fields) == 0
25         if not self.empty:
26             if len(fields) == 1:
27                 discriminant = 0
28             else:
29                 discriminant = int(content[fields[0]]) + 1
30             self.active_variant = content[fields[discriminant]]
31             self.name = fields[discriminant].name
32             self.full_name = "{}::{}".format(valobj.type.name, self.name)
33         else:
34             self.full_name = valobj.type.name
35
36     def to_string(self):
37         return self.full_name
38
39     def children(self):
40         if not self.empty:
41             yield self.name, self.active_variant
42
43
44 class StdStringProvider:
45     def __init__(self, valobj):
46         self.valobj = valobj
47         vec = valobj["vec"]
48         self.length = int(vec["len"])
49         self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"])
50
51     def to_string(self):
52         return self.data_ptr.lazy_string(encoding="utf-8", length=self.length)
53
54     @staticmethod
55     def display_hint():
56         return "string"
57
58
59 class StdOsStringProvider:
60     def __init__(self, valobj):
61         self.valobj = valobj
62         buf = self.valobj["inner"]["inner"]
63         is_windows = "Wtf8Buf" in buf.type.name
64         vec = buf[ZERO_FIELD] if is_windows else buf
65
66         self.length = int(vec["len"])
67         self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"])
68
69     def to_string(self):
70         return self.data_ptr.lazy_string(encoding="utf-8", length=self.length)
71
72     def display_hint(self):
73         return "string"
74
75
76 class StdStrProvider:
77     def __init__(self, valobj):
78         self.valobj = valobj
79         self.length = int(valobj["length"])
80         self.data_ptr = valobj["data_ptr"]
81
82     def to_string(self):
83         return self.data_ptr.lazy_string(encoding="utf-8", length=self.length)
84
85     @staticmethod
86     def display_hint():
87         return "string"
88
89
90 class StdVecProvider:
91     def __init__(self, valobj):
92         self.valobj = valobj
93         self.length = int(valobj["len"])
94         self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
95
96     def to_string(self):
97         return "Vec(size={})".format(self.length)
98
99     def children(self):
100         saw_inaccessible = False
101         for index in xrange(self.length):
102             element_ptr = self.data_ptr + index
103             if saw_inaccessible:
104                 return
105             try:
106                 # rust-lang/rust#64343: passing deref expr to `str` allows
107                 # catching exception on garbage pointer
108                 str(element_ptr.dereference())
109                 yield "[{}]".format(index), element_ptr.dereference()
110             except RuntimeError:
111                 saw_inaccessible = True
112                 yield str(index), "inaccessible"
113
114     @staticmethod
115     def display_hint():
116         return "array"
117
118
119 class StdVecDequeProvider:
120     def __init__(self, valobj):
121         self.valobj = valobj
122         self.head = int(valobj["head"])
123         self.tail = int(valobj["tail"])
124         self.cap = int(valobj["buf"]["cap"])
125         self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"])
126         if self.head >= self.tail:
127             self.size = self.head - self.tail
128         else:
129             self.size = self.cap + self.head - self.tail
130
131     def to_string(self):
132         return "VecDeque(size={})".format(self.size)
133
134     def children(self):
135         for index in xrange(0, self.size):
136             value = (self.data_ptr + ((self.tail + index) % self.cap)).dereference()
137             yield "[{}]".format(index), value
138
139     @staticmethod
140     def display_hint():
141         return "array"
142
143
144 class StdRcProvider:
145     def __init__(self, valobj, is_atomic=False):
146         self.valobj = valobj
147         self.is_atomic = is_atomic
148         self.ptr = unwrap_unique_or_non_null(valobj["ptr"])
149         self.value = self.ptr["data" if is_atomic else "value"]
150         self.strong = self.ptr["strong"]["v" if is_atomic else "value"]["value"]
151         self.weak = self.ptr["weak"]["v" if is_atomic else "value"]["value"] - 1
152
153     def to_string(self):
154         if self.is_atomic:
155             return "Arc(strong={}, weak={})".format(int(self.strong), int(self.weak))
156         else:
157             return "Rc(strong={}, weak={})".format(int(self.strong), int(self.weak))
158
159     def children(self):
160         yield "value", self.value
161         yield "strong", self.strong
162         yield "weak", self.weak
163
164
165 class StdCellProvider:
166     def __init__(self, valobj):
167         self.value = valobj["value"]["value"]
168
169     def to_string(self):
170         return "Cell"
171
172     def children(self):
173         yield "value", self.value
174
175
176 class StdRefProvider:
177     def __init__(self, valobj):
178         self.value = valobj["value"].dereference()
179         self.borrow = valobj["borrow"]["borrow"]["value"]["value"]
180
181     def to_string(self):
182         borrow = int(self.borrow)
183         if borrow >= 0:
184             return "Ref(borrow={})".format(borrow)
185         else:
186             return "Ref(borrow_mut={})".format(-borrow)
187
188     def children(self):
189         yield "*value", self.value
190         yield "borrow", self.borrow
191
192
193 class StdRefCellProvider:
194     def __init__(self, valobj):
195         self.value = valobj["value"]["value"]
196         self.borrow = valobj["borrow"]["value"]["value"]
197
198     def to_string(self):
199         borrow = int(self.borrow)
200         if borrow >= 0:
201             return "RefCell(borrow={})".format(borrow)
202         else:
203             return "RefCell(borrow_mut={})".format(-borrow)
204
205     def children(self):
206         yield "value", self.value
207         yield "borrow", self.borrow
208
209
210 # Yields children (in a provider's sense of the word) for a BTreeMap.
211 def children_of_btree_map(map):
212     # Yields each key/value pair in the node and in any child nodes.
213     def children_of_node(node_ptr, height):
214         def cast_to_internal(node):
215             internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1)
216             internal_type = lookup_type(internal_type_name)
217             return node.cast(internal_type.pointer())
218
219         leaf = node_ptr.dereference()
220         keys = leaf["keys"]
221         vals = leaf["vals"]
222         edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None
223         length = leaf["len"]
224
225         for i in xrange(0, length + 1):
226             if height > 0:
227                 boxed_child_node = edges[i]["value"]["value"]
228                 child_node = unwrap_unique_or_non_null(boxed_child_node["ptr"])
229                 for child in children_of_node(child_node, height - 1):
230                     yield child
231             if i < length:
232                 # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays.
233                 key = keys[i]["value"]["value"] if keys.type.sizeof > 0 else "()"
234                 val = vals[i]["value"]["value"] if vals.type.sizeof > 0 else "()"
235                 yield key, val
236
237     if map["length"] > 0:
238         root = map["root"]
239         if root.type.name.startswith("core::option::Option<"):
240             root = root.cast(gdb.lookup_type(root.type.name[21:-1]))
241         node_ptr = root["node"]
242         if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"):
243             node_ptr = node_ptr["ptr"]
244         node_ptr = unwrap_unique_or_non_null(node_ptr)
245         height = root["height"]
246         for child in children_of_node(node_ptr, height):
247             yield child
248
249
250 class StdBTreeSetProvider:
251     def __init__(self, valobj):
252         self.valobj = valobj
253
254     def to_string(self):
255         return "BTreeSet(size={})".format(self.valobj["map"]["length"])
256
257     def children(self):
258         inner_map = self.valobj["map"]
259         for i, (child, _) in enumerate(children_of_btree_map(inner_map)):
260             yield "[{}]".format(i), child
261
262     @staticmethod
263     def display_hint():
264         return "array"
265
266
267 class StdBTreeMapProvider:
268     def __init__(self, valobj):
269         self.valobj = valobj
270
271     def to_string(self):
272         return "BTreeMap(size={})".format(self.valobj["length"])
273
274     def children(self):
275         for i, (key, val) in enumerate(children_of_btree_map(self.valobj)):
276             yield "key{}".format(i), key
277             yield "val{}".format(i), val
278
279     @staticmethod
280     def display_hint():
281         return "map"
282
283
284 # BACKCOMPAT: rust 1.35
285 class StdOldHashMapProvider:
286     def __init__(self, valobj, show_values=True):
287         self.valobj = valobj
288         self.show_values = show_values
289
290         self.table = self.valobj["table"]
291         self.size = int(self.table["size"])
292         self.hashes = self.table["hashes"]
293         self.hash_uint_type = self.hashes.type
294         self.hash_uint_size = self.hashes.type.sizeof
295         self.modulo = 2 ** self.hash_uint_size
296         self.data_ptr = self.hashes[ZERO_FIELD]["pointer"]
297
298         self.capacity_mask = int(self.table["capacity_mask"])
299         self.capacity = (self.capacity_mask + 1) % self.modulo
300
301         marker = self.table["marker"].type
302         self.pair_type = marker.template_argument(0)
303         self.pair_type_size = self.pair_type.sizeof
304
305         self.valid_indices = []
306         for idx in range(self.capacity):
307             data_ptr = self.data_ptr.cast(self.hash_uint_type.pointer())
308             address = data_ptr + idx
309             hash_uint = address.dereference()
310             hash_ptr = hash_uint[ZERO_FIELD]["pointer"]
311             if int(hash_ptr) != 0:
312                 self.valid_indices.append(idx)
313
314     def to_string(self):
315         if self.show_values:
316             return "HashMap(size={})".format(self.size)
317         else:
318             return "HashSet(size={})".format(self.size)
319
320     def children(self):
321         start = int(self.data_ptr) & ~1
322
323         hashes = self.hash_uint_size * self.capacity
324         align = self.pair_type_size
325         len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~(
326                 (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo
327
328         pairs_offset = hashes + len_rounded_up
329         pairs_start = gdb.Value(start + pairs_offset).cast(self.pair_type.pointer())
330
331         for index in range(self.size):
332             table_index = self.valid_indices[index]
333             idx = table_index & self.capacity_mask
334             element = (pairs_start + idx).dereference()
335             if self.show_values:
336                 yield "key{}".format(index), element[ZERO_FIELD]
337                 yield "val{}".format(index), element[FIRST_FIELD]
338             else:
339                 yield "[{}]".format(index), element[ZERO_FIELD]
340
341     def display_hint(self):
342         return "map" if self.show_values else "array"
343
344
345 class StdHashMapProvider:
346     def __init__(self, valobj, show_values=True):
347         self.valobj = valobj
348         self.show_values = show_values
349
350         table = self.table()
351         capacity = int(table["bucket_mask"]) + 1
352         ctrl = table["ctrl"]["pointer"]
353
354         self.size = int(table["items"])
355         self.pair_type = table.type.template_argument(0)
356
357         self.new_layout = not table.type.has_key("data")
358         if self.new_layout:
359             self.data_ptr = ctrl.cast(self.pair_type.pointer())
360         else:
361             self.data_ptr = table["data"]["pointer"]
362
363         self.valid_indices = []
364         for idx in range(capacity):
365             address = ctrl + idx
366             value = address.dereference()
367             is_presented = value & 128 == 0
368             if is_presented:
369                 self.valid_indices.append(idx)
370
371     def table(self):
372         if self.show_values:
373             hashbrown_hashmap = self.valobj["base"]
374         elif self.valobj.type.fields()[0].name == "map":
375             # BACKCOMPAT: rust 1.47
376             # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap
377             hashbrown_hashmap = self.valobj["map"]["base"]
378         else:
379             # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap
380             hashbrown_hashmap = self.valobj["base"]["map"]
381         return hashbrown_hashmap["table"]
382
383     def to_string(self):
384         if self.show_values:
385             return "HashMap(size={})".format(self.size)
386         else:
387             return "HashSet(size={})".format(self.size)
388
389     def children(self):
390         pairs_start = self.data_ptr
391
392         for index in range(self.size):
393             idx = self.valid_indices[index]
394             if self.new_layout:
395                 idx = -(idx + 1)
396             element = (pairs_start + idx).dereference()
397             if self.show_values:
398                 yield "key{}".format(index), element[ZERO_FIELD]
399                 yield "val{}".format(index), element[FIRST_FIELD]
400             else:
401                 yield "[{}]".format(index), element[ZERO_FIELD]
402
403     def display_hint(self):
404         return "map" if self.show_values else "array"