Skip to content

Commit e4e97f5

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. The new test is in its own file because Python 3.12 actually made slice objects hashable so a .exp file is required. Signed-off-by: Jeff Epler <jepler@gmail.com>
1 parent 17fbc5a commit e4e97f5

File tree

3 files changed

+34
-8
lines changed

3 files changed

+34
-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;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
try:
2+
from collections import OrderedDict
3+
except ImportError:
4+
print("SKIP")
5+
raise SystemExit
6+
7+
d = OrderedDict()
8+
9+
# ordered dict must reject nonhashable items, including slices
10+
# This test operates with a .exp file because Python 3.12 made slice objects
11+
# hashable.
12+
try:
13+
d[:'a'] = 123
14+
except:
15+
print("exception")
16+
17+
try:
18+
d[[]] = 123
19+
except:
20+
print("exception")
21+
print(len(d))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
exception
2+
exception
3+
0

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