ANT-2026-XGXD0ZVD · osgeo/gdal
heap-buffer-overflow high
Severity Claude high · Security research firm high · Maintainer -
Discovered by Claude Mythos Preview
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.
ANT-2026-XGXD0ZVD: Heap-buffer-overflow in gpb.h:288
GDAL's OSM PBF parser reads a blob into a heap buffer (33 bytes in this reproducer) and walks it with protobuf-style helpers. When ReadOSMInfo encounters an unknown field tag it calls SkipUnknownField → SkipVarInt, which reads varint continuation bytes without a sufficient end-pointer check. A crafted OSM PBF file can place a truncated varint at the exact end of the blob, causing SkipVarInt to read one byte past the allocation. An attacker who can supply an OSM PBF file to a GDAL consumer can trigger a crash (under ASAN) or a benign 1-byte over-read in release builds.
Target
Project: gdal
Location: gpb.h:288
Technical Details
ASAN: "READ of size 1 at 0x705ba424a0b1 ... located 0 bytes after 33-byte region". SkipVarInt at gpb.h:288 dereferences the next byte of a varint without confirming the pointer is still below pabyDataLimit; when the varint's high-bit continuation chain reaches the exact end of the blob buffer allocated in ReadBlob (osm_parser.cpp:1836), the next iteration reads one byte into the heap redzone.
Crash trace (truncated — full trace in attached crash.log):
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 4293474245
INFO: Loaded 1 modules (1157955 inline 8-bit counters): 1157955 [0x5c3b756d9a60, 0x5c3b757f45a3),
INFO: Loaded 1 PC tables (1157955 PCs): 1157955 [0x5c3b757f45a8,0x5c3b7699f9d8),
/out/mitab_mif_fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
EXIT_CODE:1
=== ASAN Report ===
=================================================================
==27==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x705ba424a0b1 at pc 0x5c3b6e622e16 bp 0x7ffc251f12d0 sp 0x7ffc251f12c8
READ of size 1 at 0x705ba424a0b1 thread T0
#0 0x5c3b6e622e15 in SkipVarInt /src/gdal/ogr/ogrsf_frmts/osm/gpb.h:288:21
#1 0x5c3b6e622e15 in SkipUnknownField(int, unsigned char const*, unsigned char const*, int) /src/gdal/ogr/ogrsf_frmts/osm/gpb.h:376:9
#2 0x5c3b6fe44d15 in ReadOSMInfo(unsigned char const*, unsigned char const*, OSMInfo*, _OSMContext*) /src/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp:911:17
#3 0x5c3b6fe3acba in ReadNode(unsigned char const*, unsigned char const*, _OSMContext*) /src/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp:1046:22
#4 0x5c3b6fe38657 in ReadPrimitiveGroup /src/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp:1486:22
#5 0x5c3b6fe38657 in ReadPrimitiveBlock(unsigned char const*, unsigned char const*, _OSMContext*) /src/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp:1615:22
#6 0x5c3b6fe33a8a in ProcessSingleBlob /src/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp:1696:16
#7 0x5c3b6fe33a8a in ReadBlob(_OSMContext*, BlobType) /src/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp:1890:18
[... 30 more frames — full trace in crash.log]
Reproduction
- Construct an OSM PBF blob whose PrimitiveBlock contains a Node with an Info message ending in an unknown-tag varint field
- Truncate the varint so its continuation-bit byte lands on the final byte of the blob buffer
- Victim opens the file; ReadBlob allocates exactly blob-size bytes; ReadOSMInfo hits the unknown tag and calls SkipVarInt
- SkipVarInt reads one byte past the allocation
[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]
Acknowledgement
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-XGXD0ZVD.
Reference: ANT-2026-XGXD0ZVD
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Ada Logics.
- Verdict
- true positive
- Severity
- high
The change that resolved this finding.
diff --git a/autotest/ogr/data/osm/poc_14532.osm.pbf b/autotest/ogr/data/osm/poc_14532.osm.pbf
new file mode 100644
index 000000000000..64b9f7723fb2
Binary files /dev/null and b/autotest/ogr/data/osm/poc_14532.osm.pbf differ
diff --git a/autotest/ogr/ogr_osm.py b/autotest/ogr/ogr_osm.py
index fcd757c140c8..723e727a45cc 100755
--- a/autotest/ogr/ogr_osm.py
+++ b/autotest/ogr/ogr_osm.py
@@ -955,3 +955,15 @@ def test_ogr_osm_parse_complex_multipolygon():
f,
"MULTIPOLYGON (((0 10,0 11,1 11,1 10,0 10),(0.1 10.1,0.1 10.4,0.9 10.4,0.9 10.6,0.1 10.1),(0.1 10.6,0.1 10.9,0.9 10.9,0.9 10.6,0.1 10.6)))",
)
+
+
+###############################################################################
+# Test bugfix for https://github.com/OSGeo/gdal/issues/14532
+
+
+def test_ogr_osm_parse_fix_gh14532():
+
+ ds = ogr.Open("data/osm/poc_14532.osm.pbf")
+ for lyr in ds:
+ for f in lyr:
+ pass
diff --git a/ogr/ogrsf_frmts/osm/osm_parser.cpp b/ogr/ogrsf_frmts/osm/osm_parser.cpp
index 08d686471f8c..6e946ea4be6d 100644
--- a/ogr/ogrsf_frmts/osm/osm_parser.cpp
+++ b/ogr/ogrsf_frmts/osm/osm_parser.cpp
@@ -1043,8 +1043,7 @@ static bool ReadNode(const GByte *pabyData, const GByte *pabyDataLimit,
unsigned int nSize = 0;
READ_SIZE(pabyData, pabyDataLimit, nSize);
- if (!ReadOSMInfo(pabyData, pabyDataLimit + nSize, &sNode.sInfo,
- psCtxt))
+ if (!ReadOSMInfo(pabyData, pabyDataLimit, &sNode.sInfo, psCtxt))
THROW_OSM_PARSING_EXCEPTION;
pabyData += nSize;https://github.com/OSGeo/gdal/commit/993ce15f943d68806e36176f8ddab26c649745ae
Dates from discovery through public reveal.
- 2026-03-24 Reported to tracker
- 2026-05-11 Sent to maintainer
- 2026-05-13 Maintainer acknowledged
- 2026-06-05 Patch released
- 2026-07-08 Publicly revealed
SHA-3-512 hash:
062b06724bad1529fb53169abf44c9813d25267fe0db5ef8aaf8ade2995ed83fc2164777c2f6a89aecd15ee09a1720c5b77746fe5eb058fa8723f9527699a243
Committed 2026-05-13 10:55 PT
Revealed 2026-07-08 16:31 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-XGXD0ZVD",
"bug_class": "Heap-buffer-overflow",
"claude_severity": "high",
"commit_sha": null,
"created_at": "2026-03-24T18:28:27+00:00",
"description": "GDAL's OSM PBF parser reads a blob into a heap buffer (33 bytes in this reproducer) and walks it with protobuf-style helpers. When ReadOSMInfo encounters an unknown field tag it calls SkipUnknownField → SkipVarInt, which reads varint continuation bytes without a sufficient end-pointer check. A crafted OSM PBF file can place a truncated varint at the exact end of the blob, causing SkipVarInt to read one byte past the allocation. An attacker who can supply an OSM PBF file to a GDAL consumer can trigger a crash (under ASAN) or a benign 1-byte over-read in release builds.",
"discovered_at": null,
"location": "gpb.h:288",
"poc_sha256": null,
"preimage_version": 1,
"project": "gdal",
"reproduction": [
"1. Construct an OSM PBF blob whose PrimitiveBlock contains a Node with an Info message ending in an unknown-tag varint field",
"2. Truncate the varint so its continuation-bit byte lands on the final byte of the blob buffer",
"3. Victim opens the file; ReadBlob allocates exactly blob-size bytes; ReadOSMInfo hits the unknown tag and calls SkipVarInt",
"4. SkipVarInt reads one byte past the allocation"
],
"technical_details": "ASAN: \"READ of size 1 at 0x705ba424a0b1 ... located 0 bytes after 33-byte region\". SkipVarInt at gpb.h:288 dereferences the next byte of a varint without confirming the pointer is still below pabyDataLimit; when the varint's high-bit continuation chain reaches the exact end of the blob buffer allocated in ReadBlob (osm_parser.cpp:1836), the next iteration reads one byte into the heap redzone.",
"title": "Heap-buffer-overflow in gpb.h:288",
"vendor_severity": "high"
}