PEP 551 and PEP 7 (#392)

This commit is contained in:
Steve Dower 2017-09-06 11:27:23 -07:00 committed by GitHub
parent ebbb636b7b
commit f41260ee2d
1 changed files with 96 additions and 47 deletions

View File

@ -269,7 +269,10 @@ The new public C API for the verified open hook is::
# Set the handler
typedef PyObject *(*handler_func)(const char *narrow,
const wchar_t *wide)
int Py_SetOpenForExecuteHandler(handler_func handler)
int PyOS_SetOpenForExecuteHandler(handler_func handler)
# Open a file using the handler
PyObject *PyOS_OpenForExec(PyObject *path)
The new public Python API for the verified open hook is::
@ -356,101 +359,144 @@ should be considered part of the rationale for including the hook.
:widths: 2, 2, 3, 6
``PySys_AddAuditHook``, ``sys.addaudithook``, "", "Detect when new
audit hooks are being added."
audit hooks are being added.
"
``_PySys_ClearAuditHooks``, ``sys._clearaudithooks``, "", "Notifies
hooks they are being cleaned up, mainly in case the event is
triggered unexpectedly. This event cannot be aborted."
triggered unexpectedly. This event cannot be aborted.
"
``Py_SetOpenForExecuteHandler``, ``setopenforexecutehandler``, "", "
Detects any attempt to set the ``open_for_execute`` handler."
Detects any attempt to set the ``open_for_execute`` handler.
"
"``compile``, ``exec``, ``eval``, ``PyAst_CompileString``,
``PyAST_obj2mod``", ``compile``, "``(code, filename_or_none)``", "
Detect dynamic code compilation, where ``code`` could be a string or
AST. Note that this will be called for regular imports of source
code, including those that were opened with ``open_for_exec``."
code, including those that were opened with ``open_for_exec``.
"
"``exec``, ``eval``, ``run_mod``", ``exec``, "``(code_object,)``", "
Detect dynamic execution of code objects. This only occurs for
explicit calls, and is not raised for normal function invocation."
explicit calls, and is not raised for normal function invocation.
"
``import``, ``import``, "``(module, filename, sys.path,
sys.meta_path, sys.path_hooks)``", "Detect when modules are
imported. This is raised before the module name is resolved to a
file. All arguments other than the module name may be ``None`` if
they are not used or available."
they are not used or available.
"
``code_new``, ``code.__new__``, "``(bytecode, filename, name)``", "
Detect dynamic creation of code objects. This only occurs for
direct instantiation, and is not raised for normal compilation."
direct instantiation, and is not raised for normal compilation.
"
``func_new_impl``, ``function.__new__``, "``(code,)``", "Detect
dynamic creation of function objects. This only occurs for direct
instantiation, and is not raised for normal compilation.
"
"``_ctypes.dlopen``, ``_ctypes.LoadLibrary``", ``ctypes.dlopen``, "
``(module_or_path,)``", "Detect when native modules are used."
``(module_or_path,)``", "Detect when native modules are used.
"
``_ctypes._FuncPtr``, ``ctypes.dlsym``, "``(lib_object, name)``", "
Collect information about specific symbols retrieved from native
modules."
modules.
"
``_ctypes._CData``, ``ctypes.cdata``, "``(ptr_as_int,)``", "Detect
when code is accessing arbitrary memory using ``ctypes``"
when code is accessing arbitrary memory using ``ctypes``.
"
``id``, ``id``, "``(id_as_int,)``", "Detect when code is accessing
the id of objects, which in CPython reveals information about
memory layout."
memory layout.
"
``sys._getframe``, ``sys._getframe``, "``(frame_object,)``", "Detect
when code is accessing frames directly"
when code is accessing frames directly.
"
``sys._current_frames``, ``sys._current_frames``, "", "Detect when
code is accessing frames directly"
code is accessing frames directly.
"
``PyEval_SetProfile``, ``sys.setprofile``, "", "Detect when code is
injecting trace functions. Because of the implementation, exceptions
raised from the hook will abort the operation, but will not be
raised in Python code. Note that ``threading.setprofile`` eventually
calls this function, so the event will be audited for each thread."
calls this function, so the event will be audited for each thread.
"
``PyEval_SetTrace``, ``sys.settrace``, "", "Detect when code is
injecting trace functions. Because of the implementation, exceptions
raised from the hook will abort the operation, but will not be
raised in Python code. Note that ``threading.settrace`` eventually
calls this function, so the event will be audited for each thread."
calls this function, so the event will be audited for each thread.
"
``_PyEval_SetAsyncGenFirstiter``, ``sys.set_async_gen_firstiter``, "
", "Detect changes to async generator hooks."
", "Detect changes to async generator hooks.
"
``_PyEval_SetAsyncGenFinalizer``, ``sys.set_async_gen_finalizer``, "
", "Detect changes to async generator hooks."
", "Detect changes to async generator hooks.
"
``_PyEval_SetCoroutineWrapper``, ``sys.set_coroutine_wrapper``, "
", "Detect changes to the coroutine wrapper."
", "Detect changes to the coroutine wrapper.
"
``Py_SetRecursionLimit``, ``sys.setrecursionlimit``, "
``(new_limit,)``", "Detect changes to the recursion limit."
``(new_limit,)``", "Detect changes to the recursion limit.
"
``_PyEval_SetSwitchInterval``, ``sys.setswitchinterval``, "
``(interval_us,)``", "Detect changes to the switching interval."
``(interval_us,)``", "Detect changes to the switching interval.
"
"``socket.bind``, ``socket.connect``, ``socket.connect_ex``,
``socket.getaddrinfo``, ``socket.getnameinfo``, ``socket.sendmsg``,
``socket.sendto``", ``socket.address``, "``(address,)``", "Detect
access to network resources. The address is unmodified from the
original call."
original call.
"
``socket.__init__``, "socket()", "``(family, type, proto)``", "
Detect creation of sockets. The arguments will be int values."
Detect creation of sockets. The arguments will be int values.
"
``socket.gethostname``, ``socket.gethostname``, "", "Detect attempts
to retrieve the current host name."
to retrieve the current host name.
"
``socket.sethostname``, ``socket.sethostname``, "``(name,)``", "
Detect attempts to change the current host name. The name argument
is passed as a bytes object."
is passed as a bytes object.
"
"``socket.gethostbyname``, ``socket.gethostbyname_ex``",
"``socket.gethostbyname``", "``(name,)``", "Detect host name
resolution. The name argument is a str or bytes object."
resolution. The name argument is a str or bytes object.
"
``socket.gethostbyaddr``, ``socket.gethostbyaddr``, "
``(address,)``", "Detect host resolution. The address argument is a
str or bytes object."
str or bytes object.
"
``socket.getservbyname``, ``socket.getservbyname``, "``(name,
protocol)``", "Detect service resolution. The arguments are str
objects."
``socket.getservbyport``, ``socket.getservbyport``, "``(port,
objects.
"
"``socket.getservbyport``", ``socket.getservbyport``, "``(port,
protocol)``", "Detect service resolution. The port argument is an
int and protocol is a str."
int and protocol is a str.
"
"``member_get``, ``func_get_code``, ``func_get_[kw]defaults``
",``object.__getattr__``,"``(object, attr)``","Detect access to
restricted attributes. This event is raised for any built-in
members that are marked as restricted, and members that may allow
bypassing imports.
"
"``_PyObject_GenericSetAttr``, ``check_set_special_type_attr``,
``object_set_class``",``object.__setattr__``,"``(object, attr,
value)``","Detect monkey patching of types and objects. This event
``object_set_class``, ``func_set_code``, ``func_set_[kw]defaults``","
``object.__setattr__``","``(object, attr, value)``","Detect monkey
patching of types and objects. This event
is raised for the ``__class__`` attribute and any attribute on
``type`` objects."
``_PyObject_GenericSetAttr``,``object.__delattr__``,"``(object,
``type`` objects.
"
"``_PyObject_GenericSetAttr``",``object.__delattr__``,"``(object,
attr)``","Detect deletion of object attributes. This event is raised
for any attribute on ``type`` objects."
``Unpickler.find_class``,``pickle.find_class``,"``(module_name,
for any attribute on ``type`` objects.
"
"``Unpickler.find_class``",``pickle.find_class``,"``(module_name,
global_name)``","Detect imports and global name lookup when
unpickling."
unpickling.
"
"``array_new``",``array.__new__``,"``(typecode, initial_value)``", "
Detects creation of array objects.
"
TODO - more hooks in ``_socket``, ``_ssl``, others?
* code objects
* function objects
SPython Entry Point
===================
@ -523,16 +569,19 @@ events unless explicitly added, and so they will continue to be allowed.
Performance Impact
==================
**TODO**
The important performance impact is the case where events are being
raised but there are no hooks attached. This is the unavoidable case -
once a distributor or sysadmin begins adding audit hooks they have
explicitly chosen to trade performance for functionality. Performance
impact using ``spython`` or with hooks added are not of interest here,
since this is considered opt-in functionality.
Full impact analysis still requires investigation. Preliminary testing
shows that calling ``sys.audit`` with no hooks added does not
significantly affect any existing benchmarks, though targeted
microbenchmarks can observe an impact.
Performance impact using ``spython`` or with hooks added are not of
interest here, since this is considered opt-in functionality.
Analysis using the ``performance`` tool shows no significant impact,
with the vast majority of benchmarks showing between 1.05x faster to
1.05x slower.
In our opinion, the performance impact of the set of auditing points
described in this PEP is negligible.
Recommendations
===============