Hi all, I would like to update everyone on the progress of the static analyzer plugin for CPython extension module code. Since the last update, I have implemented known function subclasses for PyList_New and PyList_Append. The existing known function subclasses have also been enhanced to provide more information. For instance, we are now simulating object type specific fields in addition to just ob_refcnt and ob_type, which are shared by all PyObjects.
Regarding reference count checking, I have implemented a naive traversal of the store to count the actual reference count of PyObjects, allowing us to compare it against the ob_refcnt fields of the same PyObjects. Although we can compare the actual reference count and the ob_refcnt field, I am still working on implementing a diagnostic to alert about this issue. In addition to the progress update, I have some implementation related questions and would appreciate any input. The current moment at which we run the algorithm for reference count checking, and thereby also the moment at which we may want to issue impl_region_model_context::warn, is within region_model::pop_frame. However, it appears that m_stmt and m_stmt_finder are NULL at the time of region_model::pop_frame, which results in the diagnostic for the reference count getting rejected. I am having trouble finding a workaround for this issue, so any ideas would be welcome. I am also currently examining some issues related to state merging. Let's consider the following example which lacks error checking: PyObject* foo() { PyObject item = PyLong_FromLong(10); PyObject list = PyList_New(5); return list; } The states for when PyLong_FromLong fails and when PyLong_FromLong succeeds are merged before the call to PyObject* list = PyList_New(5). I suspect this may be related to me not correctly handling behavior that arises due to the analyzer deterministically selecting the IDs for heap allocations. Since there is a heap allocation for PyList_New following PyLong_FromLong, the success and fail cases for PyLong_FromLong are merged. I believe this is so that in the scenario where PyLong_FromLong fails and PyList_New succeeds, the ID for the region allocated for PyList_New wouldn't be the same as the PyLong_FromLong success case. Whatever the cause, due to this state merge, the heap allocated region representing PyObject *item has all its fields set to UNKNOWN, making it impossible to perform the reference count checking functionality. I attempted to fix this by wrapping the svalue representing PyLongObject with get_or_create_unmergeable, but it didn't seem to help. However, this issue doesn't occur in all situations. For instance: PyObject* foo() { PyObject item = PyLong_FromLong(10); PyObject list = PyList_New(5); PyList_Append(list, item); return list; } The above scenario is simulated as expected. I will continue to search for a solution, but any suggestions would be highly appreciated. Thank you! Best, Eric