bpo-38622: Add missing audit events for ctypes module (GH-17158)

This commit is contained in:
Steve Dower 2019-11-18 11:32:46 -08:00 committed by GitHub
parent 476e76f7cf
commit 00923c6399
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 7 deletions

View File

@ -1526,6 +1526,12 @@ object is available:
``ctypes.dlsym`` with arguments ``library`` (the library object) and ``name`` ``ctypes.dlsym`` with arguments ``library`` (the library object) and ``name``
(the symbol's name as a string or integer). (the symbol's name as a string or integer).
.. audit-event:: ctypes.dlsym/handle handle,name ctypes.LibraryLoader
In cases when only the library handle is available rather than the object,
accessing a function raises an auditing event ``ctypes.dlsym/handle`` with
arguments ``handle`` (the raw library handle) and ``name``.
.. _ctypes-foreign-functions: .. _ctypes-foreign-functions:
Foreign functions Foreign functions
@ -1611,6 +1617,19 @@ They are instances of a private class:
passed arguments. passed arguments.
.. audit-event:: ctypes.seh_exception code foreign-functions
On Windows, when a foreign function call raises a system exception (for
example, due to an access violation), it will be captured and replaced with
a suitable Python exception. Further, an auditing event
``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an
audit hook to replace the exception with its own.
.. audit-event:: ctypes.call_function func_pointer,arguments ctype-foreign-functions
Some ways to invoke foreign function calls may raise an auditing event
``ctypes.call_function`` with arguments ``function pointer`` and ``arguments``.
.. _ctypes-function-prototypes: .. _ctypes-function-prototypes:
Function prototypes Function prototypes
@ -1802,6 +1821,8 @@ Utility functions
Returns the address of the memory buffer as integer. *obj* must be an Returns the address of the memory buffer as integer. *obj* must be an
instance of a ctypes type. instance of a ctypes type.
.. audit-event:: ctypes.addressof obj ctypes.addressof
.. function:: alignment(obj_or_type) .. function:: alignment(obj_or_type)
@ -1844,6 +1865,7 @@ Utility functions
termination character. An integer can be passed as second argument which allows termination character. An integer can be passed as second argument which allows
specifying the size of the array if the length of the bytes should not be used. specifying the size of the array if the length of the bytes should not be used.
.. audit-event:: ctypes.create_string_buffer init,size ctypes.create_string_buffer
.. function:: create_unicode_buffer(init_or_size, size=None) .. function:: create_unicode_buffer(init_or_size, size=None)
@ -1860,6 +1882,7 @@ Utility functions
allows specifying the size of the array if the length of the string should not allows specifying the size of the array if the length of the string should not
be used. be used.
.. audit-event:: ctypes.create_unicode_buffer init,size ctypes.create_unicode_buffer
.. function:: DllCanUnloadNow() .. function:: DllCanUnloadNow()
@ -1917,11 +1940,15 @@ Utility functions
Returns the current value of the ctypes-private copy of the system Returns the current value of the ctypes-private copy of the system
:data:`errno` variable in the calling thread. :data:`errno` variable in the calling thread.
.. audit-event:: ctypes.get_errno "" ctypes.get_errno
.. function:: get_last_error() .. function:: get_last_error()
Windows only: returns the current value of the ctypes-private copy of the system Windows only: returns the current value of the ctypes-private copy of the system
:data:`LastError` variable in the calling thread. :data:`LastError` variable in the calling thread.
.. audit-event:: ctypes.get_last_error "" ctypes.get_last_error
.. function:: memmove(dst, src, count) .. function:: memmove(dst, src, count)
Same as the standard C memmove library function: copies *count* bytes from Same as the standard C memmove library function: copies *count* bytes from
@ -1965,6 +1992,7 @@ Utility functions
Set the current value of the ctypes-private copy of the system :data:`errno` Set the current value of the ctypes-private copy of the system :data:`errno`
variable in the calling thread to *value* and return the previous value. variable in the calling thread to *value* and return the previous value.
.. audit-event:: ctypes.set_errno errno ctypes.set_errno
.. function:: set_last_error(value) .. function:: set_last_error(value)
@ -1973,6 +2001,7 @@ Utility functions
:data:`LastError` variable in the calling thread to *value* and return the :data:`LastError` variable in the calling thread to *value* and return the
previous value. previous value.
.. audit-event:: ctypes.set_last_error error ctypes.set_last_error
.. function:: sizeof(obj_or_type) .. function:: sizeof(obj_or_type)
@ -1987,6 +2016,8 @@ Utility functions
object. If size is specified, it is used as size, otherwise the string is assumed object. If size is specified, it is used as size, otherwise the string is assumed
to be zero-terminated. to be zero-terminated.
.. audit-event:: ctypes.string_at address,size ctypes.string_at
.. function:: WinError(code=None, descr=None) .. function:: WinError(code=None, descr=None)
@ -2007,6 +2038,8 @@ Utility functions
characters of the string, otherwise the string is assumed to be characters of the string, otherwise the string is assumed to be
zero-terminated. zero-terminated.
.. audit-event:: ctypes.wstring_at address,size ctypes.wstring_at
.. _ctypes-data-types: .. _ctypes-data-types:
@ -2034,6 +2067,7 @@ Data types
source buffer in bytes; the default is zero. If the source buffer is not source buffer in bytes; the default is zero. If the source buffer is not
large enough a :exc:`ValueError` is raised. large enough a :exc:`ValueError` is raised.
.. audit-event:: ctypes.cdata/buffer pointer,size,offset ctypes._CData.from_buffer
.. method:: _CData.from_buffer_copy(source[, offset]) .. method:: _CData.from_buffer_copy(source[, offset])
@ -2043,6 +2077,8 @@ Data types
is zero. If the source buffer is not large enough a :exc:`ValueError` is is zero. If the source buffer is not large enough a :exc:`ValueError` is
raised. raised.
.. audit-event:: ctypes.cdata/buffer pointer,size,offset ctypes._CData.from_buffer_copy
.. method:: from_address(address) .. method:: from_address(address)
This method returns a ctypes type instance using the memory specified by This method returns a ctypes type instance using the memory specified by

