Skip to content

Commit 2b04093

Browse files
authored
getpath: Fix /proc/self/maps parsing (#922)
Correctly handle lines missing a filename (which have a space between fields immediately followed by a newline; scanf will skip over both of those as whitespace and try to parse the next line). Also use __builtin_return_address to provide some robustness against running into issues with the PLT.
1 parent 5cbc49f commit 2b04093

File tree

2 files changed

+30
-14
lines changed

2 files changed

+30
-14
lines changed

cpython-unix/patch-python-getpath-3.14.patch

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
From 4fb328cb883504dde04dfdd0b4d182a0130a0909 Mon Sep 17 00:00:00 2001
1+
From 60d6a76dcee2a5647d69874d9b5c24f701a6722d Mon Sep 17 00:00:00 2001
22
From: Geoffrey Thomas <geofft@ldpreload.com>
33
Date: Mon, 1 Dec 2025 14:11:43 -0500
44
Subject: [PATCH 1/1] getpath: Fix library detection and canonicalize paths on
@@ -25,13 +25,19 @@ relative paths and they are not canonicalized, so instead, use
2525
no safe API on glibc to read it and no API at all on musl. Note that and
2626
glibc also uses procfs to do so; see discussion at
2727
https://sourceware.org/bugzilla/show_bug.cgi?id=25263)
28+
29+
Finally, switch the target address for lookups to the current function's
30+
return address. This avoids issues on some build configurations and
31+
platforms where the addresses Python library functions are behind a
32+
layer of indirection like the PLT. (See also the BUGS section of Linux
33+
man-pages' dladdr(3).)
2834
---
29-
Modules/getpath.c | 52 ++++++++++++++++++++++++++++++++++++++++------
30-
Modules/getpath.py | 4 ++--
31-
2 files changed, 48 insertions(+), 8 deletions(-)
35+
Modules/getpath.c | 62 +++++++++++++++++++++++++++++++++++++++++-----
36+
Modules/getpath.py | 4 +--
37+
2 files changed, 58 insertions(+), 8 deletions(-)
3238

3339
diff --git a/Modules/getpath.c b/Modules/getpath.c
34-
index 1e75993480a..72860807133 100644
40+
index 1e75993480a..347c21e7387 100644
3541
--- a/Modules/getpath.c
3642
+++ b/Modules/getpath.c
3743
@@ -802,14 +802,19 @@ progname_to_dict(PyObject *dict, const char *key)
@@ -58,11 +64,13 @@ index 1e75993480a..72860807133 100644
5864
#ifdef MS_WINDOWS
5965
extern HMODULE PyWin_DLLhModule;
6066
if (PyWin_DLLhModule) {
61-
@@ -817,12 +822,47 @@ library_to_dict(PyObject *dict, const char *key)
67+
@@ -817,12 +822,57 @@ library_to_dict(PyObject *dict, const char *key)
6268
}
6369
#endif
6470

65-
+ const void *target = (void *)Py_Initialize;
71+
+ const void *target = __builtin_extract_return_addr(
72+
+ __builtin_return_address(0)
73+
+ );
6674
+
6775
+#ifdef __linux__
6876
+ /* Linux libcs do not reliably report the realpath in dladdr dli_fname and
@@ -84,11 +92,19 @@ index 1e75993480a..72860807133 100644
8492
+ * TODO(geofft): Consider using PROCMAP_QUERY if supported.
8593
+ */
8694
+ uintptr_t low, high;
87-
+ char filename[PATH_MAX];
88-
+ while (fscanf(maps,
89-
+ "%lx-%lx %*s %*s %*s %*s %[^\n]",
90-
+ &low, &high, filename) == 3) {
95+
+ char rest[PATH_MAX + 1];
96+
+ while (fscanf(maps, "%lx-%lx %*s %*s %*s %*s", &low, &high) == 2) {
97+
+ if (fgets(rest, PATH_MAX + 1, maps) == NULL) {
98+
+ break;
99+
+ }
100+
+ if (strlen(rest) >= PATH_MAX) {
101+
+ // If the line is too long our parsing will be out of sync.
102+
+ break;
103+
+ }
104+
+
91105
+ if (low <= (uintptr_t)target && (uintptr_t)target < high) {
106+
+ // Skip past padding spaces in the filename.
107+
+ const char *filename = rest + strspn(rest, " ");
92108
+ if (filename[0] == '/') {
93109
+ return decode_to_dict(dict, key, filename);
94110
+ }

src/verify_distribution.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,9 @@ def assertPythonWorks(path: Path, argv0: str = None):
300300
)
301301
assertPythonWorks(venv / "bin" / "python")
302302

303-
# TODO: does not yet work on ARM64
304-
# with self.subTest(msg="weird argv[0]"):
305-
# assertPythonWorks(sys.executable, argv0="/dev/null")
303+
if sys.version_info[:2] >= (3, 14):
304+
with self.subTest(msg="weird argv[0]"):
305+
assertPythonWorks(sys.executable, argv0="/dev/null")
306306

307307

308308
if __name__ == "__main__":

0 commit comments

Comments
 (0)