4444 } \
4545 } while (0)
4646
47+ #define CHECK_TUPLE_ITEMS (obj , n ) do { \
48+ if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) < (n)) { \
49+ PyErr_Format(PyExc_TypeError, \
50+ #obj " must be a tuple of at least %zd items", \
51+ (Py_ssize_t)(n)); \
52+ return -1; \
53+ } \
54+ } while (0)
55+
56+ #define CHECK_LIST (obj ) do { \
57+ if (!PyList_Check(obj)) { \
58+ PyErr_SetString(PyExc_TypeError, #obj " must be a list"); \
59+ return -1; \
60+ } \
61+ } while (0)
62+
4763/* ============================================================================
4864 * WRITER-SPECIFIC UTILITY HELPERS
4965 * ============================================================================ */
@@ -838,8 +854,8 @@ build_frame_stack(BinaryWriter *writer, PyObject *frame_list,
838854 * curr_depth = (stack_depth < MAX_STACK_DEPTH ) ? stack_depth : MAX_STACK_DEPTH ;
839855
840856 for (Py_ssize_t k = 0 ; k < (Py_ssize_t )* curr_depth ; k ++ ) {
841- /* Use unchecked accessors since we control the data structures */
842857 PyObject * frame_info = PyList_GET_ITEM (frame_list , k );
858+ CHECK_TUPLE_ITEMS (frame_info , 4 );
843859
844860 /* Get filename, location, funcname, opcode from FrameInfo using unchecked access */
845861 PyObject * filename = PyStructSequence_GET_ITEM (frame_info , 0 );
@@ -854,20 +870,13 @@ build_frame_stack(BinaryWriter *writer, PyObject *frame_list,
854870 int32_t end_column = LOCATION_NOT_AVAILABLE ;
855871
856872 if (location != Py_None ) {
873+ CHECK_TUPLE_ITEMS (location , 4 );
857874 /* LocationInfo is a struct sequence or tuple with:
858875 * (lineno, end_lineno, column, end_column) */
859- PyObject * lineno_obj = PyTuple_Check (location ) ?
860- PyTuple_GET_ITEM (location , 0 ) :
861- PyStructSequence_GET_ITEM (location , 0 );
862- PyObject * end_lineno_obj = PyTuple_Check (location ) ?
863- PyTuple_GET_ITEM (location , 1 ) :
864- PyStructSequence_GET_ITEM (location , 1 );
865- PyObject * column_obj = PyTuple_Check (location ) ?
866- PyTuple_GET_ITEM (location , 2 ) :
867- PyStructSequence_GET_ITEM (location , 2 );
868- PyObject * end_column_obj = PyTuple_Check (location ) ?
869- PyTuple_GET_ITEM (location , 3 ) :
870- PyStructSequence_GET_ITEM (location , 3 );
876+ PyObject * lineno_obj = PyTuple_GET_ITEM (location , 0 );
877+ PyObject * end_lineno_obj = PyTuple_GET_ITEM (location , 1 );
878+ PyObject * column_obj = PyTuple_GET_ITEM (location , 2 );
879+ PyObject * end_column_obj = PyTuple_GET_ITEM (location , 3 );
871880
872881 PYLONG_TO_INT32_OR_DEFAULT (lineno_obj , lineno , LOCATION_NOT_AVAILABLE );
873882 PYLONG_TO_INT32_OR_DEFAULT (end_lineno_obj , end_lineno , LOCATION_NOT_AVAILABLE );
@@ -925,9 +934,11 @@ static int
925934process_thread_sample (BinaryWriter * writer , PyObject * thread_info ,
926935 uint32_t interpreter_id , uint64_t timestamp_us )
927936{
937+ CHECK_TUPLE_ITEMS (thread_info , 3 );
928938 PyObject * thread_id_obj = PyStructSequence_GET_ITEM (thread_info , 0 );
929939 PyObject * status_obj = PyStructSequence_GET_ITEM (thread_info , 1 );
930940 PyObject * frame_list = PyStructSequence_GET_ITEM (thread_info , 2 );
941+ CHECK_LIST (frame_list );
931942
932943 uint64_t thread_id = PyLong_AsUnsignedLongLong (thread_id_obj );
933944 if (thread_id == (uint64_t )-1 && PyErr_Occurred ()) {
@@ -1010,17 +1021,16 @@ process_thread_sample(BinaryWriter *writer, PyObject *thread_info,
10101021int
10111022binary_writer_write_sample (BinaryWriter * writer , PyObject * stack_frames , uint64_t timestamp_us )
10121023{
1013- if (!PyList_Check (stack_frames )) {
1014- PyErr_SetString (PyExc_TypeError , "stack_frames must be a list" );
1015- return -1 ;
1016- }
1024+ CHECK_LIST (stack_frames );
10171025
10181026 Py_ssize_t num_interpreters = PyList_GET_SIZE (stack_frames );
10191027 for (Py_ssize_t i = 0 ; i < num_interpreters ; i ++ ) {
10201028 PyObject * interp_info = PyList_GET_ITEM (stack_frames , i );
1029+ CHECK_TUPLE_ITEMS (interp_info , 2 );
10211030
10221031 PyObject * interp_id_obj = PyStructSequence_GET_ITEM (interp_info , 0 );
10231032 PyObject * threads = PyStructSequence_GET_ITEM (interp_info , 1 );
1033+ CHECK_LIST (threads );
10241034
10251035 unsigned long interp_id_long = PyLong_AsUnsignedLong (interp_id_obj );
10261036 if (interp_id_long == (unsigned long )-1 && PyErr_Occurred ()) {
0 commit comments