View File

@ -52,11 +52,13 @@ def create_string_buffer(init, size=None):
if isinstance(init, bytes): if isinstance(init, bytes):
if size is None: if size is None:
size = len(init)+1 size = len(init)+1
_sys.audit("ctypes.create_string_buffer", init, size)
buftype = c_char * size buftype = c_char * size
buf = buftype() buf = buftype()
buf.value = init buf.value = init
return buf return buf
elif isinstance(init, int): elif isinstance(init, int):
_sys.audit("ctypes.create_string_buffer", None, init)
buftype = c_char * init buftype = c_char * init
buf = buftype() buf = buftype()
return buf return buf
@ -283,11 +285,13 @@ def create_unicode_buffer(init, size=None):
# 32-bit wchar_t (1 wchar_t per Unicode character). +1 for # 32-bit wchar_t (1 wchar_t per Unicode character). +1 for
# trailing NUL character. # trailing NUL character.
size = len(init) + 1 size = len(init) + 1
_sys.audit("ctypes.create_unicode_buffer", init, size)
buftype = c_wchar * size buftype = c_wchar * size
buf = buftype() buf = buftype()
buf.value = init buf.value = init
return buf return buf
elif isinstance(init, int): elif isinstance(init, int):
_sys.audit("ctypes.create_unicode_buffer", None, init)
buftype = c_wchar * init buftype = c_wchar * init
buf = buftype() buf = buftype()
return buf return buf

View File

@ -0,0 +1 @@
Add additional audit events for the :mod:`ctypes` module.

View File

