Content-Length: 325951 | pFad | http://github.com/micropython/micropython/pull/17723

48 map: Always hash the object in mp_map_lookup. by jepler · Pull Request #17723 · micropython/micropython · GitHub
Skip to content

map: Always hash the object in mp_map_lookup. #17723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jepler
Copy link
Contributor

@jepler jepler commented Jul 20, 2025

Summary

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.

Testing

I added a test for this and ran the testsuite locally.

Trade-offs and Alternatives

This might slightly lower the performance of OrderedDict objects because before this change they avoided hashing the keys at all. However, I'm not aware of a way to check whether an object is hashable short of actually hashing it.

@jepler jepler force-pushed the ordereddict-slice-crash branch from b953be5 to f9e60f8 Compare July 20, 2025 10:53
Copy link

codecov bot commented Jul 20, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.44%. Comparing base (17fbc5a) to head (f9e60f8).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #17723   +/-   ##
=======================================
  Coverage   98.44%   98.44%           
=======================================
  Files         171      171           
  Lines       22208    22208           
=======================================
  Hits        21863    21863           
  Misses        345      345           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:   -19 -0.010% 
   unix x64:   -32 -0.004% standard
      stm32:    -8 -0.002% PYBV10
     mimxrt:   -16 -0.004% TEENSY40
        rp2:    -8 -0.001% RPI_PICO_W
       samd:    -8 -0.003% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:   -18 -0.004% VIRT_RV32

@jepler
Copy link
Contributor Author

jepler commented Jul 20, 2025

The test fails on some platforms because Python 3.12 made slice objects hashable.

>>> d = collections.OrderedDict()
>>> d[:] = "oops"
>>> d
OrderedDict({slice(None, None, None): 'oops'})

@jepler jepler force-pushed the ordereddict-slice-crash branch from f9e60f8 to e4e97f5 Compare July 20, 2025 11:15
@jepler
Copy link
Contributor Author

jepler commented Jul 20, 2025

I pulled the new test into a separate file with a .exp included so that it can run regardless of Python version.

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>
@jepler jepler force-pushed the ordereddict-slice-crash branch from e4e97f5 to cf1322f Compare July 20, 2025 11:18
@dpgeorge
Copy link
Member

This might slightly lower the performance of OrderedDict objects because before this change they avoided hashing the keys at all. However, I'm not aware of a way to check whether an object is hashable short of actually hashing it.

This change will most likely impact performance quite a lot. That's because all built-in dicts (method tables and module dicts) have is_ordered = 1, so are essentially an OrderedDict. See py/obj.h:MP_DEFINE_CONST_DICT.

So we should at least measure the impact of this change on performance. And then probably need to find another way to fix this issue.

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Jul 20, 2025
@jepler
Copy link
Contributor Author

jepler commented Jul 20, 2025

That makes sense. Maybe the alternative is to add a specific check for slice objects. It's the (new?) temporary nature of the slice object which turns it from a "well don't do that then" bug into a crashing bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
py-core Relates to py/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/micropython/micropython/pull/17723

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy