This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Early auditing broken
Type: security Stage:
Components: Interpreter Core Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: christian.heimes, steve.dower, vstinner
Priority: normal Keywords:

Created on 2019-07-05 12:07 by christian.heimes, last changed 2022-04-11 14:59 by admin.

Messages (9)
msg347334 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2019-07-05 12:07
I think that commit 838f26402de82640698c38ea9d2be65c6cf780d6 / bpo-36710 broke auditing for early events. I'm no longer seeing early events like cpython.PyInterpreterState_New. The first event is an import event without interpreter state.
msg347344 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-07-05 13:40
Oh. How can I reproduce this issue?
msg347345 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2019-07-05 13:48
3.8.0b1 is also broken, so it may have been a different commit. I'm sure that I was able to see interpreter initialization with dtrace hooks.

# audit.stp
probe process("/usr/lib64/libpython3.8.*").provider("python").mark("audit") {
    printf("%s\n", user_string($arg1))
}

$ sudo stap audit.stp -c python3.8
msg347353 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-07-05 14:23
I don't see how the first call to PyInterpreterState_New() can be audited: PySys_Audit() builds a tuple to call hooks, but there is no interpreter yet, so we cannot even build tuples.

Are you talking about the "main" interpreter, or sub-interpreters?

I'm surprised that it worked previously for the main interpreter.
msg347357 - (view) Author: Steve Dower (steve.dower) * (Python committer) Date: 2019-07-05 15:24
Yeah, at some point we had to add an initialization check to the audit calls because of the tuple, so it essentially became a subinterpreter event but not the main one.

But I thought the dtrace call happened before that check? Looking through the linked commit, apparently not, but I don't actually recall right now why and I can't quite see enough context. Did we need the tuple there too?
msg347358 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-07-05 15:39
PySys_Audit() exit immediately if ts=NULL:

    /* Early exit when no hooks are registered */
    if (!should_audit(ts)) {
        return 0;
    }

It exits before calling:

    /* Dtrace USDT point */
    if (dtrace) {
        PyDTrace_AUDIT(event, (void *)eventArgs);
    }

where eventArgs is the tuple.

Do you really care of getting an audit event when the *main* interpreter is created? It doesn't sound like an attack vector to start Python, no? If you need an event "Python started", we can add one later, when the PySys_Audit() is usable.
msg347360 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2019-07-05 16:01
The hooks are about auditing the behavior of an interpreter. It's not strictly tight to some attack scenario. I would find it useful to either get back the old behavior or to have some sort of event, which indicates the start of the auditing log.
msg347536 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2019-07-09 10:07
Some audit events are designed to work before the interpreter is fully initialized or already shut down. These events pass in NULL instead of a PyObject*.

Python/pystate.c:    if (PySys_Audit("cpython.PyInterpreterState_New", NULL) < 0) {
Python/pystate.c:    if (PySys_Audit("cpython.PyInterpreterState_Clear", NULL) < 0) {
Python/sysmodule.c:    PySys_Audit("cpython._PySys_ClearAuditHooks", NULL);

(They are also currently not documented)
msg347538 - (view) Author: Steve Dower (steve.dower) * (Python committer) Date: 2019-07-09 10:13
Passing a NULL format string there means the same as passing NULL to PyObject_CallFunction(func, NULL) - no arguments, which results in an empty tuple being passed to the hooks.

Perhaps in the early cases we can pass NULL instead of a tuple? Maybe even assert when that case occurs and the event has parameters?
History
Date User Action Args
2022-04-11 14:59:17adminsetgithub: 81686
2021-10-20 16:18:20christian.heimessetversions: + Python 3.10, Python 3.11, - Python 3.8
2019-07-09 10:13:59steve.dowersetmessages: + msg347538
2019-07-09 10:07:14christian.heimessetmessages: + msg347536
2019-07-05 16:01:08christian.heimessetmessages: + msg347360
2019-07-05 15:39:22vstinnersetmessages: + msg347358
2019-07-05 15:24:34steve.dowersetmessages: + msg347357
2019-07-05 14:23:47vstinnersetmessages: + msg347353
2019-07-05 13:48:05christian.heimessetmessages: + msg347345
2019-07-05 13:40:50vstinnersetmessages: + msg347344
2019-07-05 12:07:59christian.heimescreate