Skip to content

Commit f9e60f8

Browse files
committed
map: Always hash the object in mp_map_lookup.
Fuzzing produced a crash in OrderedDict which reduced to the following: ``` from collections import OrderedDict x = OrderedDict() x[: 'a'] = 1 x['b'] = 2 ``` This occurs because, as an optimization, the key is not hashed when searching an OrderedDict, so an unhashable key can be stored. Even worse, the slice in this case is from build_slice_stack_allocated so by the time the 2nd indexing operation is being performed, it's nonsense instead of a Python object. Signed-off-by: Jeff Epler <jepler@gmail.com>
1 parent 17fbc5a commit f9e60f8

File tree

2 files changed

+22
-8
lines changed

2 files changed

+22
-8
lines changed

py/map.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ mp_map_elem_t *MICROPY_WRAP_MP_MAP_LOOKUP(mp_map_lookup)(mp_map_t * map, mp_obj_
188188
}
189189
}
190190

191+
// get hash of index, with fast path for common case of qstr
192+
// The object is hashed regardless of whether the map is ordered or not,
193+
// because performing the hash is also performing the "is object hashable" check.
194+
mp_uint_t hash;
195+
if (mp_obj_is_qstr(index)) {
196+
hash = qstr_hash(MP_OBJ_QSTR_VALUE(index));
197+
} else {
198+
hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
199+
}
200+
191201
// if the map is an ordered array then we must do a brute force linear search
192202
if (map->is_ordered) {
193203
for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
@@ -241,14 +251,6 @@ mp_map_elem_t *MICROPY_WRAP_MP_MAP_LOOKUP(mp_map_lookup)(mp_map_t * map, mp_obj_
241251
}
242252
}
243253

244-
// get hash of index, with fast path for common case of qstr
245-
mp_uint_t hash;
246-
if (mp_obj_is_qstr(index)) {
247-
hash = qstr_hash(MP_OBJ_QSTR_VALUE(index));
248-
} else {
249-
hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
250-
}
251-
252254
size_t pos = hash % map->alloc;
253255
size_t start_pos = pos;
254256
mp_map_elem_t *avail_slot = NULL;

tests/basics/ordereddict1.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,15 @@
4141
d.popitem()
4242
except:
4343
print('empty')
44+
45+
# ordered dict must reject nonhashable items, including slices
46+
try:
47+
d[:'a'] = 123
48+
except:
49+
print("exception")
50+
51+
try:
52+
d[[]] = 123
53+
except:
54+
print("exception")
55+
print(len(d))

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy