Skip to content

Commit 47db743

Browse files
bpo-38622: Add missing audit events for ctypes module (GH-17158)
(cherry picked from commit 00923c6) Co-authored-by: Steve Dower <[email protected]>
1 parent cbbf109 commit 47db743

File tree

5 files changed

+106
-7
lines changed

5 files changed

+106
-7
lines changed

Doc/library/ctypes.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,12 @@ object is available:
15261526
``ctypes.dlsym`` with arguments ``library`` (the library object) and ``name``
15271527
(the symbol's name as a string or integer).
15281528

1529+
.. audit-event:: ctypes.dlsym/handle handle,name ctypes.LibraryLoader
1530+
1531+
In cases when only the library handle is available rather than the object,
1532+
accessing a function raises an auditing event ``ctypes.dlsym/handle`` with
1533+
arguments ``handle`` (the raw library handle) and ``name``.
1534+
15291535
.. _ctypes-foreign-functions:
15301536

15311537
Foreign functions
@@ -1611,6 +1617,19 @@ They are instances of a private class:
16111617
passed arguments.
16121618

16131619

1620+
.. audit-event:: ctypes.seh_exception code foreign-functions
1621+
1622+
On Windows, when a foreign function call raises a system exception (for
1623+
example, due to an access violation), it will be captured and replaced with
1624+
a suitable Python exception. Further, an auditing event
1625+
``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an
1626+
audit hook to replace the exception with its own.
1627+
1628+
.. audit-event:: ctypes.call_function func_pointer,arguments ctype-foreign-functions
1629+
1630+
Some ways to invoke foreign function calls may raise an auditing event
1631+
``ctypes.call_function`` with arguments ``function pointer`` and ``arguments``.
1632+
16141633
.. _ctypes-function-prototypes:
16151634

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

1824+
.. audit-event:: ctypes.addressof obj ctypes.addressof
1825+
18051826

18061827
.. function:: alignment(obj_or_type)
18071828

@@ -1844,6 +1865,7 @@ Utility functions
18441865
termination character. An integer can be passed as second argument which allows
18451866
specifying the size of the array if the length of the bytes should not be used.
18461867

1868+
.. audit-event:: ctypes.create_string_buffer init,size ctypes.create_string_buffer
18471869

18481870

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

1885+
.. audit-event:: ctypes.create_unicode_buffer init,size ctypes.create_unicode_buffer
18631886

18641887

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

1943+
.. audit-event:: ctypes.get_errno "" ctypes.get_errno
1944+
19201945
.. function:: get_last_error()
19211946

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

1950+
.. audit-event:: ctypes.get_last_error "" ctypes.get_last_error
1951+
19251952
.. function:: memmove(dst, src, count)
19261953

19271954
Same as the standard C memmove library function: copies *count* bytes from
@@ -1965,6 +1992,7 @@ Utility functions
19651992
Set the current value of the ctypes-private copy of the system :data:`errno`
19661993
variable in the calling thread to *value* and return the previous value.
19671994

1995+
.. audit-event:: ctypes.set_errno errno ctypes.set_errno
19681996

19691997

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

2004+
.. audit-event:: ctypes.set_last_error error ctypes.set_last_error
19762005

19772006

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

2019+
.. audit-event:: ctypes.string_at address,size ctypes.string_at
2020+
19902021

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

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

2041+
.. audit-event:: ctypes.wstring_at address,size ctypes.wstring_at
2042+
20102043

20112044
.. _ctypes-data-types:
20122045

@@ -2034,6 +2067,7 @@ Data types
20342067
source buffer in bytes; the default is zero. If the source buffer is not
20352068
large enough a :exc:`ValueError` is raised.
20362069

2070+
.. audit-event:: ctypes.cdata/buffer pointer,size,offset ctypes._CData.from_buffer
20372071

20382072
.. method:: _CData.from_buffer_copy(source[, offset])
20392073

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

2080+
.. audit-event:: ctypes.cdata/buffer pointer,size,offset ctypes._CData.from_buffer_copy
2081+
20462082
.. method:: from_address(address)
20472083

20482084
This method returns a ctypes type instance using the memory specified by

Lib/ctypes/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ def create_string_buffer(init, size=None):
5252
if isinstance(init, bytes):
5353
if size is None:
5454
size = len(init)+1
55+
_sys.audit("ctypes.create_string_buffer", init, size)
5556
buftype = c_char * size
5657
buf = buftype()
5758
buf.value = init
5859
return buf
5960
elif isinstance(init, int):
61+
_sys.audit("ctypes.create_string_buffer", None, init)
6062
buftype = c_char * init
6163
buf = buftype()
6264
return buf
@@ -283,11 +285,13 @@ def create_unicode_buffer(init, size=None):
283285
# 32-bit wchar_t (1 wchar_t per Unicode character). +1 for
284286
# trailing NUL character.
285287
size = len(init) + 1
288+
_sys.audit("ctypes.create_unicode_buffer", init, size)
286289
buftype = c_wchar * size
287290
buf = buftype()
288291
buf.value = init
289292
return buf
290293
elif isinstance(init, int):
294+
_sys.audit("ctypes.create_unicode_buffer", None, init)
291295
buftype = c_wchar * init
292296
buf = buftype()
293297
return buf
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add additional audit events for the :mod:`ctypes` module.

Modules/_ctypes/_ctypes.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,12 @@ CDataType_from_buffer(PyObject *type, PyObject *args)
641641
return NULL;
642642
}
643643

