[3.14] gh-142571: Check for errors before calling each syscall in `PyUnstable_CopyPerfMapFile()` (GH-142460) (#142600)
Co-authored-by: AZero13 <gfunni234@gmail.com>
Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
diff --git a/Misc/NEWS.d/next/C_API/2025-12-11-09-06-36.gh-issue-142571.Csdxnn.rst b/Misc/NEWS.d/next/C_API/2025-12-11-09-06-36.gh-issue-142571.Csdxnn.rst
new file mode 100644
index 0000000..ea419b4
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2025-12-11-09-06-36.gh-issue-142571.Csdxnn.rst
@@ -0,0 +1 @@
+:c:func:`!PyUnstable_CopyPerfMapFile` now checks that opening the file succeeded before flushing.
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index a7280c0..545b130 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -2786,20 +2786,31 @@ PyAPI_FUNC(int) PyUnstable_CopyPerfMapFile(const char* parent_filename) {
}
char buf[4096];
PyThread_acquire_lock(perf_map_state.map_lock, 1);
- int fflush_result = 0, result = 0;
+ int result = 0;
while (1) {
size_t bytes_read = fread(buf, 1, sizeof(buf), from);
- size_t bytes_written = fwrite(buf, 1, bytes_read, perf_map_state.perf_map);
- fflush_result = fflush(perf_map_state.perf_map);
- if (fflush_result != 0 || bytes_read == 0 || bytes_written < bytes_read) {
- result = -1;
- goto close_and_release;
+ if (bytes_read == 0) {
+ if (ferror(from)) {
+ result = -1;
+ }
+ break;
}
+
+ size_t bytes_written = fwrite(buf, 1, bytes_read, perf_map_state.perf_map);
+ if (bytes_written < bytes_read) {
+ result = -1;
+ break;
+ }
+
+ if (fflush(perf_map_state.perf_map) != 0) {
+ result = -1;
+ break;
+ }
+
if (bytes_read < sizeof(buf) && feof(from)) {
- goto close_and_release;
+ break;
}
}
-close_and_release:
fclose(from);
PyThread_release_lock(perf_map_state.map_lock);
return result;