Skip to content

bpo-38622: Add missing audit events for ctypes module #17158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,12 @@ object is available:
``ctypes.dlsym`` with arguments ``library`` (the library object) and ``name``
(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:

Foreign functions
Expand Down Expand Up @@ -1611,6 +1617,19 @@ They are instances of a private class:
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:

Function prototypes
Expand Down Expand Up @@ -1802,6 +1821,8 @@ Utility functions
Returns the address of the memory buffer as integer. *obj* must be an
instance of a ctypes type.

.. audit-event:: ctypes.addressof obj ctypes.addressof


.. function:: alignment(obj_or_type)

Expand Down Expand Up @@ -1844,6 +1865,7 @@ Utility functions
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.

.. audit-event:: ctypes.create_string_buffer init,size ctypes.create_string_buffer


.. function:: create_unicode_buffer(init_or_size, size=None)
Expand All @@ -1860,6 +1882,7 @@ Utility functions
allows specifying the size of the array if the length of the string should not
be used.

.. audit-event:: ctypes.create_unicode_buffer init,size ctypes.create_unicode_buffer


.. function:: DllCanUnloadNow()
Expand Down Expand Up @@ -1917,11 +1940,15 @@ Utility functions
Returns the current value of the ctypes-private copy of the system
:data:`errno` variable in the calling thread.

.. audit-event:: ctypes.get_errno "" ctypes.get_errno

.. function:: get_last_error()

Windows only: returns the current value of the ctypes-private copy of the system
:data:`LastError` variable in the calling thread.

.. audit-event:: ctypes.get_last_error "" ctypes.get_last_error

.. function:: memmove(dst, src, count)

Same as the standard C memmove library function: copies *count* bytes from
Expand Down Expand Up @@ -1965,6 +1992,7 @@ Utility functions
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.

.. audit-event:: ctypes.set_errno errno ctypes.set_errno


.. function:: set_last_error(value)
Expand All @@ -1973,6 +2001,7 @@ Utility functions
:data:`LastError` variable in the calling thread to *value* and return the
previous value.

.. audit-event:: ctypes.set_last_error error ctypes.set_last_error


.. function:: sizeof(obj_or_type)
Expand All @@ -1987,6 +2016,8 @@ Utility functions
object. If size is specified, it is used as size, otherwise the string is assumed
to be zero-terminated.

.. audit-event:: ctypes.string_at address,size ctypes.string_at


.. function:: WinError(code=None, descr=None)

Expand All @@ -2007,6 +2038,8 @@ Utility functions
characters of the string, otherwise the string is assumed to be
zero-terminated.

.. audit-event:: ctypes.wstring_at address,size ctypes.wstring_at


.. _ctypes-data-types:

Expand Down Expand Up @@ -2034,6 +2067,7 @@ Data types
source buffer in bytes; the default is zero. If the source buffer is not
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])

Expand All @@ -2043,6 +2077,8 @@ Data types
is zero. If the source buffer is not large enough a :exc:`ValueError` is
raised.

.. audit-event:: ctypes.cdata/buffer pointer,size,offset ctypes._CData.from_buffer_copy

.. method:: from_address(address)

