Compare commits

...
1 Commits
Author SHA1 Message Date
Rahulandrahul05ranjan 459dfd7e35 fix(regsort): drop self-assignments through register aliases (#2795)
* fix(regsort): drop self-assignments through register aliases

The self-assignment drop compared register names only (k != v), so an
assignment through an alias (e.g. {'ax': 'eax'} with registers.native32)
was not recognized as a no-op.  It then surfaced as a spurious
1-register dependency cycle, which either raised PwnlibException with
xchg=False or emitted a useless 'mov' of a register into its own alias.

With xchg=False and other registers present, the bogus cycle also
forced the temporary-register code path, reordering unrelated moves and
silently leaving registers with wrong values (see the linked issue).

Compare the mapped names instead, like the rest of the function.

Fixes #2794

Co-authored-by: rahul05ranjan <[email protected]>

* docs(changelog): add regsort register-alias self-assignment fix entry

---------

Co-authored-by: Rahul Ranjan <[email protected]>
2026-10-30 16:20:25 +01:00
2 changed files with 14 additions and 1 deletions

No files matched your search

+2
View File
@@ -137,6 +137,7 @@ The table below shows which release corresponds to each branch, and what date th
- [#2773][2773] Disable debuginfod doctests in libcdb.unstrip_libc
- [#2772][2772] fix(packing): `dd()` no longer crashes on a `bytearray` destination
- [#2787][2787] gdb.corefile: Run gdb directly to prevent detach race
- [#2795][2795] regsort: Fix self-assignment detection through register aliases
[2694]: https://github.com/Gallopsled/pwntools/pull/2694
[2701]: https://github.com/Gallopsled/pwntools/pull/2701
@@ -144,6 +145,7 @@ The table below shows which release corresponds to each branch, and what date th
[2773]: https://github.com/Gallopsled/pwntools/pull/2773
[2772]: https://github.com/Gallopsled/pwntools/pull/2772
[2787]: https://github.com/Gallopsled/pwntools/pull/2787
[2795]: https://github.com/Gallopsled/pwntools/pull/2795
## 4.15.0 (`stable`)
+12 -1
View File
@@ -190,6 +190,15 @@ def regsort(in_out, all_regs, mapping = None, tmp = None, xchg = True, randomize
>>> regsort({'a': 1, 'b': 2}, R)
[('mov', 'a', 1), ('mov', 'b', 2)]
>>> M = {'ax': 'eax', 'eax': 'eax', 'ebx': 'ebx'}
Registers which are assigned one of their own aliases
are dropped, like same-name self-assignments.
>>> regsort({'ax': 'eax'}, M)
[]
>>> regsort({'ax': 'eax'}, M, xchg=False)
[]
>>> regsort({'a': 'b', 'b': 'a'}, R)
[('xchg', 'a', 'b')]
>>> regsort({'a': 'b', 'b': 'a'}, R, tmp='X') #doctest: +NORMALIZE_WHITESPACE
@@ -253,7 +262,9 @@ def regsort(in_out, all_regs, mapping = None, tmp = None, xchg = True, randomize
# Drop all registers which will be set to themselves.
#
# For example, {'eax': 'eax'}
in_out = {k:v for k,v in in_out.items() if k != v}
# This must go through the mapping, so that {'ax': 'eax'} is
# recognized as a self-assignment as well.
in_out = {k:v for k,v in in_out.items() if mapping.get(k, k) != mapping.get(v, v)}
# Collapse constant values
#