Repository navigation
Conversation
ClemoryView, ClemoryTranslator and ClemoryReadOnlyView define no __iter__, and neither does ClemoryBase, which declares __getitem__, __setitem__, __contains__, load, store, backers and find and raises NotImplementedError in each. Python therefore falls back to the legacy __getitem__ sequence protocol, which calls obj[0], obj[1], ... and stops only on IndexError. These classes raise KeyError, so iterating a view whose address space starts at 0 yields the byte stored at each address, and iterating one that starts anywhere else raises KeyError: 0. On binaries/tests/x86_64/fauxware the first six values a ClemoryView over loader.memory yields are [127, 69, 76, 70, 2, 1], which is the start of the ELF header rather than any address in it. Declare __iter__ on ClemoryBase beside the other seven, so a class that does not implement it refuses instead of falling through. Clemory keeps its own implementation. The alternative is to implement address iteration on the views, and it is a larger change than it looks. ClemoryReadOnlyView.backers() is a point query that yields nothing when asked to enumerate a whole clemory, ClemoryView.backers() mis-clamps a backer that runs past the end of the view, and ClemoryTranslator refuses backers() and find() outright because it has no address space to walk. That belongs in its own change.
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS What each of the three views yields when iterated, before and after this change. Before — two of the three walk off into the image's bytes; the third dies on angr/cle master 0e77adeAfter — all three refuse, and say so: with this change |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
Two mutations of the fix each fail the new test: returning an empty iterator from No caller iterates any of the three views. Neither Caveats: the workspace's complete gate could not be run for this branch, because |
|
Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_821 |
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
Iterating any of cle's three
Clemoryviews yields the byte stored at eachaddress instead of the address. On
binaries/tests/x86_64/fauxware:Those six values are the first six bytes of the image --
\x7fELFand the classand data fields after it -- not addresses.
ClemoryReadOnlyView, which is whatLoader.memory_ro_viewhands out, raisesKeyError: 0instead, and so does aClemoryViewwhose address space does not start at zero.Root cause
None of the three defines
__iter__, and neither doesClemoryBase. Pythontherefore falls back to the legacy sequence protocol, which calls
obj[0],obj[1], ... and stops only onIndexError. These classes raiseKeyError,so the loop either runs off the end of the address space or dies on its first
step.
ClemoryBasedeclares seven methods and raisesNotImplementedErrorin each:__getitem__,__setitem__,__contains__,load,store,backersandfind. That list has been one short since b75fb69 created the class in 2020 --__iter__went toClemoryalone and was never declared on the base, so theprotocol fallback has stood in for it ever since.
Fix
Declare
__iter__onClemoryBasebeside the other seven, so a class that doesnot implement it refuses instead of falling through.
Clemorykeeps its ownimplementation and is unaffected.
The other shape is to implement address iteration on the views, and I did not,
for a measured reason rather than a preference. The three observations below are
taken at master
0e77ade3.ClemoryReadOnlyView.backers()is a point query:asked to enumerate a whole clemory it returns nothing, while
_flattened_backersholds three entries for this binary.ClemoryView.backers()mis-clamps -- for the view above, 256 addresses wide, ityields one backer of 2420 bytes starting at 0. And
ClemoryTranslatorrefusesbackers()andfind()outright, because address translation gives it no spaceto walk. Say the word if you would rather have iteration. #818 fixes the same
class of defect on
Clemoryitself; the two do not overlap and neither dependson the other.
Testing
tests/test_clemory_views.pyloadsbinaries/tests/x86_64/fauxware, builds allthree views over
loader.memoryand asserts that iterating each one raisesNotImplementedError, then checks thatClemorystill yields one value perbacked byte, so the base declaration cannot shadow the subclass. It fails on
master, where the first view iterates instead.
Validation: #821 (comment)
session: sharpen