Skip to content

Fix infinite loop in fs.md5sum / fs.sha1sum on Python 3#500

Open
potato-20 wants to merge 3 commits into
ReversecLabs:developfrom
potato-20:fix-fs-hashsum
Open

Fix infinite loop in fs.md5sum / fs.sha1sum on Python 3#500
potato-20 wants to merge 3 commits into
ReversecLabs:developfrom
potato-20:fix-fs-hashsum

Conversation

@potato-20
Copy link
Copy Markdown

The bug

reversec.common.fs.md5sum() and sha1sum() open the file in binary mode but drive their read loop with:

line = data = f.read()
while line != "":      # bytes compared to a str literal
    line = f.read()
    data += line

In Python 3, f.read() on a binary handle returns bytes, and b"" != "" is always True — so the loop never terminates. Any call on a readable file spins forever.

This is reachable in normal use: drozer/modules/common/superuser.py calls fs.md5sum() when verifying the bundled minimal-su binary, so that path hangs.

Reproduction (hangs before this fix):

from reversec.common import fs
fs.md5sum("/etc/hostname")   # never returns

The fix

Replace the hand-rolled loop with a single with open(...) read — the same pattern already used by fs.read() a few lines above:

def md5sum(path):
    try:
        with open(path, "rb") as f:
            return hashlib.md5(f.read()).hexdigest()
    except IOError:
        return None

sha1sum() gets the identical treatment, and its copy-pasted docstring (which said "md5sum") is corrected.

Tests

Adds regression tests in tests/test_fs.py asserting the digests match hashlib for normal and empty files, and that a missing file returns None. Full suite: 38 passed, no hang.

Note: this is stacked on #499 (which introduces the tests/ suite). Once #499 merges, this diff reduces to just the fs.py fix and its new tests.

potato-20 added 3 commits June 2, 2026 18:58
The project shipped without any automated tests. This adds a tests/ package
with 34 unit tests covering the host-side pure-Python helpers:

  - reversec.common.list  (chunk, flatten)
  - reversec.common.text  (indent, wrap)
  - reversec.common.fs    (read/write/touch round-trips)
  - drozer.util           (parse_server, StoreZeroOrTwo)
  - drozer.android.Intent (validity, defaults, flag combination)

pyproject.toml gains a [tool.pytest.ini_options] section (pythonpath = src)
so the suite runs with a bare 'pytest' invocation, plus a 'test' optional
dependency. No production code is changed.
Adds a GitHub Actions workflow that runs the unit test suite across
Python 3.9-3.13 on every push and pull request.
Both functions read the file in binary mode but terminated their read loop
on 'while line != ""' — comparing bytes to a str literal. In Python 3
b"" != "" is always True, so the loop never exited and the call hung
indefinitely (reachable via tools.setup.minimalsu, which calls fs.md5sum).

Replace the hand-rolled loop with a single 'with open(...)' read, matching
the existing fs.read() helper. Also corrects sha1sum's copy-pasted docstring.

Adds regression tests asserting the digests match hashlib for normal and
empty files, and that a missing file returns None.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant