Skip to content

Commit b1776d2

Browse files
author
Anselm Kruis
committed
Stackless issue python#303: PEP-578 Audit Hooks for Stackless
Stackless now raises more auditing events. - Reducing objects of type AsyncGeneratorType, CoroutineType or GeneratorType, reading the attribute 'tasklet.frame' or calling 'PyTasklet_GetFrame()' raises an auditing event "sys._getframe" with no arguments. - Unpickling of a frame that could be evaluated now raises auditing event "stackless.frame.__setstate__" with no arguments and, if the frame has a trace function, also auditing event "sys.settrace". - If Stackless pickle flag bit 'PICKLEFLAGS_PRESERVE_AG_FINALIZER' is set then unpickling an async_generator with a pickled finalizer raises auditing event "stackless.async_generator.set_finalizer".
1 parent 61156ad commit b1776d2

File tree

7 files changed

+82
-12
lines changed

7 files changed

+82
-12
lines changed

Doc/c-api/stackless.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ Tasklets
187187
188188
Returns the current frame that *task* is executing in, or *NULL*
189189
190+
.. audit-event:: sys._getframe "" c.PyTasklet_GetFrame
191+
190192
.. c:function:: int PyTasklet_IsMain(PyTaskletObject *task)
191193
192194
Returns ``1`` if *task* is the main tasklet, otherwise ``0``.

Doc/library/stackless/pickling.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ types:
147147
C-types PyAsyncGenASend and PyAsyncGenAThrow (see :pep:`525`) as well as
148148
all kinds of :ref:`Dictionary view objects <dict-views>`.
149149

150+
Reduction functions raise appropriate auditing hooks.
151+
152+
.. audit-event:: sys._getframe ""
153+
154+
Reducing objects of type :data:`~types.AsyncGeneratorType`, :data:`~types.CoroutineType` or
155+
:data:`~types.GeneratorType` raises an auditing event ``sys._getframe`` with no arguments.
156+
150157
Code
151158
====
152159

@@ -177,6 +184,16 @@ to execute it raises
177184
If a program tries to unpickle a frame using a code object whose first bytecode instruction is invalid, then |SLP|
178185
marks the frame as invalid. Any attempt to execute the frame raises :exc:`RuntimeError`.
179186

187+
.. audit-event:: stackless.frame.__setstate__ ""
188+
189+
|SLP| raises a auditing event ``stackless.frame.__setstate__`` with no arguments
190+
on unpickling frames that could be evaluated.
191+
192+
.. audit-event:: sys.settrace ""
193+
194+
|SLP| raises a auditing event ``sys.settrace`` with no arguments on unpickling frames
195+
with a trace function that could be executed.
196+
180197

181198
Functions
182199
=========
@@ -222,3 +239,8 @@ If :const:`~stackless.PICKLEFLAGS_PRESERVE_AG_FINALIZER` has been set and if
222239
``ag_finalizer`` by value.
223240
Otherwise, if :const:`~stackless.PICKLEFLAGS_RESET_AG_FINALIZER` has
224241
been set, |SLP| unpickles ``ag_finalizer`` as uninitialised.
242+
243+
.. audit-event:: stackless.async_generator.set_finalizer ""
244+
245+
If ``ag_finalizer`` was unpickled by value, this function raises a auditing event
246+
``stackless.async_generator.set_finalizer`` with no arguments.

Doc/library/stackless/tasklets.rst

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ The ``tasklet`` class
433433

434434
See :meth:`object.__reduce_ex__`.
435435

436+
.. audit-event:: sys._getframe "" tasklet.__reduce_ex__
437+
436438
.. method:: tasklet.__setstate__(state)
437439

438440
See :meth:`object.__setstate__`.
@@ -444,18 +446,20 @@ The ``tasklet`` class
444446
the :class:`~contextvars.Context` object of the
445447
tasklet to the :class:`~contextvars.Context` object of the current tasklet.
446448

447-
.. versionchanged:: 3.8
448-
449-
If the state contains a trace- or profile-function :meth:`~__setstate__` now
450-
raises an auditing event ``sys.settrace`` resp. ``sys.setprofile`` with
451-
no arguments.
452-
453449
:param state: the state as given by ``__reduce_ex__(...)[2]``
454450
:type state: :class:`tuple`
455451
:return: self
456452
:rtype: :class:`tasklet`
457453
:raises RuntimeError: if the tasklet is already alive
458454

455+
If the state contains a trace- or profile-function :meth:`~__setstate__` raises
456+
auditing events.
457+
458+
.. audit-event:: sys.settrace "" tasklet.__setstate__
459+
460+
.. audit-event:: sys.setprofile "" tasklet.__setstate__
461+
462+
459463
The following (read-only) attributes allow tasklet state to be checked:
460464

461465
.. attribute:: tasklet.alive
@@ -578,11 +582,20 @@ and thus may not be available in all |SLP| implementations.
578582
are the tasklet counterparts of the functions :func:`sys.settrace`,
579583
:func:`sys.gettrace`, :func:`sys.setprofile` and :func:`sys.getprofile`.
580584

581-
.. versionchanged:: 3.8
585+
.. audit-event:: sys.settrace "" tasklet.trace_function
586+
587+
.. audit-event:: sys.setprofile "" tasklet.profile_function
582588

583589
Assignments to these attributes now raise an auditing event
584590
``sys.settrace`` resp. ``sys.setprofile`` with no arguments.
585591

