Skip to content

Commit 32347d9

Browse files
godlygeekpablogsal
authored andcommitted
Handle failures to check if a file is ELF
While attempting to find the executable for a core file, it's possible that we extract the empty string from the core file where an executable name ought to have been. In that case we construct a `pathlib.Path` with the empty string, which happens to behave like a path of `.` - which is a directory, not a file. This leads to failures in surprising places. Since the core file may have been generated on a remote machine, or at a distant point in time, we shouldn't assume that any file name found in necessarily refers to a file that we can read just because it exists on disk. It may have been replaced by a directory, or it may have its permissions set up so that it's not readable by the user we're running as. Rather than trusting that anything that we can `stat` must be a file we can read, have our `is_elf` function treat any path from which we fail to read as though it's not an ELF executable. This lets us continue processing core files even if files they refer to can't be read by us, letting us try fallbacks or otherwise attempt to make the best of things. Signed-off-by: Matt Wozniski <[email protected]>
1 parent 5b75822 commit 32347d9

File tree

4 files changed

+9
-5
lines changed

4 files changed

+9
-5
lines changed

news/221.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve handling of core files where we cannot determine the executable.

src/pystack/__main__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def process_core(parser: argparse.ArgumentParser, args: argparse.Namespace) -> N
331331
if args.executable is None:
332332
corefile_analyzer = CoreFileAnalyzer(corefile)
333333
executable = pathlib.Path(corefile_analyzer.extract_executable())
334-
if not executable.exists() or not is_elf(executable):
334+
if not is_elf(executable):
335335
first_map = next(
336336
(
337337
map
@@ -343,7 +343,6 @@ def process_core(parser: argparse.ArgumentParser, args: argparse.Namespace) -> N
343343
if (
344344
first_map is not None
345345
and first_map.path is not None
346-
and first_map.path.exists()
347346
and is_elf(first_map.path)
348347
):
349348
executable = first_map.path

src/pystack/process.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,12 @@ def get_python_version_for_core(
124124

125125
def is_elf(filename: pathlib.Path) -> bool:
126126
"Return True if the given file is an ELF file"
127-
elf_header = b"\x7fELF"
128-
with open(filename, "br") as thefile:
129-
return thefile.read(4) == elf_header
127+
try:
128+
elf_header = b"\x7fELF"
129+
with open(filename, "br") as thefile:
130+
return thefile.read(4) == elf_header
131+
except OSError:
132+
return False
130133

131134

132135
def get_thread_name(pid: int, tid: int) -> Optional[str]:

tests/integration/test_process.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ def test_reattaching_to_already_traced_process(python, tmpdir):
132132
[
133133
(sys.executable, True),
134134
(__file__, False),
135+
("/etc", False),
135136
],
136137
)
137138
def test_elf_checker(file, expected):

0 commit comments

Comments
 (0)