heap-use-after-free in /src/geos/src/geomgraph/PlanarGraph.cpp
Build platform
ubuntu20.04
Build steps
CC = clang
CFLAGS=-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link
cmake -DBUILD_SHARED_LIBS=OFF ..
make
Test case
Execution steps
./fuzz_geo2 poc
Output
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 3281110018
INFO: Loaded 1 modules (57746 inline 8-bit counters): 57746 [0xc1e520, 0xc2c6b2),
INFO: Loaded 1 PC tables (57746 PCs): 57746 [0xc2c6b8,0xd0dfd8),
./fuzz_geo2: Running 1 inputs 1 time(s) each.
Running: poc
=================================================================
==13391==ERROR: AddressSanitizer: heap-use-after-free on address 0x60e000000740 at pc 0x00000068ff62 bp 0x7ffe641c7a60 sp 0x7ffe641c7a58
READ of size 8 at 0x60e000000740 thread T0
#0 0x68ff61 in geos::geomgraph::PlanarGraph::~PlanarGraph() /src/geos/src/geomgraph/PlanarGraph.cpp:88:9
#1 0x7ed98a in geos::operation::overlay::OverlayOp::~OverlayOp() /src/geos/src/operation/overlay/OverlayOp.cpp:184:1
#2 0x7ec54b in geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) /src/geos/src/operation/overlay/OverlayOp.cpp:94:1
#3 0x62fe29 in geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) /src/geos/src/geom/HeuristicOverlay.cpp:446:19
#4 0x616c24 in geos::geom::Geometry::Union(geos::geom::Geometry const*) const /src/geos/src/geom/Geometry.cpp:599:12
#5 0x575ed8 in operator() /src/geos/capi/geos_ts_c.cpp:1378:27
#6 0x575ed8 in execute<(lambda at /src/geos/capi/geos_ts_c.cpp:1377:35), nullptr> /src/geos/capi/geos_ts_c.cpp:415:16
#7 0x575ed8 in GEOSUnion_r /src/geos/capi/geos_ts_c.cpp:1377:16
#8 0x55ee5d in LLVMFuzzerTestOneInput /src/geos/tests/fuzz/fuzz_geo2.c:57:18
#9 0x456302 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
#10 0x441eb2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
#11 0x44771c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
#12 0x4700b2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#13 0x7f280be5f0b2 in __libc_start_main /build/glibc-sMfBJT/glibc-2.31/csu/../csu/libc-start.c:308:16
#14 0x4205fd in _start (/home/wcc/workspace/geos/build-out/fuzz_geo2+0x4205fd)
0x60e000000740 is located 0 bytes inside of 160-byte region [0x60e000000740,0x60e0000007e0)
freed by thread T0 here:
#0 0x55cadd in operator delete(void*) /src/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:152:3
#1 0x95bb2b in operator() /usr/local/bin/../include/c++/v1/__memory/unique_ptr.h:54:5
#2 0x95bb2b in reset /usr/local/bin/../include/c++/v1/__memory/unique_ptr.h:315:7
#3 0x95bb2b in ~unique_ptr /usr/local/bin/../include/c++/v1/__memory/unique_ptr.h:269:19
#4 0x95bb2b in geos::geomgraph::Node::add(geos::geomgraph::EdgeEnd*) /src/geos/src/geomgraph/Node.cpp:163:1
#5 0x690910 in geos::geomgraph::PlanarGraph::add(geos::geomgraph::EdgeEnd*) /src/geos/src/geomgraph/PlanarGraph.cpp:147:12
#6 0x691234 in geos::geomgraph::PlanarGraph::addEdges(std::__1::vector<geos::geomgraph::Edge*, std::__1::allocator<geos::geomgraph::Edge*> > const&) /src/geos/src/geomgraph/PlanarGraph.cpp:225:9
#7 0x7ee61d in geos::operation::overlay::OverlayOp::computeOverlay(geos::operation::overlay::OverlayOp::OpCode) /src/geos/src/operation/overlay/OverlayOp.cpp:837:11
#8 0x7ec472 in getResultGeometry /src/geos/src/operation/overlay/OverlayOp.cpp:191:5
#9 0x7ec472 in geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) /src/geos/src/operation/overlay/OverlayOp.cpp:93:16
#10 0x62fe29 in geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) /src/geos/src/geom/HeuristicOverlay.cpp:446:19
#11 0x616c24 in geos::geom::Geometry::Union(geos::geom::Geometry const*) const /src/geos/src/geom/Geometry.cpp:599:12
#12 0x575ed8 in operator() /src/geos/capi/geos_ts_c.cpp:1378:27
#13 0x575ed8 in execute<(lambda at /src/geos/capi/geos_ts_c.cpp:1377:35), nullptr> /src/geos/capi/geos_ts_c.cpp:415:16
#14 0x575ed8 in GEOSUnion_r /src/geos/capi/geos_ts_c.cpp:1377:16
#15 0x55ee5d in LLVMFuzzerTestOneInput /src/geos/tests/fuzz/fuzz_geo2.c:57:18
#16 0x456302 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
#17 0x441eb2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
#18 0x44771c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
#19 0x4700b2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#20 0x7f280be5f0b2 in __libc_start_main /build/glibc-sMfBJT/glibc-2.31/csu/../csu/libc-start.c:308:16
previously allocated by thread T0 here:
#0 0x55c27d in operator new(unsigned long) /src/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:95:3
#1 0x691148 in make_unique<geos::geomgraph::DirectedEdge, geos::geomgraph::Edge *&, bool> /src/geos/include/geos/util.h:61:31
#2 0x691148 in geos::geomgraph::PlanarGraph::addEdges(std::__1::vector<geos::geomgraph::Edge*, std::__1::allocator<geos::geomgraph::Edge*> > const&) /src/geos/src/geomgraph/PlanarGraph.cpp:219:20
#3 0x7ee61d in geos::operation::overlay::OverlayOp::computeOverlay(geos::operation::overlay::OverlayOp::OpCode) /src/geos/src/operation/overlay/OverlayOp.cpp:837:11
#4 0x7ec472 in getResultGeometry /src/geos/src/operation/overlay/OverlayOp.cpp:191:5
#5 0x7ec472 in geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) /src/geos/src/operation/overlay/OverlayOp.cpp:93:16
#6 0x62fe29 in geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) /src/geos/src/geom/HeuristicOverlay.cpp:446:19
#7 0x616c24 in geos::geom::Geometry::Union(geos::geom::Geometry const*) const /src/geos/src/geom/Geometry.cpp:599:12
#8 0x575ed8 in operator() /src/geos/capi/geos_ts_c.cpp:1378:27
#9 0x575ed8 in execute<(lambda at /src/geos/capi/geos_ts_c.cpp:1377:35), nullptr> /src/geos/capi/geos_ts_c.cpp:415:16
#10 0x575ed8 in GEOSUnion_r /src/geos/capi/geos_ts_c.cpp:1377:16
#11 0x55ee5d in LLVMFuzzerTestOneInput /src/geos/tests/fuzz/fuzz_geo2.c:57:18
#12 0x456302 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
#13 0x441eb2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
#14 0x44771c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
#15 0x4700b2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#16 0x7f280be5f0b2 in __libc_start_main /build/glibc-sMfBJT/glibc-2.31/csu/../csu/libc-start.c:308:16
SUMMARY: AddressSanitizer: heap-use-after-free /src/geos/src/geomgraph/PlanarGraph.cpp:88:9 in geos::geomgraph::PlanarGraph::~PlanarGraph()
Shadow bytes around the buggy address:
0x0c1c7fff8090: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
0x0c1c7fff80a0: 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa fa
0x0c1c7fff80b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c1c7fff80c0: fd fd fd fa fa fa fa fa fa fa fa fa fd fd fd fd
0x0c1c7fff80d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c1c7fff80e0: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
0x0c1c7fff80f0: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
0x0c1c7fff8100: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
0x0c1c7fff8110: 00 00 00 00 00 00 fa fa fa fa fa fa fa fa fa fa
0x0c1c7fff8120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c1c7fff8130: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==13391==ABORTING
Sorry, I don't see how to reproduce? I have no source to create 'fuzz_geo2' and there is no source in the attached zip.
Sorry, I forget it. I build it according to oss-fuzz
reproduce
git apply ../patch.diff
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF ..
make -j$(nproc)
This is the patch.diff file. patch.zip
The fuzz_geo2 is created in bin/fuzz_geo2
Is this running against an old GEOS? 3.8 or lower? The call is into the /overlay/ code line instead of overlayng, which is really weird.
There's no main() in the test program, what actually runs it? I'm definitely still unable to build this. Had to do this to get the CMake stuff to turn over:
export LIB_FUZZING_ENGINE=geos_c
Even then, output won't build as there's no main().
I can reproduce it in the latest version. And I add the main() in the test program.
- install the independence
apt-get install libc++-10-dev
apt-get install libc++abi-10-dev
- set the compiler
export CC=clang
export CXX=clang++
export CFLAGS="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link"
export CXXFLAGS="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libc++"
- build This is the patch.diff file. patch.zip
git clone https://github.com/libgeos/geos.git
cd geos
git apply ../patch.diff
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF ..
make
- run poc This is the poc file. poc.zip
./bin/fuzz_geo2 poc
- output
root@ubuntu:/geos/build/bin# ./fuzz_geo2 poc
=================================================================
==25669==ERROR: AddressSanitizer: heap-use-after-free on address 0x60e000000660 at pc 0x00000060403f bp 0x7ffd058a3f70 sp 0x7ffd058a3f68
READ of size 8 at 0x60e000000660 thread T0
#0 0x60403e in geos::geomgraph::PlanarGraph::~PlanarGraph() /geos/src/geomgraph/PlanarGraph.cpp:88:9
#1 0x76bed3 in geos::operation::overlay::OverlayOp::~OverlayOp() /geos/src/operation/overlay/OverlayOp.cpp:184:1
#2 0x76aa78 in geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) /geos/src/operation/overlay/OverlayOp.cpp:94:1
#3 0x59ea15 in geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) /geos/src/geom/HeuristicOverlay.cpp:446:19
#4 0x583a3b in geos::geom::Geometry::Union(geos::geom::Geometry const*) const /geos/src/geom/Geometry.cpp:599:12
#5 0x4e16f5 in operator() /geos/capi/geos_ts_c.cpp:1378:27
#6 0x4e16f5 in execute<(lambda at /geos/capi/geos_ts_c.cpp:1377:35), nullptr> /geos/capi/geos_ts_c.cpp:415:16
#7 0x4e16f5 in GEOSUnion_r /geos/capi/geos_ts_c.cpp:1377:16
#8 0x4caa05 in main /geos/tests/fuzz/fuzz_geo2.c:70:18
#9 0x7fc909fa4082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
#10 0x41fabd in _start (/geos/build/bin/fuzz_geo2+0x41fabd)
0x60e000000660 is located 0 bytes inside of 160-byte region [0x60e000000660,0x60e000000700)
freed by thread T0 here:
#0 0x4c81cd in operator delete(void*) (/geos/build/bin/fuzz_geo2+0x4c81cd)
#1 0x8f3c80 in operator() /usr/lib/llvm-10/bin/../include/c++/v1/memory:2363:5
#2 0x8f3c80 in reset /usr/lib/llvm-10/bin/../include/c++/v1/memory:2618:7
#3 0x8f3c80 in ~unique_ptr /usr/lib/llvm-10/bin/../include/c++/v1/memory:2572:19
#4 0x8f3c80 in geos::geomgraph::Node::add(geos::geomgraph::EdgeEnd*) /geos/src/geomgraph/Node.cpp:163:1
#5 0x604933 in geos::geomgraph::PlanarGraph::add(geos::geomgraph::EdgeEnd*) /geos/src/geomgraph/PlanarGraph.cpp:147:12
#6 0x6052f8 in geos::geomgraph::PlanarGraph::addEdges(std::__1::vector<geos::geomgraph::Edge*, std::__1::allocator<geos::geomgraph::Edge*> > const&) /geos/src/geomgraph/PlanarGraph.cpp:225:9
#7 0x76c96f in geos::operation::overlay::OverlayOp::computeOverlay(geos::operation::overlay::OverlayOp::OpCode) /geos/src/operation/overlay/OverlayOp.cpp:837:11
#8 0x76a99f in getResultGeometry /geos/src/operation/overlay/OverlayOp.cpp:191:5
#9 0x76a99f in geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) /geos/src/operation/overlay/OverlayOp.cpp:93:16
#10 0x59ea15 in geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) /geos/src/geom/HeuristicOverlay.cpp:446:19
#11 0x583a3b in geos::geom::Geometry::Union(geos::geom::Geometry const*) const /geos/src/geom/Geometry.cpp:599:12
#12 0x4e16f5 in operator() /geos/capi/geos_ts_c.cpp:1378:27
#13 0x4e16f5 in execute<(lambda at /geos/capi/geos_ts_c.cpp:1377:35), nullptr> /geos/capi/geos_ts_c.cpp:415:16
#14 0x4e16f5 in GEOSUnion_r /geos/capi/geos_ts_c.cpp:1377:16
#15 0x4caa05 in main /geos/tests/fuzz/fuzz_geo2.c:70:18
#16 0x7fc909fa4082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
previously allocated by thread T0 here:
#0 0x4c796d in operator new(unsigned long) (/geos/build/bin/fuzz_geo2+0x4c796d)
#1 0x605205 in make_unique<geos::geomgraph::DirectedEdge, geos::geomgraph::Edge *&, bool> /geos/include/geos/util.h:61:31
#2 0x605205 in geos::geomgraph::PlanarGraph::addEdges(std::__1::vector<geos::geomgraph::Edge*, std::__1::allocator<geos::geomgraph::Edge*> > const&) /geos/src/geomgraph/PlanarGraph.cpp:219:20
#3 0x76c96f in geos::operation::overlay::OverlayOp::computeOverlay(geos::operation::overlay::OverlayOp::OpCode) /geos/src/operation/overlay/OverlayOp.cpp:837:11
#4 0x76a99f in getResultGeometry /geos/src/operation/overlay/OverlayOp.cpp:191:5
#5 0x76a99f in geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) /geos/src/operation/overlay/OverlayOp.cpp:93:16
#6 0x59ea15 in geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) /geos/src/geom/HeuristicOverlay.cpp:446:19
#7 0x583a3b in geos::geom::Geometry::Union(geos::geom::Geometry const*) const /geos/src/geom/Geometry.cpp:599:12
#8 0x4e16f5 in operator() /geos/capi/geos_ts_c.cpp:1378:27
#9 0x4e16f5 in execute<(lambda at /geos/capi/geos_ts_c.cpp:1377:35), nullptr> /geos/capi/geos_ts_c.cpp:415:16
#10 0x4e16f5 in GEOSUnion_r /geos/capi/geos_ts_c.cpp:1377:16
#11 0x4caa05 in main /geos/tests/fuzz/fuzz_geo2.c:70:18
#12 0x7fc909fa4082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
SUMMARY: AddressSanitizer: heap-use-after-free /geos/src/geomgraph/PlanarGraph.cpp:88:9 in geos::geomgraph::PlanarGraph::~PlanarGraph()
Shadow bytes around the buggy address:
0x0c1c7fff8070: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
0x0c1c7fff8080: 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa
0x0c1c7fff8090: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
0x0c1c7fff80a0: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
0x0c1c7fff80b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c1c7fff80c0: fd fd fd fd fa fa fa fa fa fa fa fa[fd]fd fd fd
0x0c1c7fff80d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c1c7fff80e0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
0x0c1c7fff80f0: 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa
0x0c1c7fff8100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c1c7fff8110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==25669==ABORTING
I can reproduce locally with latest GEOS master with a regular debug build of GEOS. Dumping the input geometries of poc as WKT, the issue is likely related to the LINESTRING having NaN coordinates:
==2704857== Memcheck, a memory error detector
==2704857== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2704857== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2704857== Command: bin/fuzz_geo2 poc
==2704857==
g1: POINT(0 3)
g2: LINESTRING (0.0000000000000000 7245424010045534766731828141871634255677735750711138179957503611844138067034112.0000000000000000, 0.0000000000000000 0.0000000000000000, -nan -nan)
==2704857== Invalid read of size 8
==2704857== at 0x525B57E: geos::geomgraph::PlanarGraph::~PlanarGraph() (PlanarGraph.cpp:88)
==2704857== by 0x5376429: geos::operation::overlay::OverlayOp::~OverlayOp() (OverlayOp.cpp:173)
==2704857== by 0x5375F4C: geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) (OverlayOp.cpp:92)
==2704857== by 0x5214102: geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) (HeuristicOverlay.cpp:446)
==2704857== by 0x5204BFC: geos::geom::Geometry::Union(geos::geom::Geometry const*) const (Geometry.cpp:587)
==2704857== by 0x48C115F: GEOSUnion_r::{lambda()#1}::operator()() const (geos_ts_c.cpp:1441)
==2704857== by 0x48D2F15: decltype ({parm#2}()) execute<GEOSUnion_r::{lambda()#1}, (decltype(nullptr))0>(GEOSContextHandle_HS*, GEOSUnion_r::{lambda()#1}&&) (geos_ts_c.cpp:423)
==2704857== by 0x48C1249: GEOSUnion_r (geos_ts_c.cpp:1440)
==2704857== by 0x48B9075: GEOSUnion (geos_c.cpp:576)
==2704857== by 0x1097F9: main (fuzz_geo2.c:74)
==2704857== Address 0x5991560 is 0 bytes inside a block of size 160 free'd
==2704857== at 0x483CFBF: operator delete(void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2704857== by 0x523BBBF: geos::geomgraph::DirectedEdge::~DirectedEdge() (DirectedEdge.h:42)
==2704857== by 0x52578F7: std::default_delete<geos::geomgraph::EdgeEnd>::operator()(geos::geomgraph::EdgeEnd*) const (unique_ptr.h:81)
==2704857== by 0x525777B: std::unique_ptr<geos::geomgraph::EdgeEnd, std::default_delete<geos::geomgraph::EdgeEnd> >::~unique_ptr() (unique_ptr.h:292)
==2704857== by 0x5256EA8: geos::geomgraph::Node::add(geos::geomgraph::EdgeEnd*) (Node.cpp:138)
==2704857== by 0x5258347: geos::geomgraph::NodeMap::add(geos::geomgraph::EdgeEnd*) (NodeMap.cpp:115)
==2704857== by 0x525B851: geos::geomgraph::PlanarGraph::add(geos::geomgraph::EdgeEnd*) (PlanarGraph.cpp:147)
==2704857== by 0x525BC34: geos::geomgraph::PlanarGraph::addEdges(std::vector<geos::geomgraph::Edge*, std::allocator<geos::geomgraph::Edge*> > const&) (PlanarGraph.cpp:223)
==2704857== by 0x5378191: geos::operation::overlay::OverlayOp::computeOverlay(geos::operation::overlay::OverlayOp::OpCode) (OverlayOp.cpp:832)
==2704857== by 0x537649D: geos::operation::overlay::OverlayOp::getResultGeometry(geos::operation::overlay::OverlayOp::OpCode) (OverlayOp.cpp:191)
==2704857== by 0x5375F0F: geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) (OverlayOp.cpp:93)
==2704857== by 0x5214102: geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) (HeuristicOverlay.cpp:446)
==2704857== Block was alloc'd at
==2704857== at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2704857== by 0x525CD37: geos::detail::_Unique_if<geos::geomgraph::DirectedEdge>::_Single_object geos::detail::make_unique<geos::geomgraph::DirectedEdge, geos::geomgraph::Edge*&, bool>(geos::geomgraph::Edge*&, bool&&) (util.h:61)
==2704857== by 0x525BB96: geos::geomgraph::PlanarGraph::addEdges(std::vector<geos::geomgraph::Edge*, std::allocator<geos::geomgraph::Edge*> > const&) (PlanarGraph.cpp:217)
==2704857== by 0x5378191: geos::operation::overlay::OverlayOp::computeOverlay(geos::operation::overlay::OverlayOp::OpCode) (OverlayOp.cpp:832)
==2704857== by 0x537649D: geos::operation::overlay::OverlayOp::getResultGeometry(geos::operation::overlay::OverlayOp::OpCode) (OverlayOp.cpp:191)
==2704857== by 0x5375F0F: geos::operation::overlay::OverlayOp::overlayOp(geos::geom::Geometry const*, geos::geom::Geometry const*, geos::operation::overlay::OverlayOp::OpCode) (OverlayOp.cpp:93)
==2704857== by 0x5214102: geos::geom::HeuristicOverlay(geos::geom::Geometry const*, geos::geom::Geometry const*, int) (HeuristicOverlay.cpp:446)
==2704857== by 0x5204BFC: geos::geom::Geometry::Union(geos::geom::Geometry const*) const (Geometry.cpp:587)
==2704857== by 0x48C115F: GEOSUnion_r::{lambda()#1}::operator()() const (geos_ts_c.cpp:1441)
==2704857== by 0x48D2F15: decltype ({parm#2}()) execute<GEOSUnion_r::{lambda()#1}, (decltype(nullptr))0>(GEOSContextHandle_HS*, GEOSUnion_r::{lambda()#1}&&) (geos_ts_c.cpp:423)
==2704857== by 0x48C1249: GEOSUnion_r (geos_ts_c.cpp:1440)
==2704857== by 0x48B9075: GEOSUnion (geos_c.cpp:576)
==2704857==
the issue is likely related to the LINESTRING having NaN coordinates:
Which means the input geometry is invalid. The usual GEOS contract is that inputs must be valid for output to be valid.
Although of course it's always better to avoid crashing. But checking for Nan coordinates everywhere is going to be a big code and performance hit.
This is a basic problem with fuzzing - the inputs may turn out to be so malformed that they fall outside the envelope of what the library is reasonably able to handle. Not sure what the solution is here. Perhaps restrict the kinds of fuzzed inputs that are tested?
We can certainly strip NaN from WKT and WKB and GeoJSON inputs, but yeah, testing the underlying coordinates in a bulk buffer load could potentially get quite costly.
I'm happy to look into it but don't want to duplicate effort. Let me know.
I'm happy to look into it but don't want to duplicate effort.
I'm not investigating
Hm, locally I don't get a crash, though I do get an exception.
IllegalArgumentException: CGAlgorithmsDD::orientationIndex encountered NaN/Inf numbers
I guess this error is actually a memory error though, so perhaps we're doing some bad reads while stepping up out of the call. Here's a quick test case for GEOSUnionTest.cpp. I will stop now.
template<>
template<>
void object::test<2>()
{
geom1_ = GEOSGeomFromWKT("POINT(0 3)");
geom2_ = GEOSGeomFromWKT("LINESTRING (0.0 7245424010045534766731828141871634255677735750711138179957503611844138067034112.0, 0.0 0.0, -nan -nan)");
const char *expected_wkt = "MULTIPOINT (2 8, 3 9)";
ensure(geom1_);
ensure(geom2_);
GEOSSetSRID(geom1_, 4326);
GEOSGeometry* result = GEOSUnion(geom1_, geom2_);
// std::cout << toWKT(result) << std::endl;
GEOSGeometry* expected = GEOSGeomFromWKT(expected_wkt);
ensure(result);
ensure(expected);
ensure_equals(GEOSEqualsExact(result, expected, 0), 1);
ensure_equals(GEOSGetSRID(geom1_), GEOSGetSRID(result));
GEOSGeom_destroy(result);
GEOSGeom_destroy(expected);
}
Hm, locally I don't get a crash, though I do get an exception.
I also get that when using geom2 built from its export as WKT. The memory error requires to use the WKB input of the poc file.
We can certainly strip NaN from WKT and WKB and GeoJSON inputs, but yeah, testing the underlying coordinates in a bulk buffer load could potentially get quite costly.
Silently dropping input seems like a bad idea? Perhaps throw an exception? Although generally isn't the paradigm to be lenient in what can be read, and then rely on isValid checking on the part of the user?
Yea, true, pretty sure we had this convo before and it's why we're catching NaN quite deep in the stack.
Yea, true, pretty sure we had this convo before and it's why we're catching NaN quite deep in the stack.
Maybe there's just one more place that needs a Nan/Inf check... maybe...
On the other hand, if we're only ending up in the old overlay code because the new overlay code failed, does it make sense to just ... not call the old overlay code?
On the other hand, if we're only ending up in the old overlay code because the new overlay code failed, does it make sense to just ... not call the old overlay code?
tentative +1 to this. Would be nice to drop HeuristicOverlay - it's a mess.
Unfortunately there are still some failure cases with OverlayNG - but they produce bad results rather than exceptions, so aren't detected anyway.