644+
if (PySys_Audit("ctypes.cdata/buffer", "nnn",
645+
(Py_ssize_t)buffer->buf, buffer->len, offset) < 0) {
646+
Py_DECREF(mv);
647+
return NULL;
648+
}
649+
644650
result = PyCData_AtAddress(type, (char *)buffer->buf + offset);
645651
if (result == NULL) {
646652
Py_DECREF(mv);
@@ -691,6 +697,12 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args)
691697
return NULL;
692698
}
693699

700+
if (PySys_Audit("ctypes.cdata/buffer", "nnn",
701+
(Py_ssize_t)buffer.buf, buffer.len, offset) < 0) {
702+
PyBuffer_Release(&buffer);
703+
return NULL;
704+
}
705+
694706
result = GenericPyCData_new((PyTypeObject *)type, NULL, NULL);
695707
if (result != NULL) {
696708
memcpy(((CDataObject *)result)->b_ptr,
@@ -714,6 +726,9 @@ CDataType_in_dll(PyObject *type, PyObject *args)
714726

715727
if (!PyArg_ParseTuple(args, "Os:in_dll", &dll, &name))
716728
return NULL;
729+
if (PySys_Audit("ctypes.dlsym", "O", args) < 0) {
730+
return NULL;
731+
}
717732

718733
obj = PyObject_GetAttrString(dll, "_handle");
719734
if (!obj)
@@ -5534,6 +5549,9 @@ create_comerror(void)
55345549
static PyObject *
55355550
string_at(const char *ptr, int size)
55365551
{
5552+
if (PySys_Audit("ctypes.string_at", "ni", (Py_ssize_t)ptr, size) < 0) {
5553+
return NULL;
5554+
}
55375555
if (size == -1)
55385556
return PyBytes_FromStringAndSize(ptr, strlen(ptr));
55395557
return PyBytes_FromStringAndSize(ptr, size);
@@ -5625,6 +5643,9 @@ static PyObject *
56255643
wstring_at(const wchar_t *ptr, int size)
56265644
{
56275645
Py_ssize_t ssize = size;
5646+
if (PySys_Audit("ctypes.wstring_at", "nn", (Py_ssize_t)ptr, ssize) < 0) {
5647+
return NULL;
5648+
}
56285649
if (ssize == -1)
56295650
ssize = wcslen(ptr);
56305651
return PyUnicode_FromWideChar(ptr, ssize);

Modules/_ctypes/callproc.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,9 @@ set_error_internal(PyObject *self, PyObject *args, int index)
199199
PyObject *errobj;
200200
int *space;
201201

202-
if (!PyArg_ParseTuple(args, "i", &new_errno))
202+
if (!PyArg_ParseTuple(args, "i", &new_errno)) {
203203
return NULL;
204+
}
204205
errobj = _ctypes_get_errobj(&space);
205206
if (errobj == NULL)
206207
return NULL;
@@ -213,12 +214,18 @@ set_error_internal(PyObject *self, PyObject *args, int index)
213214
static PyObject *
214215
get_errno(PyObject *self, PyObject *args)
215216
{
217+
if (PySys_Audit("ctypes.get_errno", NULL) < 0) {
218+
return NULL;
219+
}
216220
return get_error_internal(self, args, 0);
217221
}
218222

219223
static PyObject *
220224
set_errno(PyObject *self, PyObject *args)
221225
{
226+
if (PySys_Audit("ctypes.set_errno", "O", args) < 0) {
227+
return NULL;
228+
}
222229
return set_error_internal(self, args, 0);
223230
}
224231

@@ -227,12 +234,18 @@ set_errno(PyObject *self, PyObject *args)
227234
static PyObject *
228235
get_last_error(PyObject *self, PyObject *args)
229236
{
237+
if (PySys_Audit("ctypes.get_last_error", NULL) < 0) {
238+
return NULL;
239+
}
230240
return get_error_internal(self, args, 1);
231241
}
232242

233243
static PyObject *
234244
set_last_error(PyObject *self, PyObject *args)
235245
{
246+
if (PySys_Audit("ctypes.set_last_error", "O", args) < 0) {
247+
return NULL;
248+
}
236249
return set_error_internal(self, args, 1);
237250
}
238251

@@ -262,6 +275,11 @@ static WCHAR *FormatError(DWORD code)
262275
#ifndef DONT_USE_SEH
263276
static void SetException(DWORD code, EXCEPTION_RECORD *pr)
264277
{
278+
if (PySys_Audit("ctypes.seh_exception", "I", code) < 0) {
279+
/* An exception was set by the audit hook */
280+
return;
281+
}
282+
265283
/* The 'code' is a normal win32 error code so it could be handled by
266284
PyErr_SetFromWindowsErr(). However, for some errors, we have additional
267285
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)
14271445
if (!PyArg_ParseTuple(args, "O&s:dlsym",
14281446
&_parse_voidp, &handle, &name))
14291447
return NULL;
1448+
if (PySys_Audit("ctypes.dlsym/handle", "O", args) < 0) {
1449+
return NULL;
1450+
}
14301451
ptr = ctypes_dlsym((void*)handle, name);
14311452
if (!ptr) {
14321453
PyErr_SetString(PyExc_OSError,
@@ -1454,6 +1475,10 @@ call_function(PyObject *self, PyObject *args)
14541475
&_parse_voidp, &func,
14551476
&PyTuple_Type, &arguments))
14561477
return NULL;
1478+
if (PySys_Audit("ctypes.call_function", "nO",
1479+
(Py_ssize_t)func, arguments) < 0) {
1480+
return NULL;
1481+
}
14571482

14581483
result = _ctypes_callproc((PPROC)func,
14591484
arguments,
@@ -1485,6 +1510,10 @@ call_cdeclfunction(PyObject *self, PyObject *args)
14851510
&_parse_voidp, &func,
14861511
&PyTuple_Type, &arguments))
14871512
return NULL;
1513+
if (PySys_Audit("ctypes.call_function", "nO",
1514+
(Py_ssize_t)func, arguments) < 0) {
1515+
return NULL;
1516+
}
14881517

14891518
result = _ctypes_callproc((PPROC)func,
14901519
arguments,
@@ -1597,11 +1626,15 @@ static const char addressof_doc[] =
15971626
static PyObject *
15981627
addressof(PyObject *self, PyObject *obj)
15991628
{
1600-
if (CDataObject_Check(obj))
1601-
return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr);
1602-
PyErr_SetString(PyExc_TypeError,
1603-
"invalid type");
1604-
return NULL;
1629+
if (!CDataObject_Check(obj)) {
1630+
PyErr_SetString(PyExc_TypeError,
1631+
"invalid type");
1632+
return NULL;
1633+
}
1634+
if (PySys_Audit("ctypes.addressof", "O", obj) < 0) {
1635+
return NULL;
1636+
}
1637+
return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr);
16051638
}
16061639

16071640
static int
@@ -1615,8 +1648,12 @@ static PyObject *
16151648
My_PyObj_FromPtr(PyObject *self, PyObject *args)
16161649
{
16171650
PyObject *ob;
1618-
if (!PyArg_ParseTuple(args, "O&:PyObj_FromPtr", converter, &ob))
1651+
if (!PyArg_ParseTuple(args, "O&:PyObj_FromPtr", converter, &ob)) {
16191652
return NULL;
1653+
}
1654+
if (PySys_Audit("ctypes.PyObj_FromPtr", "O", ob) < 0) {
1655+
return NULL;
1656+
}
16201657
Py_INCREF(ob);
16211658
return ob;
16221659
}

0 commit comments

Comments
 (0)