lib/vector: Fix null pointer dereference for an empty cat_list - #7835
lib/vector: Fix null pointer dereference for an empty cat_list#7835Pranav-error wants to merge 1 commit into
Conversation
|
I restarted the timeout in macOS run |
|
Thanks. For what it is worth, the two new tests take about 0.4s for the file locally, so they should not be adding anything meaningful to the macOS run time — but tell me if it times out again and I will dig into it. |
f5d35e5 to
6c7d2e8
Compare
|
The Windows failure was mine, sorry — not flakiness. The test called the library through ctypes in the pytest process, so the null dereference ended the whole run rather than one test, and the Windows job stopped at 8%. Pushed a fix: the library calls now run in a subprocess, so a crash shows up as a return code the test asserts on instead of taking the run down. Without the guard the test now fails with The macOS run looks separate: attempt 2 was 1215 passed with 29 errors, all |
|
An AI review (Opus 5) that I made seems to question the fact of returning success and Details
Reviewed PR 7835 ( Findings:
|
…_list Vect_cat_list_to_array() read cats[0] with cats still NULL when the list had no ranges, which is a null pointer dereference reachable through Vect_copy_table_by_cat_list(). Returning success with a null array would replace the crash with a silent wrong result on that same path: Vect_copy_table_by_cats() passes a null array as a null selection column, and db_copy_table_by_ints() reads a null column as "no filter" and copies every row instead of none. That function already treats an empty selection with a filter column as DB_FAILED, so failing here matches the convention around it. The only in-tree caller already propagates a non-zero return.
6c7d2e8 to
d52000f
Compare
|
Thanks — that review is right, and I checked each step of it against the tree rather than taking it on trust. All four links hold:
So my patch turned a crash into a silent wrong result on exactly the path I cited as motivation, which is worse. I have changed it to return -1 for a list with no ranges. Of the three options you listed I went with the first. The zero-length allocation would work but leans on Returning -1 also needs nothing from the caller: I updated the Doxygen to say an empty list is a failure and why, and flipped the test to assert -1. The ranges case is unchanged and still covers sorting and dedup. |
Vect_cat_list_to_array()allocates its array inside the loop over the ranges, so alist with no ranges leaves
catsNULL, andlast_cat = ucats[0] = cats[0]thendereferences it.
An empty list is reachable through the public API:
Vect_new_cat_list()creates a listwith no ranges, and
Vect_copy_table_by_cat_list()checks only that the list is notNULL, not that it has any.
This returns an empty array instead. The documented contract says -1 is failure, and an
empty selection isn't a failure, so it returns 0 with
nvalsset to 0. Happy to make it-1 instead if you would rather callers treat an empty list as an error.
The guard is exact:
n_catsonly ever grows byn >= 1, son_cats == 0happensexactly when the loop never ran, which is also the only case where
catsis still NULL.cppcheck has this under
# True positivesin.cppcheck-suppressions(as lines 513 and517, which have since drifted to 515); both entries are removed here.
Tests:
lib/vector/Vlib/tests/lib_vector_cats_test.pycovers the empty list and thesorting and deduplication that must not change. Run the way CI runs pytest, the empty-list
test segfaults without the guard and passes with it.
The tests need a session, so they come with a
conftest.pywhose fixture callsgs.setup.init(project)withoutenv=os.environ.copy(). The copy is right for thesubprocess-based tests but not here, since the library reads the environment in this
process.
I used an AI assistant while investigating and writing this. I understand the change and
can explain it.