Skip to content
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

Stackless issue #303: PEP-578 Audit Hooks for Stackless #304

Open
wants to merge 2 commits into
base: 3.8-slp
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions Doc/c-api/stackless.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ Tasklets

Returns the current frame that *task* is executing in, or *NULL*

.. audit-event:: sys._getframe "" c.PyTasklet_GetFrame

.. c:function:: int PyTasklet_IsMain(PyTaskletObject *task)

Returns ``1`` if *task* is the main tasklet, otherwise ``0``.
Expand Down
22 changes: 22 additions & 0 deletions Doc/library/stackless/pickling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ types:
C-types PyAsyncGenASend and PyAsyncGenAThrow (see :pep:`525`) as well as
all kinds of :ref:`Dictionary view objects <dict-views>`.

Reduction functions raise appropriate auditing hooks.

.. audit-event:: sys._getframe ""

Reducing objects of type :data:`~types.AsyncGeneratorType`, :data:`~types.CoroutineType` or
:data:`~types.GeneratorType` raises an auditing event ``sys._getframe`` with no arguments.

Code
====

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

.. audit-event:: stackless.frame.__setstate__ frame

On unpickling frames |SLP| raises an auditing event ``stackless.frame.__setstate__`` with the fully initialized
frame object as the argument, if the frame could be evaluated.

.. audit-event:: sys.settrace ""

On unpickling frames |SLP| raises an auditing event ``sys.settrace`` with no arguments
if the frame has a trace function that could be executed.


Functions
=========
Expand Down Expand Up @@ -222,3 +239,8 @@ If :const:`~stackless.PICKLEFLAGS_PRESERVE_AG_FINALIZER` has been set and if
``ag_finalizer`` by value.
Otherwise, if :const:`~stackless.PICKLEFLAGS_RESET_AG_FINALIZER` has
been set, |SLP| unpickles ``ag_finalizer`` as uninitialised.

.. audit-event:: stackless.async_generator.set_finalizer ""

If ``ag_finalizer`` was unpickled by value, this function raises a auditing event
``stackless.async_generator.set_finalizer`` with no arguments.
27 changes: 20 additions & 7 deletions Doc/library/stackless/tasklets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,8 @@ The ``tasklet`` class

See :meth:`object.__reduce_ex__`.

.. audit-event:: sys._getframe "" tasklet.__reduce_ex__

.. method:: tasklet.__setstate__(state)

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

.. versionchanged:: 3.8

If the state contains a trace- or profile-function :meth:`~__setstate__` now
raises an auditing event ``sys.settrace`` resp. ``sys.setprofile`` with
no arguments.

:param state: the state as given by ``__reduce_ex__(...)[2]``
:type state: :class:`tuple`
:return: self
:rtype: :class:`tasklet`
:raises RuntimeError: if the tasklet is already alive

If the state contains a trace- or profile-function :meth:`~__setstate__` raises
auditing events.

.. audit-event:: sys.settrace "" tasklet.__setstate__

.. audit-event:: sys.setprofile "" tasklet.__setstate__


The following (read-only) attributes allow tasklet state to be checked:

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

.. versionchanged:: 3.8
.. audit-event:: sys.settrace "" tasklet.trace_function

.. audit-event:: sys.setprofile "" tasklet.profile_function

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

.. attribute:: tasklet.frame

The current frame of the tasklet or :data:`None`.

.. audit-event:: sys._getframe "" tasklet.frame



^^^^^^^^^^^^^^^^^^
Tasklet Life Cycle
Expand Down
8 changes: 5 additions & 3 deletions Python/_warnings.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "Python.h"
#include "pycore_pystate.h"
#include "frameobject.h"
#include "stackless_api.h"
#include "pycore_stackless.h"
#include "clinic/_warnings.c.h"

#define MODULE_NAME "_warnings"
Expand Down Expand Up @@ -835,9 +835,11 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
PyFrameObject *f = NULL;
PyObject *current = PyStackless_GetCurrent();
if (current != NULL) {
f = (PyFrameObject *)PyTasklet_GetFrame((PyTaskletObject*)current);
f = slp_get_frame((PyTaskletObject*)current); /* returns a borrowed reference */
while (f != NULL && !PyFrame_Check(f)) {
f = f->f_back;
}
Py_DECREF(current);
Py_XDECREF(f); /* turn it into a borrowed reference */
}
/* fallback to the state frame */
if (f == NULL)
Expand Down
13 changes: 13 additions & 0 deletions Stackless/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ What's New in Stackless 3.X.X?

*Release date: 20XX-XX-XX*

- https:/stackless-dev/stackless/issues/303
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__" 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".


What's New in Stackless 3.8.0 and 3.8.1?
========================================
Expand Down
9 changes: 7 additions & 2 deletions Stackless/module/taskletobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,10 @@ tasklet_reduce(PyTaskletObject * t, PyObject *value)
f = t->f.frame;
while (f != NULL) {
int ret;
PyObject * frame_reducer = slp_reduce_frame(f);
PyObject * frame_reducer;
if (PySys_Audit("sys._getframe", NULL))
goto err_exit;
frame_reducer = slp_reduce_frame(f);
if (frame_reducer == NULL)
goto err_exit;
ret = PyList_Append(lis, frame_reducer);
Expand Down Expand Up @@ -1912,8 +1915,10 @@ tasklet_get_frame(PyTaskletObject *task, void *closure)
PyObject *
PyTasklet_GetFrame(PyTaskletObject *task)
{
PyFrameObject *f = (PyFrameObject *) slp_get_frame(task);
if (PySys_Audit("sys._getframe", NULL))
return NULL;

PyFrameObject *f = slp_get_frame(task);
while (f != NULL && !PyFrame_Check(f)) {
f = f->f_back;
}
Expand Down
15 changes: 14 additions & 1 deletion Stackless/pickling/prickelpit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1212,8 +1212,15 @@ frame_setstate(PyFrameObject *f, PyObject *args)

/* See if this frame is valid to be run. */
f->f_executing = valid ? f_executing : SLP_FRAME_EXECUTING_INVALID;

Py_TYPE(f) = &PyFrame_Type;
if(valid && f_executing) {
if (PySys_Audit("stackless.frame.__setstate__", "O", f))
goto err_exit;
if (f->f_trace && PySys_Audit("sys.settrace", NULL)) {
goto err_exit;
}
}

Py_INCREF(f);
return (PyObject *) f;
err_exit:
Expand Down Expand Up @@ -1684,6 +1691,8 @@ reduce_to_gen_obj_head(gen_obj_head_ty *goh, PyFrameObject * frame, const _PyErr
/* Pickle NULL as None. See gen_setstate() for the corresponding
* unpickling code. */
if (frame != NULL) {
if (PySys_Audit("sys._getframe", NULL))
return -1;
goh->frame = slp_reduce_frame(frame);
if (goh->frame == NULL)
return -1;
Expand Down Expand Up @@ -2186,6 +2195,10 @@ async_gen_setstate(PyObject *self, PyObject *args)
Py_CLEAR(async_gen->ag_finalizer);
}
else {
if(PySys_Audit("stackless.async_generator.set_finalizer", NULL)) {
async_gen = NULL;
goto error;
}
async_gen->ag_hooks_inited = hooks_inited;
Py_INCREF(finalizer);
Py_XSETREF(async_gen->ag_finalizer, finalizer);
Expand Down