Skip to content

Commit 35a0009

Browse files
authored
Revert "gh-158445: Allocate memory in the heap in Py_GetVersion() (#158608)" (#159519)
This reverts commit f5e913c. Truncating the static buffer to 20 bytes caused a regression in debuggers and crash reporters checking `version` static buffer, see: #158608 (comment)
1 parent 21a36aa commit 35a0009

8 files changed

Lines changed: 25 additions & 74 deletions

File tree

‎Doc/c-api/interp-lifecycle.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ Process-wide parameters
514514
Return the version of this Python interpreter. This is a string that looks
515515
something like ::
516516
517-
"3.15.0rc2 (3.15.0~rc2-1.fc44.x86_64, Sep 3 2026, 00:00:00) [GCC 16.2.1 20260819 (Red Hat 16.2.1-2)]"
517+
"3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]"
518518
519519
.. index:: single: version (in module sys)
520520

‎Include/internal/pycore_pylifecycle.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extern int _Py_IsLocaleCoercionTarget(const char *ctype_loc);
2323

2424
/* Various one-time initializers */
2525

26-
extern PyStatus _Py_GetVersion_Init(void);
26+
extern void _Py_InitVersion(void);
2727
extern PyStatus _PyFaulthandler_Init(int enable);
2828
extern PyObject * _PyBuiltin_Init(PyInterpreterState *interp);
2929
extern int _PyBuiltin_InitPythonFunctions(PyObject *dict);
@@ -43,7 +43,6 @@ extern PyStatus _PyDateTime_InitTypes(PyInterpreterState *interp);
4343

4444
/* Various internal finalizers */
4545

46-
extern void _Py_GetVersion_Fini(void);
4746
extern int _PySignal_Init(int install_signal_handlers);
4847
extern void _PySignal_Fini(void);
4948

‎Lib/test/test_capi/test_build.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ def test_getcopyright(self):
2020

2121
# Test Py_GetCompiler()
2222
def test_getcompiler(self):
23-
self.assertIn(_testlimitedcapi.py_getcompiler(), sys.version)
23+
compiler = _testlimitedcapi.py_getcompiler()
24+
self.assertIsInstance(compiler, str)
25+
self.assertNotEqual(compiler, '')
2426

2527
# Test Py_GetBuildInfo()
2628
def test_getbuildinfo(self):
27-
self.assertIn(_testlimitedcapi.py_getbuildinfo(), sys.version)
29+
buildinfo = _testlimitedcapi.py_getbuildinfo()
30+
self.assertIsInstance(buildinfo, str)
31+
self.assertNotEqual(buildinfo, '')
2832

2933
# Test internal _Py_GetBuiltWithAssert()
3034
def test__getbuiltwithassert(self):

‎Modules/main.c‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,6 @@ pymain_free(void)
847847
_PyPathConfig_ClearGlobal();
848848
_Py_ClearArgcArgv();
849849
_PyRuntime_Finalize();
850-
_Py_GetVersion_Fini();
851850
}
852851

853852

‎Python/getcompiler.c‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#include "Python.h"
55

6-
// PC/pyconfig.h defines _Py_COMPILER
76
#ifdef _Py_COMPILER
87
# define COMPILER _Py_COMPILER
98
#endif
@@ -13,14 +12,14 @@
1312
// Note the __clang__ conditional has to come before the __GNUC__ one because
1413
// clang pretends to be GCC.
1514
#if defined(__clang__)
16-
# define COMPILER "[Clang " __clang_version__ "]"
15+
#define COMPILER "[Clang " __clang_version__ "]"
1716
#elif defined(__GNUC__)
18-
# define COMPILER "[GCC " __VERSION__ "]"
17+
#define COMPILER "[GCC " __VERSION__ "]"
1918
// Generic fallbacks.
2019
#elif defined(__cplusplus)
21-
# define COMPILER "[C++]"
20+
#define COMPILER "[C++]"
2221
#else
23-
# define COMPILER "[C]"
22+
#define COMPILER "[C]"
2423
#endif
2524

2625
#endif /* !COMPILER */

‎Python/getversion.c‎

