diff --git a/Changes.md b/Changes.md index 29591851..ac58c430 100644 --- a/Changes.md +++ b/Changes.md @@ -1,3 +1,9 @@ +## 1.13.3 - 2026-XX-XX + +- Fixed validation of empty maps and arrays at the end of the metadata section. + `MMDB_open` would incorrectly reject databases where a 0-element map or array + was the last field in metadata. Reported in vimt/MaxMind-DB-Writer-python#16. + ## 1.13.2 - 2026-02-25 - Fixed a compilation failure on macOS 26 (Tahoe) where `sys/endian.h` defines diff --git a/src/maxminddb.c b/src/maxminddb.c index 66b69be2..5aa3b254 100644 --- a/src/maxminddb.c +++ b/src/maxminddb.c @@ -1729,7 +1729,7 @@ static int get_entry_data_list(const MMDB_s *const mmdb, uint32_t array_size = entry_data_list->entry_data.data_size; uint32_t array_offset = entry_data_list->entry_data.offset_to_next; /* Each array element needs at least 1 byte. */ - if (array_offset >= mmdb->data_section_size || + if (array_offset > mmdb->data_section_size || array_size > mmdb->data_section_size - array_offset) { DEBUG_MSG("array size exceeds remaining data section"); return MMDB_INVALID_DATA_ERROR; @@ -1758,7 +1758,7 @@ static int get_entry_data_list(const MMDB_s *const mmdb, offset = entry_data_list->entry_data.offset_to_next; /* Each map entry needs at least a key and a value (1 byte each). */ - if (offset >= mmdb->data_section_size || + if (offset > mmdb->data_section_size || size > (mmdb->data_section_size - offset) / 2) { DEBUG_MSG("map size exceeds remaining data section"); return MMDB_INVALID_DATA_ERROR; diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index 8ea56c44..5065cca8 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -31,6 +31,7 @@ if(UNIX) # or if (NOT WIN32) bad_data_size_t bad_epoch_t bad_indent_t + empty_container_metadata_t max_depth_t threads_t ) diff --git a/t/Makefile.am b/t/Makefile.am index 8468f868..a85c507d 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -19,7 +19,8 @@ check_PROGRAMS = \ bad_pointers_t bad_databases_t bad_data_size_t bad_epoch_t bad_indent_t \ bad_search_tree_t \ basic_lookup_t data_entry_list_t \ - data-pool-t data_types_t double_close_t dump_t gai_error_t get_value_t \ + data-pool-t data_types_t double_close_t dump_t empty_container_metadata_t \ + gai_error_t get_value_t \ get_value_pointer_bug_t \ ipv4_start_cache_t ipv6_lookup_in_ipv4_t max_depth_t metadata_t \ metadata_pointers_t no_map_get_value_t overflow_bounds_t read_node_t \ diff --git a/t/empty_container_metadata_t.c b/t/empty_container_metadata_t.c new file mode 100644 index 00000000..6ad3dc76 --- /dev/null +++ b/t/empty_container_metadata_t.c @@ -0,0 +1,42 @@ +#include "maxminddb_test_helper.h" + +static void test_db_opens_and_lookup_succeeds(const char *filename, + const char *open_msg) { + char *db_file = bad_database_path(filename); + + MMDB_s mmdb; + int status = MMDB_open(db_file, MMDB_MODE_MMAP, &mmdb); + cmp_ok(status, "==", MMDB_SUCCESS, open_msg); + + if (status != MMDB_SUCCESS) { + diag("MMDB_open failed: %s", MMDB_strerror(status)); + free(db_file); + return; + } + + int gai_error, mmdb_error; + MMDB_lookup_string(&mmdb, "1.2.3.4", &gai_error, &mmdb_error); + cmp_ok(mmdb_error, "==", MMDB_SUCCESS, "lookup succeeded"); + + MMDB_close(&mmdb); + free(db_file); +} + +void test_empty_map_last_in_metadata(void) { + test_db_opens_and_lookup_succeeds( + "libmaxminddb-empty-map-last-in-metadata.mmdb", + "opened MMDB with empty map at end of metadata"); +} + +void test_empty_array_last_in_metadata(void) { + test_db_opens_and_lookup_succeeds( + "libmaxminddb-empty-array-last-in-metadata.mmdb", + "opened MMDB with empty array at end of metadata"); +} + +int main(void) { + plan(NO_PLAN); + test_empty_map_last_in_metadata(); + test_empty_array_last_in_metadata(); + done_testing(); +} diff --git a/t/maxmind-db b/t/maxmind-db index 327e23aa..809dd1ca 160000 --- a/t/maxmind-db +++ b/t/maxmind-db @@ -1 +1 @@ -Subproject commit 327e23aaccd3873c7d92fd9a6076694c48d877cd +Subproject commit 809dd1ca37e5c9d3ebc63bf159f81ec332a4f08e