From 9f244cea8d6c7e589e3ad91a7d37d183828ae72b Mon Sep 17 00:00:00 2001 From: alhudz Date: Wed, 10 Jun 2026 11:15:01 +0530 Subject: [PATCH 1/2] fix oob read on malformed length field in dba flatfile handler --- ext/dba/libflatfile/flatfile.c | 24 ++++++++++++++++++++++ ext/dba/tests/dba_flatfile_oob.phpt | 31 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 ext/dba/tests/dba_flatfile_oob.phpt diff --git a/ext/dba/libflatfile/flatfile.c b/ext/dba/libflatfile/flatfile.c index 561766777f6f..c6192351d1c6 100644 --- a/ext/dba/libflatfile/flatfile.c +++ b/ext/dba/libflatfile/flatfile.c @@ -112,6 +112,9 @@ int flatfile_delete(flatfile *dba, datum key_datum) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } @@ -135,6 +138,9 @@ int flatfile_delete(flatfile *dba, datum key_datum) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } @@ -162,6 +168,9 @@ int flatfile_findkey(flatfile *dba, datum key_datum) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } @@ -178,6 +187,9 @@ int flatfile_findkey(flatfile *dba, datum key_datum) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } @@ -202,6 +214,9 @@ datum flatfile_firstkey(flatfile *dba) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } @@ -218,6 +233,9 @@ datum flatfile_firstkey(flatfile *dba) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } @@ -244,6 +262,9 @@ datum flatfile_nextkey(flatfile *dba) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } @@ -254,6 +275,9 @@ datum flatfile_nextkey(flatfile *dba) { } num = atoi(buf); if (num >= buf_size) { + if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { + break; + } buf_size = num + FLATFILE_BLOCK_SIZE; buf = erealloc(buf, buf_size); } diff --git a/ext/dba/tests/dba_flatfile_oob.phpt b/ext/dba/tests/dba_flatfile_oob.phpt new file mode 100644 index 000000000000..3328e1dcba90 --- /dev/null +++ b/ext/dba/tests/dba_flatfile_oob.phpt @@ -0,0 +1,31 @@ +--TEST-- +DBA FlatFile handler bounds with a malformed (negative) length field +--EXTENSIONS-- +dba +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(false) +bool(false) +bool(false) +done From 647dab2c81f12b627c013284386144a79e7b1497 Mon Sep 17 00:00:00 2001 From: alhudz Date: Fri, 19 Jun 2026 17:46:00 +0530 Subject: [PATCH 2/2] ext/dba: fold repeated flatfile length guard into a macro --- ext/dba/libflatfile/flatfile.c | 84 ++++++++++++---------------------- 1 file changed, 28 insertions(+), 56 deletions(-) diff --git a/ext/dba/libflatfile/flatfile.c b/ext/dba/libflatfile/flatfile.c index c6192351d1c6..a5a9bf70c23b 100644 --- a/ext/dba/libflatfile/flatfile.c +++ b/ext/dba/libflatfile/flatfile.c @@ -35,6 +35,18 @@ #define FLATFILE_BLOCK_SIZE 1024 +/* Parse the length prefix in `buf` into `num` and grow `buf` to hold it. + * atoi() narrows a malformed (e.g. negative) length to a huge size_t whose + * `+ FLATFILE_BLOCK_SIZE` would overflow erealloc(); the macro yields true in + * that case so the caller stops reading and the read stays within `buf_size`. */ +#define FLATFILE_GROW_BUF(num, buf, buf_size) ( \ + (num) = atoi(buf), \ + (num) >= (buf_size) && ( \ + (num) > SIZE_MAX - FLATFILE_BLOCK_SIZE \ + || ((buf) = erealloc((buf), (buf_size) = (num) + FLATFILE_BLOCK_SIZE), 0) \ + ) \ +) + /* * ret = -1 means that database was opened for read-only * ret = 0 success @@ -110,13 +122,8 @@ int flatfile_delete(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } pos = php_stream_tell(dba->fp); @@ -136,13 +143,8 @@ int flatfile_delete(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } /* read in the value */ num = php_stream_read(dba->fp, buf, num); @@ -166,13 +168,8 @@ int flatfile_findkey(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); @@ -185,13 +182,8 @@ int flatfile_findkey(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); } @@ -212,13 +204,8 @@ datum flatfile_firstkey(flatfile *dba) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); @@ -231,13 +218,8 @@ datum flatfile_firstkey(flatfile *dba) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); } @@ -260,26 +242,16 @@ datum flatfile_nextkey(flatfile *dba) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - if (num > SIZE_MAX - FLATFILE_BLOCK_SIZE) { - break; - } - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num);