self.assertEqual(cm.unraisable.exc_type, TypeError)
+ def test_evil_external_timer(self):
+ # gh-120289
+ # Disabling profiler in external timer should not crash
+ import _lsprof
+ class EvilTimer():
+ def __init__(self, disable_count):
+ self.count = 0
+ self.disable_count = disable_count
+
+ def __call__(self):
+ self.count += 1
+ if self.count == self.disable_count:
+ profiler_with_evil_timer.disable()
+ return self.count
+
+ # this will trigger external timer to disable profiler at
+ # call event - in initContext in _lsprof.c
+ with support.catch_unraisable_exception() as cm:
+ profiler_with_evil_timer = _lsprof.Profiler(EvilTimer(1))
+ profiler_with_evil_timer.enable()
+ # Make a call to trigger timer
+ (lambda: None)()
+ profiler_with_evil_timer.disable()
+ profiler_with_evil_timer.clear()
+ self.assertEqual(cm.unraisable.exc_type, RuntimeError)
+
+ # this will trigger external timer to disable profiler at
+ # return event - in Stop in _lsprof.c
+ with support.catch_unraisable_exception() as cm:
+ profiler_with_evil_timer = _lsprof.Profiler(EvilTimer(2))
+ profiler_with_evil_timer.enable()
+ # Make a call to trigger timer
+ (lambda: None)()
+ profiler_with_evil_timer.disable()
+ profiler_with_evil_timer.clear()
+ self.assertEqual(cm.unraisable.exc_type, RuntimeError)
+
def test_profile_enable_disable(self):
prof = self.profilerclass()
# Make sure we clean ourselves up if the test fails for some reason.
#define POF_ENABLED 0x001
#define POF_SUBCALLS 0x002
#define POF_BUILTINS 0x004
+#define POF_EXT_TIMER 0x008
#define POF_NOMEMORY 0x100
/*[clinic input]
static PyTime_t CallExternalTimer(ProfilerObject *pObj)
{
- PyObject *o = _PyObject_CallNoArgs(pObj->externalTimer);
+ PyObject *o = NULL;
+
+ // External timer can do arbitrary things so we need a flag to prevent
+ // horrible things to happen
+ pObj->flags |= POF_EXT_TIMER;
+ o = _PyObject_CallNoArgs(pObj->externalTimer);
+ pObj->flags &= ~POF_EXT_TIMER;
+
if (o == NULL) {
PyErr_WriteUnraisable(pObj->externalTimer);
return 0;
static PyObject*
profiler_disable(ProfilerObject *self, PyObject* noarg)
{
+ if (self->flags & POF_EXT_TIMER) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "cannot disable profiler in external timer");
+ return NULL;
+ }
if (self->flags & POF_ENABLED) {
PyObject* result = NULL;
PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
static PyObject*
profiler_clear(ProfilerObject *pObj, PyObject* noarg)
{
+ if (pObj->flags & POF_EXT_TIMER) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "cannot clear profiler in external timer");
+ return NULL;
+ }
clearEntries(pObj);
Py_RETURN_NONE;
}