Branch data Line data Source code
1 : : #include "parts.h"
2 : :
3 : : #include <stddef.h>
4 : :
5 : :
6 : : typedef struct {
7 : : PyMemAllocatorEx alloc;
8 : :
9 : : size_t malloc_size;
10 : : size_t calloc_nelem;
11 : : size_t calloc_elsize;
12 : : void *realloc_ptr;
13 : : size_t realloc_new_size;
14 : : void *free_ptr;
15 : : void *ctx;
16 : : } alloc_hook_t;
17 : :
18 : : static void *
19 : 0 : hook_malloc(void *ctx, size_t size)
20 : : {
21 : 0 : alloc_hook_t *hook = (alloc_hook_t *)ctx;
22 : 0 : hook->ctx = ctx;
23 : 0 : hook->malloc_size = size;
24 : 0 : return hook->alloc.malloc(hook->alloc.ctx, size);
25 : : }
26 : :
27 : : static void *
28 : 0 : hook_calloc(void *ctx, size_t nelem, size_t elsize)
29 : : {
30 : 0 : alloc_hook_t *hook = (alloc_hook_t *)ctx;
31 : 0 : hook->ctx = ctx;
32 : 0 : hook->calloc_nelem = nelem;
33 : 0 : hook->calloc_elsize = elsize;
34 : 0 : return hook->alloc.calloc(hook->alloc.ctx, nelem, elsize);
35 : : }
36 : :
37 : : static void *
38 : 0 : hook_realloc(void *ctx, void *ptr, size_t new_size)
39 : : {
40 : 0 : alloc_hook_t *hook = (alloc_hook_t *)ctx;
41 : 0 : hook->ctx = ctx;
42 : 0 : hook->realloc_ptr = ptr;
43 : 0 : hook->realloc_new_size = new_size;
44 : 0 : return hook->alloc.realloc(hook->alloc.ctx, ptr, new_size);
45 : : }
46 : :
47 : : static void
48 : 0 : hook_free(void *ctx, void *ptr)
49 : : {
50 : 0 : alloc_hook_t *hook = (alloc_hook_t *)ctx;
51 : 0 : hook->ctx = ctx;
52 : 0 : hook->free_ptr = ptr;
53 : 0 : hook->alloc.free(hook->alloc.ctx, ptr);
54 : 0 : }
55 : :
56 : : /* Most part of the following code is inherited from the pyfailmalloc project
57 : : * written by Victor Stinner. */
58 : : static struct {
59 : : int installed;
60 : : PyMemAllocatorEx raw;
61 : : PyMemAllocatorEx mem;
62 : : PyMemAllocatorEx obj;
63 : : } FmHook;
64 : :
65 : : static struct {
66 : : int start;
67 : : int stop;
68 : : Py_ssize_t count;
69 : : } FmData;
70 : :
71 : : static int
72 : 0 : fm_nomemory(void)
73 : : {
74 : 0 : FmData.count++;
75 [ # # ]: 0 : if (FmData.count > FmData.start &&
76 [ # # # # ]: 0 : (FmData.stop <= 0 || FmData.count <= FmData.stop))
77 : : {
78 : 0 : return 1;
79 : : }
80 : 0 : return 0;
81 : : }
82 : :
83 : : static void *
84 : 0 : hook_fmalloc(void *ctx, size_t size)
85 : : {
86 : 0 : PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
87 [ # # ]: 0 : if (fm_nomemory()) {
88 : 0 : return NULL;
89 : : }
90 : 0 : return alloc->malloc(alloc->ctx, size);
91 : : }
92 : :
93 : : static void *
94 : 0 : hook_fcalloc(void *ctx, size_t nelem, size_t elsize)
95 : : {
96 : 0 : PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
97 [ # # ]: 0 : if (fm_nomemory()) {
98 : 0 : return NULL;
99 : : }
100 : 0 : return alloc->calloc(alloc->ctx, nelem, elsize);
101 : : }
102 : :
103 : : static void *
104 : 0 : hook_frealloc(void *ctx, void *ptr, size_t new_size)
105 : : {
106 : 0 : PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
107 [ # # ]: 0 : if (fm_nomemory()) {
108 : 0 : return NULL;
109 : : }
110 : 0 : return alloc->realloc(alloc->ctx, ptr, new_size);
111 : : }
112 : :
113 : : static void
114 : 0 : hook_ffree(void *ctx, void *ptr)
115 : : {
116 : 0 : PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
117 : 0 : alloc->free(alloc->ctx, ptr);
118 : 0 : }
119 : :
120 : : static void
121 : 0 : fm_setup_hooks(void)
122 : : {
123 [ # # ]: 0 : if (FmHook.installed) {
124 : 0 : return;
125 : : }
126 : 0 : FmHook.installed = 1;
127 : :
128 : : PyMemAllocatorEx alloc;
129 : 0 : alloc.malloc = hook_fmalloc;
130 : 0 : alloc.calloc = hook_fcalloc;
131 : 0 : alloc.realloc = hook_frealloc;
132 : 0 : alloc.free = hook_ffree;
133 : 0 : PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &FmHook.raw);
134 : 0 : PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &FmHook.mem);
135 : 0 : PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &FmHook.obj);
136 : :
137 : 0 : alloc.ctx = &FmHook.raw;
138 : 0 : PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
139 : :
140 : 0 : alloc.ctx = &FmHook.mem;
141 : 0 : PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
142 : :
143 : 0 : alloc.ctx = &FmHook.obj;
144 : 0 : PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
145 : : }
146 : :
147 : : static void
148 : 0 : fm_remove_hooks(void)
149 : : {
150 [ # # ]: 0 : if (FmHook.installed) {
151 : 0 : FmHook.installed = 0;
152 : 0 : PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &FmHook.raw);
153 : 0 : PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &FmHook.mem);
154 : 0 : PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &FmHook.obj);
155 : : }
156 : 0 : }
157 : :
158 : : static PyObject *
159 : 0 : set_nomemory(PyObject *self, PyObject *args)
160 : : {
161 : : /* Memory allocation fails after 'start' allocation requests, and until
162 : : * 'stop' allocation requests except when 'stop' is negative or equal
163 : : * to 0 (default) in which case allocation failures never stop. */
164 : 0 : FmData.count = 0;
165 : 0 : FmData.stop = 0;
166 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "i|i", &FmData.start, &FmData.stop)) {
167 : 0 : return NULL;
168 : : }
169 : 0 : fm_setup_hooks();
170 : 0 : Py_RETURN_NONE;
171 : : }
172 : :
173 : : static PyObject *
174 : 0 : remove_mem_hooks(PyObject *self, PyObject *Py_UNUSED(ignored))
175 : : {
176 : 0 : fm_remove_hooks();
177 : 0 : Py_RETURN_NONE;
178 : : }
179 : :
180 : : static PyObject *
181 : 0 : test_setallocators(PyMemAllocatorDomain domain)
182 : : {
183 : 0 : PyObject *res = NULL;
184 : : const char *error_msg;
185 : : alloc_hook_t hook;
186 : :
187 : 0 : memset(&hook, 0, sizeof(hook));
188 : :
189 : : PyMemAllocatorEx alloc;
190 : 0 : alloc.ctx = &hook;
191 : 0 : alloc.malloc = &hook_malloc;
192 : 0 : alloc.calloc = &hook_calloc;
193 : 0 : alloc.realloc = &hook_realloc;
194 : 0 : alloc.free = &hook_free;
195 : 0 : PyMem_GetAllocator(domain, &hook.alloc);
196 : 0 : PyMem_SetAllocator(domain, &alloc);
197 : :
198 : : /* malloc, realloc, free */
199 : 0 : size_t size = 42;
200 : 0 : hook.ctx = NULL;
201 : : void *ptr;
202 [ # # # # ]: 0 : switch(domain) {
203 : 0 : case PYMEM_DOMAIN_RAW:
204 : 0 : ptr = PyMem_RawMalloc(size);
205 : 0 : break;
206 : 0 : case PYMEM_DOMAIN_MEM:
207 : 0 : ptr = PyMem_Malloc(size);
208 : 0 : break;
209 : 0 : case PYMEM_DOMAIN_OBJ:
210 : 0 : ptr = PyObject_Malloc(size);
211 : 0 : break;
212 : 0 : default:
213 : 0 : ptr = NULL;
214 : 0 : break;
215 : : }
216 : :
217 : : #define CHECK_CTX(FUNC) \
218 : : if (hook.ctx != &hook) { \
219 : : error_msg = FUNC " wrong context"; \
220 : : goto fail; \
221 : : } \
222 : : hook.ctx = NULL; /* reset for next check */
223 : :
224 [ # # ]: 0 : if (ptr == NULL) {
225 : 0 : error_msg = "malloc failed";
226 : 0 : goto fail;
227 : : }
228 [ # # ]: 0 : CHECK_CTX("malloc");
229 [ # # ]: 0 : if (hook.malloc_size != size) {
230 : 0 : error_msg = "malloc invalid size";
231 : 0 : goto fail;
232 : : }
233 : :
234 : 0 : size_t size2 = 200;
235 : : void *ptr2;
236 [ # # # # ]: 0 : switch(domain) {
237 : 0 : case PYMEM_DOMAIN_RAW:
238 : 0 : ptr2 = PyMem_RawRealloc(ptr, size2);
239 : 0 : break;
240 : 0 : case PYMEM_DOMAIN_MEM:
241 : 0 : ptr2 = PyMem_Realloc(ptr, size2);
242 : 0 : break;
243 : 0 : case PYMEM_DOMAIN_OBJ:
244 : 0 : ptr2 = PyObject_Realloc(ptr, size2);
245 : 0 : break;
246 : 0 : default:
247 : 0 : ptr2 = NULL;
248 : 0 : break;
249 : : }
250 : :
251 [ # # ]: 0 : if (ptr2 == NULL) {
252 : 0 : error_msg = "realloc failed";
253 : 0 : goto fail;
254 : : }
255 [ # # ]: 0 : CHECK_CTX("realloc");
256 [ # # # # ]: 0 : if (hook.realloc_ptr != ptr || hook.realloc_new_size != size2) {
257 : 0 : error_msg = "realloc invalid parameters";
258 : 0 : goto fail;
259 : : }
260 : :
261 [ # # # # ]: 0 : switch(domain) {
262 : 0 : case PYMEM_DOMAIN_RAW:
263 : 0 : PyMem_RawFree(ptr2);
264 : 0 : break;
265 : 0 : case PYMEM_DOMAIN_MEM:
266 : 0 : PyMem_Free(ptr2);
267 : 0 : break;
268 : 0 : case PYMEM_DOMAIN_OBJ:
269 : 0 : PyObject_Free(ptr2);
270 : 0 : break;
271 : : }
272 : :
273 [ # # ]: 0 : CHECK_CTX("free");
274 [ # # ]: 0 : if (hook.free_ptr != ptr2) {
275 : 0 : error_msg = "free invalid pointer";
276 : 0 : goto fail;
277 : : }
278 : :
279 : : /* calloc, free */
280 : 0 : size_t nelem = 2;
281 : 0 : size_t elsize = 5;
282 [ # # # # ]: 0 : switch(domain) {
283 : 0 : case PYMEM_DOMAIN_RAW:
284 : 0 : ptr = PyMem_RawCalloc(nelem, elsize);
285 : 0 : break;
286 : 0 : case PYMEM_DOMAIN_MEM:
287 : 0 : ptr = PyMem_Calloc(nelem, elsize);
288 : 0 : break;
289 : 0 : case PYMEM_DOMAIN_OBJ:
290 : 0 : ptr = PyObject_Calloc(nelem, elsize);
291 : 0 : break;
292 : 0 : default:
293 : 0 : ptr = NULL;
294 : 0 : break;
295 : : }
296 : :
297 [ # # ]: 0 : if (ptr == NULL) {
298 : 0 : error_msg = "calloc failed";
299 : 0 : goto fail;
300 : : }
301 [ # # ]: 0 : CHECK_CTX("calloc");
302 [ # # # # ]: 0 : if (hook.calloc_nelem != nelem || hook.calloc_elsize != elsize) {
303 : 0 : error_msg = "calloc invalid nelem or elsize";
304 : 0 : goto fail;
305 : : }
306 : :
307 : 0 : hook.free_ptr = NULL;
308 [ # # # # ]: 0 : switch(domain) {
309 : 0 : case PYMEM_DOMAIN_RAW:
310 : 0 : PyMem_RawFree(ptr);
311 : 0 : break;
312 : 0 : case PYMEM_DOMAIN_MEM:
313 : 0 : PyMem_Free(ptr);
314 : 0 : break;
315 : 0 : case PYMEM_DOMAIN_OBJ:
316 : 0 : PyObject_Free(ptr);
317 : 0 : break;
318 : : }
319 : :
320 [ # # ]: 0 : CHECK_CTX("calloc free");
321 [ # # ]: 0 : if (hook.free_ptr != ptr) {
322 : 0 : error_msg = "calloc free invalid pointer";
323 : 0 : goto fail;
324 : : }
325 : :
326 : 0 : res = Py_NewRef(Py_None);
327 : 0 : goto finally;
328 : :
329 : 0 : fail:
330 : 0 : PyErr_SetString(PyExc_RuntimeError, error_msg);
331 : :
332 : 0 : finally:
333 : 0 : PyMem_SetAllocator(domain, &hook.alloc);
334 : 0 : return res;
335 : :
336 : : #undef CHECK_CTX
337 : : }
338 : :
339 : : static PyObject *
340 : 0 : test_pyobject_setallocators(PyObject *self, PyObject *Py_UNUSED(ignored))
341 : : {
342 : 0 : return test_setallocators(PYMEM_DOMAIN_OBJ);
343 : : }
344 : :
345 : : static PyObject *
346 : 0 : test_pyobject_new(PyObject *self, PyObject *Py_UNUSED(ignored))
347 : : {
348 : : PyObject *obj;
349 : 0 : PyTypeObject *type = &PyBaseObject_Type;
350 : 0 : PyTypeObject *var_type = &PyLong_Type;
351 : :
352 : : // PyObject_New()
353 : 0 : obj = PyObject_New(PyObject, type);
354 [ # # ]: 0 : if (obj == NULL) {
355 : 0 : goto alloc_failed;
356 : : }
357 : 0 : Py_DECREF(obj);
358 : :
359 : : // PyObject_NEW()
360 : 0 : obj = PyObject_NEW(PyObject, type);
361 [ # # ]: 0 : if (obj == NULL) {
362 : 0 : goto alloc_failed;
363 : : }
364 : 0 : Py_DECREF(obj);
365 : :
366 : : // PyObject_NewVar()
367 : 0 : obj = PyObject_NewVar(PyObject, var_type, 3);
368 [ # # ]: 0 : if (obj == NULL) {
369 : 0 : goto alloc_failed;
370 : : }
371 : 0 : Py_DECREF(obj);
372 : :
373 : : // PyObject_NEW_VAR()
374 : 0 : obj = PyObject_NEW_VAR(PyObject, var_type, 3);
375 [ # # ]: 0 : if (obj == NULL) {
376 : 0 : goto alloc_failed;
377 : : }
378 : 0 : Py_DECREF(obj);
379 : :
380 : 0 : Py_RETURN_NONE;
381 : :
382 : 0 : alloc_failed:
383 : 0 : PyErr_NoMemory();
384 : 0 : return NULL;
385 : : }
386 : :
387 : : static PyObject *
388 : 0 : test_pymem_alloc0(PyObject *self, PyObject *Py_UNUSED(ignored))
389 : : {
390 : : void *ptr;
391 : :
392 : 0 : ptr = PyMem_RawMalloc(0);
393 [ # # ]: 0 : if (ptr == NULL) {
394 : 0 : PyErr_SetString(PyExc_RuntimeError,
395 : : "PyMem_RawMalloc(0) returns NULL");
396 : 0 : return NULL;
397 : : }
398 : 0 : PyMem_RawFree(ptr);
399 : :
400 : 0 : ptr = PyMem_RawCalloc(0, 0);
401 [ # # ]: 0 : if (ptr == NULL) {
402 : 0 : PyErr_SetString(PyExc_RuntimeError,
403 : : "PyMem_RawCalloc(0, 0) returns NULL");
404 : 0 : return NULL;
405 : : }
406 : 0 : PyMem_RawFree(ptr);
407 : :
408 : 0 : ptr = PyMem_Malloc(0);
409 [ # # ]: 0 : if (ptr == NULL) {
410 : 0 : PyErr_SetString(PyExc_RuntimeError,
411 : : "PyMem_Malloc(0) returns NULL");
412 : 0 : return NULL;
413 : : }
414 : 0 : PyMem_Free(ptr);
415 : :
416 : 0 : ptr = PyMem_Calloc(0, 0);
417 [ # # ]: 0 : if (ptr == NULL) {
418 : 0 : PyErr_SetString(PyExc_RuntimeError,
419 : : "PyMem_Calloc(0, 0) returns NULL");
420 : 0 : return NULL;
421 : : }
422 : 0 : PyMem_Free(ptr);
423 : :
424 : 0 : ptr = PyObject_Malloc(0);
425 [ # # ]: 0 : if (ptr == NULL) {
426 : 0 : PyErr_SetString(PyExc_RuntimeError,
427 : : "PyObject_Malloc(0) returns NULL");
428 : 0 : return NULL;
429 : : }
430 : 0 : PyObject_Free(ptr);
431 : :
432 : 0 : ptr = PyObject_Calloc(0, 0);
433 [ # # ]: 0 : if (ptr == NULL) {
434 : 0 : PyErr_SetString(PyExc_RuntimeError,
435 : : "PyObject_Calloc(0, 0) returns NULL");
436 : 0 : return NULL;
437 : : }
438 : 0 : PyObject_Free(ptr);
439 : :
440 : 0 : Py_RETURN_NONE;
441 : : }
442 : :
443 : : static PyObject *
444 : 0 : test_pymem_getallocatorsname(PyObject *self, PyObject *args)
445 : : {
446 : 0 : const char *name = _PyMem_GetCurrentAllocatorName();
447 [ # # ]: 0 : if (name == NULL) {
448 : 0 : PyErr_SetString(PyExc_RuntimeError, "cannot get allocators name");
449 : 0 : return NULL;
450 : : }
451 : 0 : return PyUnicode_FromString(name);
452 : : }
453 : :
454 : : static PyObject *
455 : 0 : test_pymem_setrawallocators(PyObject *self, PyObject *Py_UNUSED(ignored))
456 : : {
457 : 0 : return test_setallocators(PYMEM_DOMAIN_RAW);
458 : : }
459 : :
460 : : static PyObject *
461 : 0 : test_pymem_setallocators(PyObject *self, PyObject *Py_UNUSED(ignored))
462 : : {
463 : 0 : return test_setallocators(PYMEM_DOMAIN_MEM);
464 : : }
465 : :
466 : : static PyObject *
467 : 0 : pyobject_malloc_without_gil(PyObject *self, PyObject *args)
468 : : {
469 : : char *buffer;
470 : :
471 : : /* Deliberate bug to test debug hooks on Python memory allocators:
472 : : call PyObject_Malloc() without holding the GIL */
473 : 0 : Py_BEGIN_ALLOW_THREADS
474 : 0 : buffer = PyObject_Malloc(10);
475 : 0 : Py_END_ALLOW_THREADS
476 : :
477 : 0 : PyObject_Free(buffer);
478 : :
479 : 0 : Py_RETURN_NONE;
480 : : }
481 : :
482 : : static PyObject *
483 : 0 : pymem_buffer_overflow(PyObject *self, PyObject *args)
484 : : {
485 : : char *buffer;
486 : :
487 : : /* Deliberate buffer overflow to check that PyMem_Free() detects
488 : : the overflow when debug hooks are installed. */
489 : 0 : buffer = PyMem_Malloc(16);
490 [ # # ]: 0 : if (buffer == NULL) {
491 : 0 : PyErr_NoMemory();
492 : 0 : return NULL;
493 : : }
494 : 0 : buffer[16] = 'x';
495 : 0 : PyMem_Free(buffer);
496 : :
497 : 0 : Py_RETURN_NONE;
498 : : }
499 : :
500 : : static PyObject *
501 : 0 : pymem_api_misuse(PyObject *self, PyObject *args)
502 : : {
503 : : char *buffer;
504 : :
505 : : /* Deliberate misusage of Python allocators:
506 : : allococate with PyMem but release with PyMem_Raw. */
507 : 0 : buffer = PyMem_Malloc(16);
508 : 0 : PyMem_RawFree(buffer);
509 : :
510 : 0 : Py_RETURN_NONE;
511 : : }
512 : :
513 : : static PyObject *
514 : 0 : pymem_malloc_without_gil(PyObject *self, PyObject *args)
515 : : {
516 : : char *buffer;
517 : :
518 : : /* Deliberate bug to test debug hooks on Python memory allocators:
519 : : call PyMem_Malloc() without holding the GIL */
520 : 0 : Py_BEGIN_ALLOW_THREADS
521 : 0 : buffer = PyMem_Malloc(10);
522 : 0 : Py_END_ALLOW_THREADS
523 : :
524 : 0 : PyMem_Free(buffer);
525 : :
526 : 0 : Py_RETURN_NONE;
527 : : }
528 : :
529 : : static PyObject *
530 : 0 : test_pyobject_is_freed(const char *test_name, PyObject *op)
531 : : {
532 [ # # ]: 0 : if (!_PyObject_IsFreed(op)) {
533 : 0 : PyErr_SetString(PyExc_AssertionError,
534 : : "object is not seen as freed");
535 : 0 : return NULL;
536 : : }
537 : 0 : Py_RETURN_NONE;
538 : : }
539 : :
540 : : static PyObject *
541 : 0 : check_pyobject_null_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
542 : : {
543 : 0 : PyObject *op = NULL;
544 : 0 : return test_pyobject_is_freed("check_pyobject_null_is_freed", op);
545 : : }
546 : :
547 : :
548 : : static PyObject *
549 : 0 : check_pyobject_uninitialized_is_freed(PyObject *self,
550 : : PyObject *Py_UNUSED(args))
551 : : {
552 : 0 : PyObject *op = (PyObject *)PyObject_Malloc(sizeof(PyObject));
553 [ # # ]: 0 : if (op == NULL) {
554 : 0 : return NULL;
555 : : }
556 : : /* Initialize reference count to avoid early crash in ceval or GC */
557 : 0 : Py_SET_REFCNT(op, 1);
558 : : /* object fields like ob_type are uninitialized! */
559 : 0 : return test_pyobject_is_freed("check_pyobject_uninitialized_is_freed", op);
560 : : }
561 : :
562 : :
563 : : static PyObject *
564 : 0 : check_pyobject_forbidden_bytes_is_freed(PyObject *self,
565 : : PyObject *Py_UNUSED(args))
566 : : {
567 : : /* Allocate an incomplete PyObject structure: truncate 'ob_type' field */
568 : 0 : PyObject *op = (PyObject *)PyObject_Malloc(offsetof(PyObject, ob_type));
569 [ # # ]: 0 : if (op == NULL) {
570 : 0 : return NULL;
571 : : }
572 : : /* Initialize reference count to avoid early crash in ceval or GC */
573 : 0 : Py_SET_REFCNT(op, 1);
574 : : /* ob_type field is after the memory block: part of "forbidden bytes"
575 : : when using debug hooks on memory allocators! */
576 : 0 : return test_pyobject_is_freed("check_pyobject_forbidden_bytes_is_freed", op);
577 : : }
578 : :
579 : :
580 : : static PyObject *
581 : 0 : check_pyobject_freed_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
582 : : {
583 : : /* This test would fail if run with the address sanitizer */
584 : : #ifdef _Py_ADDRESS_SANITIZER
585 : : Py_RETURN_NONE;
586 : : #else
587 : 0 : PyObject *op = PyObject_CallNoArgs((PyObject *)&PyBaseObject_Type);
588 [ # # ]: 0 : if (op == NULL) {
589 : 0 : return NULL;
590 : : }
591 : 0 : Py_TYPE(op)->tp_dealloc(op);
592 : : /* Reset reference count to avoid early crash in ceval or GC */
593 : 0 : Py_SET_REFCNT(op, 1);
594 : : /* object memory is freed! */
595 : 0 : return test_pyobject_is_freed("check_pyobject_freed_is_freed", op);
596 : : #endif
597 : : }
598 : :
599 : : // Tracemalloc tests
600 : : static PyObject *
601 : 0 : tracemalloc_track(PyObject *self, PyObject *args)
602 : : {
603 : : unsigned int domain;
604 : : PyObject *ptr_obj;
605 : : Py_ssize_t size;
606 : 0 : int release_gil = 0;
607 : :
608 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "IOn|i",
609 : : &domain, &ptr_obj, &size, &release_gil))
610 : : {
611 : 0 : return NULL;
612 : : }
613 : 0 : void *ptr = PyLong_AsVoidPtr(ptr_obj);
614 [ # # ]: 0 : if (PyErr_Occurred()) {
615 : 0 : return NULL;
616 : : }
617 : :
618 : : int res;
619 [ # # ]: 0 : if (release_gil) {
620 : 0 : Py_BEGIN_ALLOW_THREADS
621 : 0 : res = PyTraceMalloc_Track(domain, (uintptr_t)ptr, size);
622 : 0 : Py_END_ALLOW_THREADS
623 : : }
624 : : else {
625 : 0 : res = PyTraceMalloc_Track(domain, (uintptr_t)ptr, size);
626 : : }
627 [ # # ]: 0 : if (res < 0) {
628 : 0 : PyErr_SetString(PyExc_RuntimeError, "PyTraceMalloc_Track error");
629 : 0 : return NULL;
630 : : }
631 : :
632 : 0 : Py_RETURN_NONE;
633 : : }
634 : :
635 : : static PyObject *
636 : 0 : tracemalloc_untrack(PyObject *self, PyObject *args)
637 : : {
638 : : unsigned int domain;
639 : : PyObject *ptr_obj;
640 : :
641 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "IO", &domain, &ptr_obj)) {
642 : 0 : return NULL;
643 : : }
644 : 0 : void *ptr = PyLong_AsVoidPtr(ptr_obj);
645 [ # # ]: 0 : if (PyErr_Occurred()) {
646 : 0 : return NULL;
647 : : }
648 : :
649 : 0 : int res = PyTraceMalloc_Untrack(domain, (uintptr_t)ptr);
650 [ # # ]: 0 : if (res < 0) {
651 : 0 : PyErr_SetString(PyExc_RuntimeError, "PyTraceMalloc_Untrack error");
652 : 0 : return NULL;
653 : : }
654 : :
655 : 0 : Py_RETURN_NONE;
656 : : }
657 : :
658 : : static PyObject *
659 : 0 : tracemalloc_get_traceback(PyObject *self, PyObject *args)
660 : : {
661 : : unsigned int domain;
662 : : PyObject *ptr_obj;
663 : :
664 [ # # ]: 0 : if (!PyArg_ParseTuple(args, "IO", &domain, &ptr_obj)) {
665 : 0 : return NULL;
666 : : }
667 : 0 : void *ptr = PyLong_AsVoidPtr(ptr_obj);
668 [ # # ]: 0 : if (PyErr_Occurred()) {
669 : 0 : return NULL;
670 : : }
671 : :
672 : 0 : return _PyTraceMalloc_GetTraceback(domain, (uintptr_t)ptr);
673 : : }
674 : :
675 : : static PyMethodDef test_methods[] = {
676 : : {"check_pyobject_forbidden_bytes_is_freed",
677 : : check_pyobject_forbidden_bytes_is_freed, METH_NOARGS},
678 : : {"check_pyobject_freed_is_freed", check_pyobject_freed_is_freed, METH_NOARGS},
679 : : {"check_pyobject_null_is_freed", check_pyobject_null_is_freed, METH_NOARGS},
680 : : {"check_pyobject_uninitialized_is_freed",
681 : : check_pyobject_uninitialized_is_freed, METH_NOARGS},
682 : : {"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
683 : : {"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS},
684 : : {"pymem_getallocatorsname", test_pymem_getallocatorsname, METH_NOARGS},
685 : : {"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS},
686 : : {"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS},
687 : : {"remove_mem_hooks", remove_mem_hooks, METH_NOARGS,
688 : : PyDoc_STR("Remove memory hooks.")},
689 : : {"set_nomemory", (PyCFunction)set_nomemory, METH_VARARGS,
690 : : PyDoc_STR("set_nomemory(start:int, stop:int = 0)")},
691 : : {"test_pymem_alloc0", test_pymem_alloc0, METH_NOARGS},
692 : : {"test_pymem_setallocators", test_pymem_setallocators, METH_NOARGS},
693 : : {"test_pymem_setrawallocators", test_pymem_setrawallocators, METH_NOARGS},
694 : : {"test_pyobject_new", test_pyobject_new, METH_NOARGS},
695 : : {"test_pyobject_setallocators", test_pyobject_setallocators, METH_NOARGS},
696 : :
697 : : // Tracemalloc tests
698 : : {"tracemalloc_track", tracemalloc_track, METH_VARARGS},
699 : : {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS},
700 : : {"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS},
701 : : {NULL},
702 : : };
703 : :
704 : : int
705 : 2 : _PyTestCapi_Init_Mem(PyObject *mod)
706 : : {
707 [ - + ]: 2 : if (PyModule_AddFunctions(mod, test_methods) < 0) {
708 : 0 : return -1;
709 : : }
710 : :
711 : : PyObject *v;
712 : : #ifdef WITH_PYMALLOC
713 : 2 : v = Py_NewRef(Py_True);
714 : : #else
715 : : v = Py_NewRef(Py_False);
716 : : #endif
717 : 2 : int rc = PyModule_AddObjectRef(mod, "WITH_PYMALLOC", v);
718 : 2 : Py_DECREF(v);
719 [ - + ]: 2 : if (rc < 0) {
720 : 0 : return -1;
721 : : }
722 : :
723 : 2 : return 0;
724 : : }
|