Repository navigation
r.lake: Reject seed coordinates on the region edge - #7877
Conversation
The bounds check used > instead of >=, so a seed on the southern or eastern edge gave a row or column index equal to rows or cols. That index is one past the end of the terrain and water arrays, which caused an out of bounds read and write, and a crash for the southern edge.
|
/cc @echoix |
|
I’m not sure. Also, why would only one side need a change? |
|
Are you able to see if the other bounds (north or west) would also have a problem? Details
Review of PR 7877 (OSGeo/grass, The core fix is correct: One finding:
|
|
yeahhh, i checked. North and west are fine because a coordinate on those edges maps to row 0 / col 0, which is already inside the region. South and east map to rows and cols, which is one past the end, so that's why only that side broke. But they do have a smaller issue. The (int) cast truncates towards zero, so anything between -1 and 0 becomes 0 and start_row < 0 never catches it. On a 50x50 region a seed half a cell north of the region gets silently accepted as row 0 instead of being rejected. Same on the west side. using floor() on the two conversions fixes that, and the existing tests still pass. |
|
Open another one, someone else will review only that part. But in that PR, can you add a test that fails without the fix? |
|
yes i can |
Problem
r.lakevalidates the seed coordinate before using it as a cell index:The valid indices are
0torows - 1and0tocols - 1, so the comparisons need to be>=. As written,start_row == rowsandstart_col == colspass the check.Those values are reachable from ordinary input.
Rast_northing_to_row()returns exactlyrowsfor a coordinate on the southern edge of the region, andRast_easting_to_col()returns exactlycolson the eastern edge. Neither is clamped afterwards.The arrays have no spare element.
in_terranandout_waterhold exactlyrowsrow pointers, and each row holds exactlycolsvalues, so both indices are one past the end:Effect
A seed on the southern edge reads a pointer from past the end of the pointer array and dereferences it, which crashes. A seed on the eastern edge reads past the end of a row and then writes four bytes past the end of another, after which the tool completes with exit code 0 and writes a lake map containing no water at all, with no warning.
AddressSanitizer on the current main branch:
Reproduction
The coordinate does not have to be chosen deliberately.
g.region vector=<points>sets the region bounds to the bounding box of the vector, so the southernmost and easternmost points sit exactly on the region edge:Fix
Use
>=so the check covers the valid index range. Seeds on the edge then take the existing error path, which is what the check was written to do.Tests
Three cases covering the southern edge, the eastern edge and the corner, asserting the tool exits with the regular error rather than a signal, plus one case for a seed in the last row to confirm valid coordinates are still accepted. The three edge cases fail on main and pass with the fix.
r.costandr.walkalready use the>=form for the same check.r.pathandr.draindo not, but they behave differently and are left for a separate change.