Skip to content

Fix FileNotFoundError when CWD does not exist at argument parse time - #44

Open
blshkv wants to merge 2 commits into
bannsec:masterfrom
blshkv:fix-getcwd-default
Open

blshkv wants to merge 2 commits into
bannsec:masterfrom
blshkv:fix-getcwd-default

Conversation

@blshkv

@blshkv blshkv commented Jul 23, 2026

Copy link
Copy Markdown

Problems

Two separate FileNotFoundError crash sites were found while packaging stegoveritas for Pentoo Linux:
pentoo/pentoo-overlay#2923

1. add_argument default evaluated eagerly (line 179)

parser.add_argument('-out', ..., default=os.path.join(os.getcwd(), 'results'))

Python evaluates default= when add_argument() is called, not when -out is absent. If the process CWD has been deleted at that point, os.getcwd() raises FileNotFoundError before any test body runs.

Fix: use default=None and resolve lazily at the results_directory assignment site.

2. os.getcwd() / os.chdir() in binwalk scan path (line 98)

saved_dir = os.getcwd()
os.chdir(tmpdirname)
# ... binwalk.scan() — uses multiprocessing internally ...
os.chdir(saved_dir)

Two issues here:

  • If a previous test left the process in a since-deleted temp directory, os.getcwd() fails immediately.
  • binwalk uses multiprocessing internally. On some platforms Python's multiprocessing.spawn serialises the parent CWD and tries to restore it in the child. If tmpdirname is removed between spawn and child startup, spawn.py raises FileNotFoundError.

Fix: save the current directory as an O_RDONLY fd with os.open('.') and restore with os.fchdir(). An open fd keeps the inode alive even if the path is later unlinked, and fchdir never has to resolve a path. Fall back to self.results_directory if the initial open fails (CWD already gone).


Note: a separate failure (AttributeError: module 'capstone' has no attribute 'CS_ARCH_ARM64') is caused by a capstone 5.x API change in binwalk/modules/disasm.py and is outside the scope of this PR.

blshkv added 2 commits July 23, 2026 08:16
os.getcwd() was called eagerly as the default= value of the -out
argument, which is evaluated when add_argument() is called, not when
the argument is actually absent from the command line.  If the process
CWD has been deleted (e.g. during pytest runs inside a package manager
sandbox), this raises FileNotFoundError before any test body runs.

Move the os.getcwd() call to the assignment site (results_directory),
where it is only reached after parse_args() succeeds and only when
-out was not supplied by the caller.
The binwalk scan helper does os.chdir(tmpdirname) to work around
binwalk's lack of an output-directory option.  It saves and restores
the CWD using os.getcwd() / os.chdir(saved_dir).

Two problems with this approach:

1. os.getcwd() raises FileNotFoundError if a previous test left the
   process in a since-deleted temp directory.

2. binwalk uses multiprocessing internally; the spawned child process
   inherits the parent's CWD (tmpdirname).  On some platforms/configs
   Python's multiprocessing.spawn serialises the CWD and tries to
   restore it in the child.  If tmpdirname is deleted between the
   spawn and the child starting, spawn.py raises FileNotFoundError.

Fix: open the current directory as an O_RDONLY fd before chdir'ing,
then restore with os.fchdir().  An open fd keeps the inode alive even
if the path is later removed, and fchdir never has to resolve a path.
If the initial open fails (CWD already gone), fall back to
self.results_directory which is always a valid path at this point.
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