Skip to content

Commit d27523a

Browse files
authored
Merge pull request python#36 from tiran/perf-file
2 parents e082e93 + 9052237 commit d27523a

1 file changed

Lines changed: 45 additions & 11 deletions

File tree

Objects/perf_trampoline.c

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#ifdef HAVE_PERF_TRAMPOLINE
77

8+
#include <fcntl.h>
89
#include <stdio.h>
910
#include <stdlib.h>
1011
#include <sys/mman.h>
@@ -29,6 +30,12 @@ struct code_arena_st {
2930

3031
typedef struct code_arena_st code_arena_t;
3132

33+
typedef enum {
34+
PERF_STATUS_FAILED = -1,
35+
PERF_STATUS_NO_INIT = 0,
36+
} perf_status_t;
37+
38+
static perf_status_t perf_status = PERF_STATUS_NO_INIT;
3239
static Py_ssize_t extra_code_index = -1;
3340
static code_arena_t *code_arena;
3441
static FILE *perf_map_file;
@@ -46,7 +53,10 @@ new_code_arena(void)
4653
-1, // fd (not used here)
4754
0); // offset (not used here)
4855
if (!memory) {
49-
Py_FatalError("Failed to allocate new code arena");
56+
PyErr_SetFromErrno(PyExc_OSError);
57+
_PyErr_WriteUnraisableMsg(
58+
"Failed to create new mmap for perf trampoline", NULL);
59+
perf_status = PERF_STATUS_FAILED;
5060
return -1;
5161
}
5262
void *start = &_Py_trampoline_func_start;
@@ -57,12 +67,21 @@ new_code_arena(void)
5767
for (size_t i = 0; i < n_copies; i++) {
5868
memcpy(memory + i * code_size, start, code_size * sizeof(char));
5969
}
60-
61-
mprotect(memory, mem_size, PROT_READ | PROT_EXEC);
70+
// Some systems may prevent us from creating executable code on the fly.
71+
int res = mprotect(memory, mem_size, PROT_READ | PROT_EXEC);
72+
if (res == -1) {
73+
PyErr_SetFromErrno(PyExc_OSError);
74+
munmap(memory, mem_size);
75+
_PyErr_WriteUnraisableMsg(
76+
"Failed to set mmap for perf trampoline to PROT_READ | PROT_EXEC", NULL);
77+
}
6278

6379
code_arena_t *new_arena = PyMem_RawCalloc(1, sizeof(code_arena_t));
6480
if (new_arena == NULL) {
65-
Py_FatalError("Failed to allocate new code arena struct");
81+
PyErr_NoMemory();
82+
munmap(memory, mem_size);
83+
_PyErr_WriteUnraisableMsg(
84+
"Failed to allocate new code arena struct", NULL);
6685
return -1;
6786
}
6887

@@ -107,7 +126,6 @@ compile_trampoline(void)
107126
return NULL;
108127
}
109128
}
110-
111129
assert(code_arena->size_left <= code_arena->size);
112130
return code_arena_new_code(code_arena);
113131
}
@@ -120,11 +138,23 @@ perf_map_get_file(void)
120138
}
121139
char filename[100];
122140
pid_t pid = getpid();
123-
// TODO: %d is incorrect if pid_t is long long
124-
snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", pid);
125-
perf_map_file = fopen(filename, "a");
141+
// Location and file name of perf map is hard-coded in perf tool.
142+
// Use exclusive create flag wit nofollow to prevent symlink attacks.
143+
int flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC;
144+
snprintf(filename, sizeof(filename)-1, "/tmp/perf-%jd.map", (intmax_t)pid);
145+
int fd = open(filename, flags, 0600);
146+
if (fd == -1) {
147+
perf_status = PERF_STATUS_FAILED;
148+
PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
149+
_PyErr_WriteUnraisableMsg("Failed to create perf map file", NULL);
150+
return NULL;
151+
}
152+
perf_map_file = fdopen(fd, "w");
126153
if (!perf_map_file) {
127-
_Py_FatalErrorFormat(__func__, "Couldn't open %s: errno(%d)", filename, errno);
154+
perf_status = PERF_STATUS_FAILED;
155+
PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
156+
close(fd);
157+
_PyErr_WriteUnraisableMsg("Failed to create perf map file handle", NULL);
128158
return NULL;
129159
}
130160
return perf_map_file;
@@ -136,6 +166,7 @@ perf_map_close(FILE *fp)
136166
if (fp) {
137167
return fclose(fp);
138168
}
169+
perf_status = PERF_STATUS_NO_INIT;
139170
return 0;
140171
}
141172

@@ -154,20 +185,23 @@ static PyObject *
154185
py_trampoline_evaluator(PyThreadState *ts, _PyInterpreterFrame *frame,
155186
int throw)
156187
{
188+
if (perf_status == PERF_STATUS_FAILED) {
189+
return _PyEval_EvalFrameDefault(ts, frame, throw);
190+
}
157191
PyCodeObject *co = frame->f_code;
158192
py_trampoline f = NULL;
159193
_PyCode_GetExtra((PyObject *)co, extra_code_index, (void **)&f);
160194
if (f == NULL) {
161195
FILE *pfile = perf_map_get_file();
162196
if (pfile == NULL) {
163-
return NULL;
197+
return _PyEval_EvalFrameDefault(ts, frame, throw);
164198
}
165199
if (extra_code_index == -1) {
166200
extra_code_index = _PyEval_RequestCodeExtraIndex(NULL);
167201
}
168202
py_trampoline new_trampoline = compile_trampoline();
169203
if (new_trampoline == NULL) {
170-
return NULL;
204+
return _PyEval_EvalFrameDefault(ts, frame, throw);
171205
}
172206
perf_map_write_entry(pfile, new_trampoline, code_arena->code_size,
173207
PyUnicode_AsUTF8(co->co_qualname),

0 commit comments

Comments
 (0)