ANT-2026-NQ9DPCK0 · osgeo/gdal

heap-buffer-overflow high

Severity Claude high · Security research firm high · Maintainer -

Discovered by Claude Mythos Preview

REPORT

Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.

ANT-2026-NQ9DPCK0: Heap-buffer-overflow in hfaopen.cpp:3533

When GDAL opens an HFA/ERDAS Imagine raster file, HFADataset::Open invokes HFAReadCameraModel, which calls HFAEntry::BuildEntryFromMIFObject. This function performs a 4-byte read at an offset that lands 8 bytes before the start of a 10-byte heap buffer previously allocated by HFAEntry::LoadData. An attacker who can supply a crafted .img file to any GDAL-based application triggers the out-of-bounds read on file open, with no authentication or interaction beyond file parsing.

Target

Project: gdal
Location: hfaopen.cpp:3533

Technical Details

ASAN: READ of size 4 at 0x7bacb43e3e88, located 8 bytes before a 10-byte heap region. BuildEntryFromMIFObject computes a pointer into the entry data buffer (allocated at hfaentry.cpp:424) that underflows the allocation — the 10-byte buffer is too small for the offset arithmetic performed at line 205, and the code lacks a bounds check before the 4-byte dereference.

Crash trace (truncated — full trace in attached crash.log):

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1626728843
INFO: Loaded 1 modules   (1158086 inline 8-bit counters): 1158086 [0x5b73a153fa60, 0x5b73a165a626), 
INFO: Loaded 1 PC tables (1158086 PCs): 1158086 [0x5b73a165a628,0x5b73a2806288), 
/out/hfa_fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
EXIT_CODE:1


=== ASAN Report ===
=================================================================
==27==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7bacb43e3e88 at pc 0x5b739a0c7903 bp 0x7ffe3d3e4410 sp 0x7ffe3d3e4408
READ of size 4 at 0x7bacb43e3e88 thread T0
    #0 0x5b739a0c7902 in HFAEntry::BuildEntryFromMIFObject(HFAEntry*, char const*) /src/gdal/frmts/hfa/hfaentry.cpp:205:5
    #1 0x5b739a0df7c2 in HFAReadCameraModel /src/gdal/frmts/hfa/hfaopen.cpp:3533:9
    #2 0x5b739a0bcb49 in HFADataset::Open(GDALOpenInfo*) /src/gdal/frmts/hfa/hfadataset.cpp:4656:22
    #3 0x5b739a107884 in GDALDriver::Open(GDALOpenInfo*, bool) /src/gdal/gcore/gdaldriver.cpp:105:16
    #4 0x5b739a1469ed in GDALDataset::Open(GDALOpenInfo*, char const* const*, char const* const*) /src/gdal/gcore/gdaldataset.cpp:4365:39
    #5 0x5b739a145145 in GDALOpenEx /src/gdal/gcore/gdaldataset.cpp:4106:12
    #6 0x5b7399ddbbab in LLVMFuzzerTestOneInput /src/gdal/./fuzzers/gdal_fuzzer.cpp:222:24
    #7 0x5b7399c77d6d in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
    [... 25 more frames — full trace in crash.log]

Reproduction

  1. Craft an HFA file with a camera model entry whose MIF object data buffer is undersized (~10 bytes) relative to the offsets BuildEntryFromMIFObject expects
  2. Deliver the file to the target (upload, email, GIS data exchange, web map tile source, etc.)
  3. Target calls GDALOpenEx on the file; HFAReadCameraModel -> BuildEntryFromMIFObject dereferences 4 bytes at buffer-8