@ -641,6 +641,12 @@ CDataType_from_buffer(PyObject *type, PyObject *args)
return NULL; return NULL;
} }
if (PySys_Audit("ctypes.cdata/buffer", "nnn",
(Py_ssize_t)buffer->buf, buffer->len, offset) < 0) {
Py_DECREF(mv);
return NULL;
}
result = PyCData_AtAddress(type, (char *)buffer->buf + offset); result = PyCData_AtAddress(type, (char *)buffer->buf + offset);
if (result == NULL) { if (result == NULL) {
Py_DECREF(mv); Py_DECREF(mv);
@ -691,6 +697,12 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args)
return NULL; return NULL;
} }
if (PySys_Audit("ctypes.cdata/buffer", "nnn",
(Py_ssize_t)buffer.buf, buffer.len, offset) < 0) {
PyBuffer_Release(&buffer);
return NULL;
}
result = GenericPyCData_new((PyTypeObject *)type, NULL, NULL); result = GenericPyCData_new((PyTypeObject *)type, NULL, NULL);
if (result != NULL) { if (result != NULL) {
memcpy(((CDataObject *)result)->b_ptr, memcpy(((CDataObject *)result)->b_ptr,
@ -714,6 +726,9 @@ CDataType_in_dll(PyObject *type, PyObject *args)
if (!PyArg_ParseTuple(args, "Os:in_dll", &dll, &name)) if (!PyArg_ParseTuple(args, "Os:in_dll", &dll, &name))
return NULL; return NULL;
if (PySys_Audit("ctypes.dlsym", "O", args) < 0) {
return NULL;
}
obj = PyObject_GetAttrString(dll, "_handle"); obj = PyObject_GetAttrString(dll, "_handle");
if (!obj) if (!obj)
@ -5535,6 +5550,9 @@ create_comerror(void)
static PyObject * static PyObject *
string_at(const char *ptr, int size) string_at(const char *ptr, int size)
{ {
if (PySys_Audit("ctypes.string_at", "ni", (Py_ssize_t)ptr, size) < 0) {
return NULL;
}
if (size == -1) if (size == -1)
return PyBytes_FromStringAndSize(ptr, strlen(ptr)); return PyBytes_FromStringAndSize(ptr, strlen(ptr));
return PyBytes_FromStringAndSize(ptr, size); return PyBytes_FromStringAndSize(ptr, size);
@ -5626,6 +5644,9 @@ static PyObject *
wstring_at(const wchar_t *ptr, int size) wstring_at(const wchar_t *ptr, int size)
{ {
Py_ssize_t ssize = size; Py_ssize_t ssize = size;
if (PySys_Audit("ctypes.wstring_at", "nn", (Py_ssize_t)ptr, ssize) < 0) {
return NULL;
}
if (ssize == -1) if (ssize == -1)
ssize = wcslen(ptr); ssize = wcslen(ptr);
return PyUnicode_FromWideChar(ptr, ssize); return PyUnicode_FromWideChar(ptr, ssize);

View File

@ -199,8 +199,9 @@ set_error_internal(PyObject *self, PyObject *args, int index)
PyObject *errobj; PyObject *errobj;
int *space; int *space;
if (!PyArg_ParseTuple(args, "i", &new_errno)) if (!PyArg_ParseTuple(args, "i", &new_errno)) {
return NULL; return NULL;
}
errobj = _ctypes_get_errobj(&space); errobj = _ctypes_get_errobj(&space);
if (errobj == NULL) if (errobj == NULL)
return NULL; return NULL;
@ -213,12 +214,18 @@ set_error_internal(PyObject *self, PyObject *args, int index)
static PyObject * static PyObject *
get_errno(PyObject *self, PyObject *args) get_errno(PyObject *self, PyObject *args)
{ {
if (PySys_Audit("ctypes.get_errno", NULL) < 0) {
return NULL;
}
return get_error_internal(self, args, 0); return get_error_internal(self, args, 0);
} }
static PyObject * static PyObject *
set_errno(PyObject *self, PyObject *args) set_errno(PyObject *self, PyObject *args)
{ {
if (PySys_Audit("ctypes.set_errno", "O", args) < 0) {
return NULL;
}
return set_error_internal(self, args, 0); return set_error_internal(self, args, 0);
} }
@ -227,12 +234,18 @@ set_errno(PyObject *self, PyObject *args)
static PyObject * static PyObject *
get_last_error(PyObject *self, PyObject *args) get_last_error(PyObject *self, PyObject *args)
{ {
if (PySys_Audit("ctypes.get_last_error", NULL) < 0) {
return NULL;
}
return get_error_internal(self, args, 1); return get_error_internal(self, args, 1);
} }
static PyObject * static PyObject *
set_last_error(PyObject *self, PyObject *args) set_last_error(PyObject *self, PyObject *args)
{ {
if (PySys_Audit("ctypes.set_last_error", "O", args) < 0) {
return NULL;
}
return set_error_internal(self, args, 1); return set_error_internal(self, args, 1);
} }
@ -262,6 +275,11 @@ static WCHAR *FormatError(DWORD code)
#ifndef DONT_USE_SEH #ifndef DONT_USE_SEH
static void SetException(DWORD code, EXCEPTION_RECORD *pr) static void SetException(DWORD code, EXCEPTION_RECORD *pr)
{ {
if (PySys_Audit("ctypes.seh_exception", "I", code) < 0) {
/* An exception was set by the audit hook */
return;
}
/* The 'code' is a normal win32 error code so it could be handled by /* The 'code' is a normal win32 error code so it could be handled by
PyErr_SetFromWindowsErr(). However, for some errors, we have additional PyErr_SetFromWindowsErr(). However, for some errors, we have additional
information not included in the error code. We handle those here and information not included in the error code. We handle those here and
@ -1427,6 +1445,9 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O&s:dlsym", if (!PyArg_ParseTuple(args, "O&s:dlsym",
&_parse_voidp, &handle, &name)) &_parse_voidp, &handle, &name))
return NULL; return NULL;
if (PySys_Audit("ctypes.dlsym/handle", "O", args) < 0) {
return NULL;
}
ptr = ctypes_dlsym((void*)handle, name); ptr = ctypes_dlsym((void*)handle, name);
if (!ptr) { if (!ptr) {
PyErr_SetString(PyExc_OSError, PyErr_SetString(PyExc_OSError,
@ -1454,6 +1475,10 @@ call_function(PyObject *self, PyObject *args)
&_parse_voidp, &func, &_parse_voidp, &func,
&PyTuple_Type, &arguments)) &PyTuple_Type, &arguments))
return NULL; return NULL;
if (PySys_Audit("ctypes.call_function", "nO",
(Py_ssize_t)func, arguments) < 0) {
return NULL;
}
result = _ctypes_callproc((PPROC)func, result = _ctypes_callproc((PPROC)func,
arguments, arguments,
@ -1485,6 +1510,10 @@ call_cdeclfunction(PyObject *self, PyObject *args)
&_parse_voidp, &func, &_parse_voidp, &func,
&PyTuple_Type, &arguments)) &PyTuple_Type, &arguments))
return NULL; return NULL;
if (PySys_Audit("ctypes.call_function", "nO",
(Py_ssize_t)func, arguments) < 0) {
return NULL;
}
result = _ctypes_callproc((PPROC)func, result = _ctypes_callproc((PPROC)func,
arguments, arguments,
@ -1597,11 +1626,15 @@ static const char addressof_doc[] =
static PyObject * static PyObject *
addressof(PyObject *self, PyObject *obj) addressof(PyObject *self, PyObject *obj)
{ {
if (CDataObject_Check(obj)) if (!CDataObject_Check(obj)) {
return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr); PyErr_SetString(PyExc_TypeError,
PyErr_SetString(PyExc_TypeError, "invalid type");
"invalid type"); return NULL;
return NULL; }
if (PySys_Audit("ctypes.addressof", "O", obj) < 0) {
return NULL;
}
return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr);
} }
static int static int
@ -1615,8 +1648,12 @@ static PyObject *
My_PyObj_FromPtr(PyObject *self, PyObject *args) My_PyObj_FromPtr(PyObject *self, PyObject *args)
{ {
PyObject *ob; PyObject *ob;
if (!PyArg_ParseTuple(args, "O&:PyObj_FromPtr", converter, &ob)) if (!PyArg_ParseTuple(args, "O&:PyObj_FromPtr", converter, &ob)) {
return NULL; return NULL;
}
if (PySys_Audit("ctypes.PyObj_FromPtr", "O", ob) < 0) {
return NULL;
}
Py_INCREF(ob); Py_INCREF(ob);
return ob; return ob;
} }