Lines changed: 11 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,32 @@
22
/* Return the full version string. */
33

44
#include "Python.h"
5-
#include "pycore_initconfig.h" // _PyStatus_NO_MEMORY()
65

6+
#include "patchlevel.h"
77

88
static int initialized = 0;
9-
// Use the smallest possible buffer: it's only needed if malloc() fails *and*
10-
// Py_GetVersion() is called before Py_Initialize(). The buffer should be big
11-
// enough to store "3.16.0a0 " string.
12-
static char static_version[20];
13-
static char *heap_version = NULL;
9+
static char version[300];
1410

15-
16-
PyStatus
17-
_Py_GetVersion_Init(void)
11+
void _Py_InitVersion(void)
1812
{
1913
if (initialized) {
20-
return _PyStatus_OK();
14+
return;
2115
}
22-
16+
initialized = 1;
2317
#ifdef Py_GIL_DISABLED
24-
const char *format = "%s free-threading build (%s) %s";
25-
size_t format_len = strlen(" free-threading build () ");
18+
const char *buildinfo_format = "%.80s free-threading build (%.80s) %.80s";
2619
#else
27-
const char *format = "%s (%s) %s";
28-
size_t format_len = strlen(" () ");
20+
const char *buildinfo_format = "%.80s (%.80s) %.80s";
2921
#endif
30-
const char *version_str = PY_VERSION;
31-
const char *buildinfo = Py_GetBuildInfo();
32-
const char *compiler = Py_GetCompiler();
33-
// +1 for the trailing NUL byte
34-
size_t len = (format_len + strlen(version_str) + strlen(buildinfo)
35-
+ strlen(compiler) + 1);
36-
37-
// Always format the static version
38-
PyOS_snprintf(static_version, sizeof(static_version), format,
39-
version_str, buildinfo, compiler);
40-
41-
heap_version = malloc(len);
42-
if (heap_version == NULL) {
43-
// If malloc() failed, don't set initialized to 1, so next
44-
// Py_GetVersion() will try again to allocate memory.
45-
return _PyStatus_NO_MEMORY();
46-
}
47-
48-
PyOS_snprintf(heap_version, len, format,
49-
version_str, buildinfo, compiler);
50-
initialized = 1;
51-
return _PyStatus_OK();
52-
}
53-
54-
void
55-
_Py_GetVersion_Fini(void)
56-
{
57-
if (heap_version) {
58-
free(heap_version);
59-
heap_version = NULL;
60-
}
22+
PyOS_snprintf(version, sizeof(version), buildinfo_format,
23+
PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler());
6124
}
6225

6326
const char *
6427
Py_GetVersion(void)
6528
{
66-
PyStatus status = _Py_GetVersion_Init();
67-
// Ignore error: Py_GetVersion() API cannot report error
68-
(void)status;
69-
70-
if (heap_version) {
71-
return heap_version;
72-
}
73-
else {
74-
return static_version;
75-
}
29+
_Py_InitVersion();
30+
return version;
7631
}
7732

7833
// Export the Python hex version as a constant.

‎Python/pylifecycle.c‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -559,11 +559,7 @@ pycore_init_runtime(_PyRuntimeState *runtime,
559559
*/
560560
_PyRuntimeState_SetFinalizing(runtime, NULL);
561561

562-
status = _Py_GetVersion_Init();
563-
if (_PyStatus_EXCEPTION(status)) {
564-
return status;
565-
}
566-
562+
_Py_InitVersion();
567563
_Py_DumpTraceback_Init();
568564

569565
status = _Py_HashRandomization_Init(config);

‎Tools/c-analyzer/cpython/ignored.tsv‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ Objects/unicodeobject.c _init_global_state initialized -
4848
Modules/getbuildinfo.c - buildinfo -
4949
Modules/getbuildinfo.c - initialized -
5050
Python/getversion.c - initialized -
51-
Python/getversion.c - static_version -
52-
Python/getversion.c - heap_version -
51+
Python/getversion.c - version -
5352

5453
## public C-API - set during first init
5554
Python/bootstrap_hash.c - _Py_HashSecret_Initialized -

0 commit comments

Comments
 (0)