This method returns a ctypes type instance using the memory specified by
Expand Down
4 changes: 4 additions & 0 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ def create_string_buffer(init, size=None):
if isinstance(init, bytes):
if size is None:
size = len(init)+1
_sys.audit("ctypes.create_string_buffer", init, size)
buftype = c_char * size
buf = buftype()
buf.value = init
return buf
elif isinstance(init, int):
_sys.audit("ctypes.create_string_buffer", None, init)
buftype = c_char * init
buf = buftype()
return buf
Expand Down Expand Up @@ -283,11 +285,13 @@ def create_unicode_buffer(init, size=None):
# 32-bit wchar_t (1 wchar_t per Unicode character). +1 for
# trailing NUL character.
size = len(init) + 1
_sys.audit("ctypes.create_unicode_buffer", init, size)
buftype = c_wchar * size
buf = buftype()
buf.value = init
return buf
elif isinstance(init, int):
_sys.audit("ctypes.create_unicode_buffer", None, init)
buftype = c_wchar * init
buf = buftype()
return buf
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add additional audit events for the :mod:`ctypes` module.
21 changes: 21 additions & 0 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,12 @@ CDataType_from_buffer(PyObject *type, PyObject *args)
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);
if (result == NULL) {
Py_DECREF(mv);
Expand Down Expand Up @@ -691,6 +697,12 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args)
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);
if (result != NULL) {
memcpy(((CDataObject *)result)->b_ptr,
Expand All @@ -714,6 +726,9 @@ CDataType_in_dll(PyObject *type, PyObject *args)

if (!PyArg_ParseTuple(args, "Os:in_dll", &dll, &name))
return NULL;
if (PySys_Audit("ctypes.dlsym", "O", args) < 0) {
return NULL;
}

obj = PyObject_GetAttrString(dll, "_handle");
if (!obj)
Expand Down Expand Up @@ -5535,6 +5550,9 @@ create_comerror(void)
static PyObject *
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)
return PyBytes_FromStringAndSize(ptr, strlen(ptr));
return PyBytes_FromStringAndSize(ptr, size);
Expand Down Expand Up @@ -5626,6 +5644,9 @@ static PyObject *
wstring_at(const wchar_t *ptr, int size)
{
Py_ssize_t ssize = size;
if (PySys_Audit("ctypes.wstring_at", "nn", (Py_ssize_t)ptr, ssize) < 0) {
return NULL;
}
if (ssize == -1)
ssize = wcslen(ptr);
return PyUnicode_FromWideChar(ptr, ssize);
Expand Down
51 changes: 44 additions & 7 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,9 @@ set_error_internal(PyObject *self, PyObject *args, int index)
PyObject *errobj;
int *space;

if (!PyArg_ParseTuple(args, "i", &new_errno))
if (!PyArg_ParseTuple(args, "i", &new_errno)) {
return NULL;
}
errobj = _ctypes_get_errobj(&space);
if (errobj == NULL)
return NULL;
Expand All @@ -213,12 +214,18 @@ set_error_internal(PyObject *self, PyObject *args, int index)
static PyObject *
get_errno(PyObject *self, PyObject *args)
{
if (PySys_Audit("ctypes.get_errno", NULL) < 0) {
return NULL;
}
return get_error_internal(self, args, 0);
}

static PyObject *
set_errno(PyObject *self, PyObject *args)
{
if (PySys_Audit("ctypes.set_errno", "O", args) < 0) {
return NULL;
}
return set_error_internal(self, args, 0);
}

Expand All @@ -227,12 +234,18 @@ set_errno(PyObject *self, PyObject *args)
static PyObject *
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);
}

static PyObject *
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);
}

Expand Down Expand Up @@ -262,6 +275,11 @@ static WCHAR *FormatError(DWORD code)
#ifndef DONT_USE_SEH
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
PyErr_SetFromWindowsErr(). However, for some errors, we have additional
information not included in the error code. We handle those here and
Expand Down Expand Up @@ -1427,6 +1445,9 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O&s:dlsym",
&_parse_voidp, &handle, &name))
return NULL;
if (PySys_Audit("ctypes.dlsym/handle", "O", args) < 0) {
return NULL;
}
ptr = ctypes_dlsym((void*)handle, name);
if (!ptr) {
PyErr_SetString(PyExc_OSError,
Expand Down Expand Up @@ -1454,6 +1475,10 @@ call_function(PyObject *self, PyObject *args)
&_parse_voidp, &func,
&PyTuple_Type, &arguments))
return NULL;
if (PySys_Audit("ctypes.call_function", "nO",
(Py_ssize_t)func, arguments) < 0) {
return NULL;
}

result = _ctypes_callproc((PPROC)func,
arguments,
Expand Down Expand Up @@ -1485,6 +1510,10 @@ call_cdeclfunction(PyObject *self, PyObject *args)
&_parse_voidp, &func,
&PyTuple_Type, &arguments))
return NULL;
if (PySys_Audit("ctypes.call_function", "nO",
(Py_ssize_t)func, arguments) < 0) {
return NULL;
}

result = _ctypes_callproc((PPROC)func,
arguments,
Expand Down Expand Up @@ -1597,11 +1626,15 @@ static const char addressof_doc[] =
static PyObject *
addressof(PyObject *self, PyObject *obj)
{
if (CDataObject_Check(obj))
return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr);
PyErr_SetString(PyExc_TypeError,
"invalid type");
return NULL;
if (!CDataObject_Check(obj)) {
PyErr_SetString(PyExc_TypeError,
"invalid type");
return NULL;
}
if (PySys_Audit("ctypes.addressof", "O", obj) < 0) {
return NULL;
}
return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr);
}

static int
Expand All @@ -1615,8 +1648,12 @@ static PyObject *
My_PyObj_FromPtr(PyObject *self, PyObject *args)
{
PyObject *ob;
if (!PyArg_ParseTuple(args, "O&:PyObj_FromPtr", converter, &ob))
if (!PyArg_ParseTuple(args, "O&:PyObj_FromPtr", converter, &ob)) {
return NULL;
}
if (PySys_Audit("ctypes.PyObj_FromPtr", "O", ob) < 0) {
return NULL;
}
Py_INCREF(ob);
return ob;
}
Expand Down