ANT-2026-K3APEMB5 · osgeo/gdal
heap-buffer-overflow high
Severity Claude critical · 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-K3APEMB5: Heap-buffer-overflow in gdal
The libFuzzer harness mrf_fuzzer was executed against a single reproducer (/tmp/poc) and exited with status 1, but the log contains no AddressSanitizer/UBSan diagnostic, no crashing function, and no stack frames. Without a sanitizer tag or backtrace, the crash type, access shape (READ/WRITE, size), and source location cannot be determined from this output alone.
Target
Project: gdal
Technical Details
Fuzz target mrf_fuzzer terminated with EXIT_CODE:1 on the provided PoC; no sanitizer (ASAN/UBSan) report or stack trace was emitted.
Crash trace:
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2887737378
INFO: Loaded 1 modules (1021859 inline 8-bit counters): 1021859 [0x638c0623a440, 0x638c06333be3),
INFO: Loaded 1 PC tables (1021859 PCs): 1021859 [0x638c06333be8,0x638c072cb618),
/out/mrf_fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
EXIT_CODE:1
Reproduction
Reproduce against the target as described under Technical Details.
[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-K3APEMB5.
Reference: ANT-2026-K3APEMB5
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/frmts/mrf/mrf_band.cpp b/frmts/mrf/mrf_band.cpp
index d4f71a7cd041..21e6dad1b6ba 100644
--- a/frmts/mrf/mrf_band.cpp
+++ b/frmts/mrf/mrf_band.cpp
@@ -898,7 +898,16 @@ CPLErr MRFRasterBand::FetchClonedBlock(int xblk, int yblk, void *buffer)
MRFDataset *poSrc = static_cast<MRFDataset *>(poMRFDS->GetSrcDS());
if (nullptr == poSrc)
{
- CPLError(CE_Failure, CPLE_AppDefined, "MRF: Can't open source file %s",
+ CPLError(CE_Failure, CPLE_AppDefined, "MRF: Can't open source %s",
+ poMRFDS->source.c_str());
+ return CE_Failure;
+ }
+ // Check that the source is an MRF
+ auto srcDriver = poSrc->GetDriver();
+ if (srcDriver == nullptr || !EQUAL(srcDriver->GetDescription(), "MRF"))
+ {
+ CPLError(CE_Failure, CPLE_AppDefined,
+ "MRF: Cloned source %s is not an MRF",
poMRFDS->source.c_str());
return CE_Failure;
}
@@ -908,11 +917,35 @@ CPLErr MRFRasterBand::FetchClonedBlock(int xblk, int yblk, void *buffer)
// Can't store, so just fetch from source, which is an MRF with
// identical structure
MRFRasterBand *b =
- static_cast<MRFRasterBand *>(poSrc->GetRasterBand(nBand));
+ reinterpret_cast<MRFRasterBand *>(poSrc->GetRasterBand(nBand));
+
+ // Check the datatype and structure of the bands
+ // This is a sanity check, the source is lazy opened
+ if (b == nullptr || poSrc->GetRasterCount() != poMRFDS->nBands ||
+ poSrc->GetRasterXSize() != poMRFDS->full.size.x ||
+ poSrc->GetRasterYSize() != poMRFDS->full.size.y)
+ {
+ CPLError(CE_Failure, CPLE_AppDefined,
+ "MRF: Cloned source %s doesn't have the same structure",
+ poMRFDS->source.c_str());
+ return CE_Failure;
+ }
+
+ // Switch to overview if needed
if (b->GetOverviewCount() && m_l)
b = static_cast<MRFRasterBand *>(b->GetOverview(m_l - 1));
if (b == nullptr)
return CE_Failure;
+
+ // One last check, blocksize and type matches
+ int bsx, bsy;
+ b->GetBlockSize(&bsx, &bsy);
+ if (bsx != img.pagesize.x || bsy != img.pagesize.y ||
+ b->GetRasterDataType() != eDataType)
+ {
+ return CE_Failure;
+ }
+
return b->IReadBlock(xblk, yblk, buffer);
}
diff --git a/frmts/mrf/mrf_util.cpp b/frmts/mrf/mrf_util.cpp
index b2b7b2bcd5a4..75cc83618b4a 100644
--- a/frmts/mrf/mrf_util.cpp
+++ b/frmts/mrf/mrf_util.cpp
@@ -679,6 +679,10 @@ void GDALRegister_MRF()
"optimization\n"
#if defined(BRUNSLI)
" JFIF - boolean, for JPEG, disable brunsli encoding\n"
+#endif
+#if defined(QB3_SUPPORT)
+ " QB3_BAND_MAP - space separted list of band indices for "
+ "band to band decorrelation\n"
#endif
"'/>"
"</CreationOptionList>\n");https://github.com/OSGeo/gdal/commit/e2b913be67cda104a84aa1f9449cd9479707f4d0
Dates from discovery through public reveal.
- 2026-03-24 Reported to tracker
- 2026-04-29 Sent to maintainer
- 2026-05-08 Patch released
- 2026-05-12 Maintainer acknowledged
- 2026-07-08 Publicly revealed
SHA-3-512 hash:
7d3067339b8ed530b7bd76b9cabeae8e1570bc0e7f51294169a287f0f5686654291c6668fb791dd4748cf626bb3a2d0d0f79476c256da73a6ef7f07446d06d9c
Committed 2026-05-07 03:17 PT
Revealed 2026-07-08 16:46 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-K3APEMB5",
"bug_class": "Heap-buffer-overflow",
"claude_severity": "critical",
"commit_sha": null,
"created_at": "2026-03-24T18:28:27+00:00",
"description": "The libFuzzer harness mrf_fuzzer was executed against a single reproducer (/tmp/poc) and exited with status 1, but the log contains no AddressSanitizer/UBSan diagnostic, no crashing function, and no stack frames. Without a sanitizer tag or backtrace, the crash type, access shape (READ/WRITE, size), and source location cannot be determined from this output alone.",
"discovered_at": null,
"location": null,
"poc_sha256": null,
"preimage_version": 1,
"project": "gdal",
"reproduction": null,
"technical_details": null,
"title": "Heap-buffer-overflow in gdal",
"vendor_severity": "high"
}