[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-NQ9DPCK0.


Reference: ANT-2026-NQ9DPCK0
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Ada Logics.

Verdict
true positive
Severity
high
UPSTREAM FIX

The change that resolved this finding.

diff --git a/autotest/gdrivers/data/hfa/poc_14547.img b/autotest/gdrivers/data/hfa/poc_14547.img
new file mode 100644
index 000000000000..c11d9339d05e
Binary files /dev/null and b/autotest/gdrivers/data/hfa/poc_14547.img differ
diff --git a/autotest/gdrivers/hfa.py b/autotest/gdrivers/hfa.py
index 26af09c1d640..7e9a2cf934c2 100755
--- a/autotest/gdrivers/hfa.py
+++ b/autotest/gdrivers/hfa.py
@@ -1375,3 +1375,8 @@ def test_hfa_rat_new_types(tmp_vsimem):
 
         rat.SetValueAsWKBGeometry(0, 2, wkb)
         assert rat.GetValueAsWKBGeometry(0, 2) == wkb
+
+
+def test_hfa_read_poc_14547():
+
+    gdal.Open("data/hfa/poc_14547.img")
diff --git a/frmts/hfa/hfaentry.cpp b/frmts/hfa/hfaentry.cpp
index 990fa71ce0b3..521a2fe80da2 100644
--- a/frmts/hfa/hfaentry.cpp
+++ b/frmts/hfa/hfaentry.cpp
@@ -201,7 +201,15 @@ HFAEntry *HFAEntry::BuildEntryFromMIFObject(HFAEntry *poContainer,
     }
 
     GInt32 nMIFObjectSize = 0;
-    // We rudely look before the field data to get at the pointer/size info.
+    // We look before the field data to get at the pointer/size info.
+    const GByte *pabyEntryData = poContainer->GetData();
+    CPLAssert(reinterpret_cast<const GByte *>(pszField) - pabyEntryData >= 0);
+    if (reinterpret_cast<const GByte *>(pszField) - pabyEntryData < 8)
+    {
+        CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s entry",
+                 osFieldName.c_str());
+        return nullptr;
+    }
     memcpy(&nMIFObjectSize, pszField - 8, 4);
     HFAStandard(4, &nMIFObjectSize);
     if (nMIFObjectSize <= 0)

https://github.com/OSGeo/gdal/commit/03ee967da1b22ec61cc7a6b8abb5de3307f213c7

TIMELINE

Dates from discovery through public reveal.

  1. 2026-03-24 Reported to tracker
  2. 2026-05-11 Sent to maintainer
  3. 2026-05-13 Maintainer acknowledged
  4. 2026-06-05 Patch released
  5. 2026-07-20 Publicly revealed
PROVENANCE

SHA-3-512 hash:

f57f3a9f0d76212352423279892545fb34dc45d1cecf6cff9244e0f706b157a60b4ad8e55ee07c5ffc4ff7c7670d053b842d9258561dbb9b9a1c9897a1c34405

Committed 2026-05-13 10:55 PT

Revealed 2026-07-20 22:01 PT

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-NQ9DPCK0",
  "bug_class": "Heap-buffer-overflow",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-03-24T18:28:22+00:00",
  "description": "When GDAL opens an HFA/ERDAS Imagine raster file, HFADataset::Open invokes HFAReadCameraModel, which calls HFAEntry::BuildEntryFromMIFObject. This function performs a 4-byte read at an offset that lands 8 bytes before the start of a 10-byte heap buffer previously allocated by HFAEntry::LoadData. An attacker who can supply a crafted .img file to any GDAL-based application triggers the out-of-bounds read on file open, with no authentication or interaction beyond file parsing.",
  "discovered_at": null,
  "location": "hfaopen.cpp:3533",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "gdal",
  "reproduction": [
    "1. Craft an HFA file with a camera model entry whose MIF object data buffer is undersized (~10 bytes) relative to the offsets BuildEntryFromMIFObject expects",
    "2. Deliver the file to the target (upload, email, GIS data exchange, web map tile source, etc.)",
    "3. Target calls GDALOpenEx on the file; HFAReadCameraModel -> BuildEntryFromMIFObject dereferences 4 bytes at buffer-8"
  ],
  "technical_details": "ASAN: READ of size 4 at 0x7bacb43e3e88, located 8 bytes before a 10-byte heap region. BuildEntryFromMIFObject computes a pointer into the entry data buffer (allocated at hfaentry.cpp:424) that underflows the allocation — the 10-byte buffer is too small for the offset arithmetic performed at line 205, and the code lacks a bounds check before the 4-byte dereference.",
  "title": "Heap-buffer-overflow in hfaopen.cpp:3533",
  "vendor_severity": "high"
}