From abd96ca899b6c339ccf547b8e143af678ec89ff1 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Thu, 9 Jul 2026 09:12:48 -0700 Subject: [PATCH] fix: memory corruption due to accessing out of bounds output vector index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In numpy_scan.cpp, the OBJECT/STRING scan path uses pos = i + offset (the absolute row index in the source array) as the output vector index for setNull() when handling None or NaN values: ```cpp // BEFORE (buggy): outputVector->setNull(pos, true /* isNull */); // pos = i + offset ``` The output vector is sized for DEFAULT_VECTOR_CAPACITY (= 2048) entries and expects 0-based indices within each batch. But pos is the absolute dataframe row index. When the COPY spans multiple batches (i.e., when offset >= DEFAULT_VECTOR_CAPACITY), pos exceeds the null mask buffer, causing a heap buffer overrun → the segfault / free(): invalid next size / munmap_chunk(): invalid pointer crashes seen on Linux. --- src_cpp/numpy/numpy_scan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_cpp/numpy/numpy_scan.cpp b/src_cpp/numpy/numpy_scan.cpp index 0dc885c..c172205 100644 --- a/src_cpp/numpy/numpy_scan.cpp +++ b/src_cpp/numpy/numpy_scan.cpp @@ -172,7 +172,7 @@ void NumpyScan::scan(PandasColumnBindData* bindData, uint64_t count, uint64_t of !py::isinstance(val)) { if (val == Py_None || (py::isinstance(val) && std::isnan(PyFloat_AsDouble(val)))) { - outputVector->setNull(pos, true /* isNull */); + outputVector->setNull(i, true /* isNull */); continue; } if (!py::isinstance(val)) {