comparison python/pydc/pydcext.c @ 43:1086ca649715

- fixed use after free issue with string handling (keeping strings as copy until after call)
author Tassilo Philipp
date Wed, 15 Apr 2020 21:58:13 +0200
parents 1d50532dce12
children 0f86a5ecfe61
comparison
equal deleted inserted replaced
42:1e3d929e43be 43:1086ca649715
31 # define DcPyCObject_FromVoidPtr(ptr, dtor) PyCapsule_New((ptr), NULL, (dtor)) // !new ref! 31 # define DcPyCObject_FromVoidPtr(ptr, dtor) PyCapsule_New((ptr), NULL, (dtor)) // !new ref!
32 # define DcPyCObject_AsVoidPtr(ppobj) PyCapsule_GetPointer((ppobj), NULL) 32 # define DcPyCObject_AsVoidPtr(ppobj) PyCapsule_GetPointer((ppobj), NULL)
33 # define DcPyCObject_SetVoidPtr(ppobj, ptr) //@@@ unsure what to do, cannot/shouldn't call this with a null pointer as this wants to call the dtor, so not doing anything: PyCapsule_SetPointer((ppobj), (ptr)) // this might need to call the dtor to behave like PyCObject_SetVoidPtr? 33 # define DcPyCObject_SetVoidPtr(ppobj, ptr) //@@@ unsure what to do, cannot/shouldn't call this with a null pointer as this wants to call the dtor, so not doing anything: PyCapsule_SetPointer((ppobj), (ptr)) // this might need to call the dtor to behave like PyCObject_SetVoidPtr?
34 #endif 34 #endif
35 35
36 #if(PY_VERSION_HEX >= 0x03030000)
37 # define PYUNICODE_CACHES_UTF8
38 #endif
39
36 #if PY_MAJOR_VERSION >= 3 40 #if PY_MAJOR_VERSION >= 3
37 # define EXPECT_LONG_TYPE_STR "an int" 41 # define EXPECT_LONG_TYPE_STR "an int"
38 # define DcPyString_GET_SIZE PyBytes_GET_SIZE 42 # define DcPyString_GET_SIZE PyBytes_GET_SIZE
39 # define DcPyString_Check PyBytes_Check 43 # define DcPyString_Check PyBytes_Check
40 # define DcPyString_AsString PyBytes_AsString 44 # define DcPyString_AsString PyBytes_AsString
167 #include "dyncall.h" 171 #include "dyncall.h"
168 #include "dyncall_signature.h" 172 #include "dyncall_signature.h"
169 173
170 DCCallVM* gpCall = NULL; 174 DCCallVM* gpCall = NULL;
171 175
176 // helper to temporarily copy string arguments
177 #define NUM_AUX_STRS 64
178 static int n_str_aux;
179 static char* str_aux[NUM_AUX_STRS]; // hard limit, most likely enough and checked for below @@@ugly though
180
172 181
173 /* call function */ 182 /* call function */
174 183
175 static PyObject* 184 static PyObject*
176 pydc_call(PyObject* self, PyObject* in_args) 185 pydc_call_impl(PyObject* self, PyObject* in_args) /* implementation, called by wrapper func pydc_call() */
177 { 186 {
178 PyObject *pcobj_funcptr, *args; 187 PyObject *pcobj_funcptr, *args;
179 const char *signature, *ptr; 188 const char *signature, *ptr;
180 char ch; 189 char ch;
181 int pos, ts; 190 int pos, ts;
358 367
359 case DC_SIGCHAR_STRING: // strings are considered to be immutable objects 368 case DC_SIGCHAR_STRING: // strings are considered to be immutable objects
360 { 369 {
361 PyObject* bo = NULL; 370 PyObject* bo = NULL;
362 const char* p; 371 const char* p;
363 char* p_;
364 size_t s; 372 size_t s;
365 if ( PyUnicode_Check(po) ) { 373 if ( PyUnicode_Check(po) )
374 {
375 if(n_str_aux >= NUM_AUX_STRS)
376 return PyErr_Format( PyExc_RuntimeError, "too many arguments (implementation limit of %d new UTF-8 string references reached) - abort", n_str_aux );
377
378 #if defined(PYUNICODE_CACHES_UTF8)
379 p = PyUnicode_AsUTF8(po);
380 #else
381 // w/o PyUnicode_AsUTF8(), which caches the UTF-8 representation, itself, create new ref we'll dec below
366 if((bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"))) // !new ref! 382 if((bo = PyUnicode_AsEncodedString(po, "utf-8", "strict"))) // !new ref!
367 p = PyBytes_AS_STRING(bo); // Borrowed pointer 383 p = PyBytes_AS_STRING(bo); // Borrowed pointer
384 #endif
368 } else if ( DcPyString_Check(po) ) 385 } else if ( DcPyString_Check(po) )
369 p = DcPyString_AsString(po); //@@@ must not be modified in any way 386 p = DcPyString_AsString(po);
370 else if ( PyByteArray_Check(po) ) 387 else if ( PyByteArray_Check(po) )
371 p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok //@@@ not sure if allowed to modify 388 p = (DCpointer) PyByteArray_AsString(po); // adds an extra '\0', but that's ok //@@@ not sure if allowed to modify
372 else 389 else
373 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str", pos ); 390 return PyErr_Format( PyExc_RuntimeError, "arg %d - expecting a str", pos );
374 391
375 // pointer points in any case to a buffer that shouldn't be modified, so pass a copy of the string to dyncall 392 // p points in any case to a buffer that shouldn't be modified, so pass a copy to dyncall (cleaned up after call)
376 s = strlen(p)+1; 393 s = strlen(p)+1;
377 p_ = malloc(s); 394 str_aux[n_str_aux] = malloc(s);
378 strncpy(p_, p, s); 395 strncpy(str_aux[n_str_aux], p, s);
379 Py_XDECREF(bo); 396 Py_XDECREF(bo);
380 dcArgPointer(gpCall, (DCpointer)p_); 397 dcArgPointer(gpCall, (DCpointer)str_aux[n_str_aux++]);
381 free(p_);
382 } 398 }
383 break; 399 break;
384 400
385 default: 401 default:
386 return PyErr_Format( PyExc_RuntimeError, "unknown signature character '%c'", ch); 402 return PyErr_Format( PyExc_RuntimeError, "unknown signature character '%c'", ch);
417 default: return PyErr_Format(PyExc_RuntimeError, "invalid return type signature"); 433 default: return PyErr_Format(PyExc_RuntimeError, "invalid return type signature");
418 } 434 }
419 } 435 }
420 436
421 437
438 static PyObject*
439 pydc_call(PyObject* self, PyObject* in_args)
440 {
441 int i;
442 n_str_aux = 0;
443 PyObject* o = pydc_call_impl(self, in_args);
444 for(i = 0; i<n_str_aux; ++i)
445 free(str_aux[i]);
446 return o;
447 }
448
422 449
423 // module deinit 450 // module deinit
424 static void deinit_pydc(void* x) 451 static void deinit_pydc(void* x)
425 { 452 {
426 if(gpCall) { 453 if(gpCall) {