592+
.. attribute:: tasklet.frame
593+
594+
The current frame of the tasklet or :data:`None`.
595+
596+
.. audit-event:: sys._getframe "" tasklet.frame
597+
598+
586599

587600
^^^^^^^^^^^^^^^^^^
588601
Tasklet Life Cycle

Python/_warnings.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include "Python.h"
22
#include "pycore_pystate.h"
33
#include "frameobject.h"
4-
#include "stackless_api.h"
4+
#include "pycore_stackless.h"
55
#include "clinic/_warnings.c.h"
66

77
#define MODULE_NAME "_warnings"
@@ -835,9 +835,11 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
835835
PyFrameObject *f = NULL;
836836
PyObject *current = PyStackless_GetCurrent();
837837
if (current != NULL) {
838-
f = (PyFrameObject *)PyTasklet_GetFrame((PyTaskletObject*)current);
838+
f = slp_get_frame((PyTaskletObject*)current); /* returns a borrowed reference */
839+
while (f != NULL && !PyFrame_Check(f)) {
840+
f = f->f_back;
841+
}
839842
Py_DECREF(current);
840-
Py_XDECREF(f); /* turn it into a borrowed reference */
841843
}
842844
/* fallback to the state frame */
843845
if (f == NULL)

Stackless/changelog.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ What's New in Stackless 3.X.X?
99

1010
*Release date: 20XX-XX-XX*
1111

12+
- https://github.com/stackless-dev/stackless/issues/303
13+
Stackless now raises more auditing events:
14+
- Reducing objects of type AsyncGeneratorType, CoroutineType or
15+
GeneratorType, reading the attribute 'tasklet.frame' or calling
16+
'PyTasklet_GetFrame()' raises an auditing event "sys._getframe" with no
17+
arguments.
18+
- Unpickling of a frame that could be evaluated now raises auditing event
19+
"stackless.frame.__setstate__" with no arguments and, if the frame has a
20+
trace function, also auditing event "sys.settrace".
21+
- If Stackless pickle flag bit 'PICKLEFLAGS_PRESERVE_AG_FINALIZER' is set
22+
then unpickling an async_generator with a pickled finalizer raises auditing
23+
event "stackless.async_generator.set_finalizer".
24+
1225

1326
What's New in Stackless 3.8.0 and 3.8.1?
1427
========================================

Stackless/module/taskletobject.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,10 @@ tasklet_reduce(PyTaskletObject * t, PyObject *value)
655655
f = t->f.frame;
656656
while (f != NULL) {
657657
int ret;
658-
PyObject * frame_reducer = slp_reduce_frame(f);
658+
PyObject * frame_reducer;
659+
if (PySys_Audit("sys._getframe", NULL))
660+
goto err_exit;
661+
frame_reducer = slp_reduce_frame(f);
659662
if (frame_reducer == NULL)
660663
goto err_exit;
661664
ret = PyList_Append(lis, frame_reducer);
@@ -1912,8 +1915,10 @@ tasklet_get_frame(PyTaskletObject *task, void *closure)
19121915
PyObject *
19131916
PyTasklet_GetFrame(PyTaskletObject *task)
19141917
{
1915-
PyFrameObject *f = (PyFrameObject *) slp_get_frame(task);
1918+
if (PySys_Audit("sys._getframe", NULL))
1919+
return NULL;
19161920

1921+
PyFrameObject *f = slp_get_frame(task);
19171922
while (f != NULL && !PyFrame_Check(f)) {
19181923
f = f->f_back;
19191924
}

Stackless/pickling/prickelpit.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,13 @@ frame_setstate(PyFrameObject *f, PyObject *args)
12121212

12131213
/* See if this frame is valid to be run. */
12141214
f->f_executing = valid ? f_executing : SLP_FRAME_EXECUTING_INVALID;
1215+
if(valid && f_executing) {
1216+
if (PySys_Audit("stackless.frame.__setstate__", NULL))
1217+
goto err_exit;
1218+
if (f->f_trace && PySys_Audit("sys.settrace", NULL)) {
1219+
goto err_exit;
1220+
}
1221+
}
12151222

12161223
Py_TYPE(f) = &PyFrame_Type;
12171224
Py_INCREF(f);
@@ -1684,6 +1691,8 @@ reduce_to_gen_obj_head(gen_obj_head_ty *goh, PyFrameObject * frame, const _PyErr
16841691
/* Pickle NULL as None. See gen_setstate() for the corresponding
16851692
* unpickling code. */
16861693
if (frame != NULL) {
1694+
if (PySys_Audit("sys._getframe", NULL))
1695+
return -1;
16871696
goh->frame = slp_reduce_frame(frame);
16881697
if (goh->frame == NULL)
16891698
return -1;
@@ -2186,6 +2195,10 @@ async_gen_setstate(PyObject *self, PyObject *args)
21862195
Py_CLEAR(async_gen->ag_finalizer);
21872196
}
21882197
else {
2198+
if(PySys_Audit("stackless.async_generator.set_finalizer", NULL)) {
2199+
async_gen = NULL;
2200+
goto error;
2201+
}
21892202
async_gen->ag_hooks_inited = hooks_inited;
21902203
Py_INCREF(finalizer);
21912204
Py_XSETREF(async_gen->ag_finalizer, finalizer);

0 commit comments

Comments
 (0)