LCOV - code coverage report
Current view: | top level - Modules/_testcapi - mem.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 7 | 354 | 2.0 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 1 | 33 | 3.0 %
|  |  | Branches: | 2 | 122 | 1.6 %

               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                 :            : }

Generated by: LCOV version 1.14