Skip to content

Commit 93d2b6c

Browse files
committed
Reject empty metadata sections
1 parent 712cd42 commit 93d2b6c

5 files changed

Lines changed: 88 additions & 2 deletions

File tree

Changes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
`sockaddr` with an unsupported address family. The function now rejects any
55
family other than `AF_INET` and `AF_INET6` with
66
`MMDB_INVALID_NETWORK_ADDRESS_ERROR`.
7+
- Fixed metadata parsing for files that end immediately after the
8+
`\xAB\xCD\xEFMaxMind.com` marker. Such files are now rejected as invalid
9+
metadata instead of allowing a zero-length metadata section to reach the
10+
decoder.
711

812
## 1.13.3 - 2026-03-05
913

src/maxminddb.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ static const uint8_t *find_metadata(const uint8_t *file_content,
518518
}
519519
} while (NULL != tmp);
520520

521-
if (search_area == start) {
521+
if (search_area == start || max_size <= 0) {
522522
return NULL;
523523
}
524524

@@ -1431,6 +1431,13 @@ static int decode_one(const MMDB_s *const mmdb,
14311431
MMDB_entry_data_s *entry_data) {
14321432
const uint8_t *mem = mmdb->data_section;
14331433

1434+
if (mmdb->data_section_size == 0) {
1435+
// decode_one is also called with a fake mmdb whose data_section
1436+
// points at the metadata; either way an empty section is invalid.
1437+
DEBUG_MSG("decode_one called with an empty section");
1438+
return MMDB_INVALID_DATA_ERROR;
1439+
}
1440+
14341441
// We subtract rather than add as it possible that offset + 1
14351442
// could overflow for a corrupt database while an underflow
14361443
// from data_section_size - 1 should not be possible.

t/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ set(TEST_TARGET_NAMES
1717
get_value_t
1818
ipv4_start_cache_t
1919
ipv6_lookup_in_ipv4_t
20+
metadata_marker_t
2021
metadata_pointers_t
2122
metadata_t
2223
no_map_get_value_t

t/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ check_PROGRAMS = \
2323
gai_error_t get_value_t \
2424
get_value_pointer_bug_t invalid_sockaddr_t \
2525
ipv4_start_cache_t ipv6_lookup_in_ipv4_t max_depth_t metadata_t \
26-
metadata_pointers_t no_map_get_value_t overflow_bounds_t read_node_t \
26+
metadata_marker_t metadata_pointers_t no_map_get_value_t \
27+
overflow_bounds_t read_node_t \
2728
threads_t version_t
2829

2930
data_pool_t_LDFLAGS = $(AM_LDFLAGS) -lm

t/metadata_marker_t.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Required for mkstemp visibility under -D_POSIX_C_SOURCE=200112L on glibc.
2+
#define _XOPEN_SOURCE 500
3+
4+
#include "maxminddb_test_helper.h"
5+
6+
#include <errno.h>
7+
#include <stdio.h>
8+
#include <string.h>
9+
10+
#ifdef _WIN32
11+
#include <io.h>
12+
#define unlink _unlink
13+
#else
14+
#include <unistd.h>
15+
#endif
16+
17+
static FILE *open_temp_file(char *path, size_t path_size) {
18+
#ifdef _WIN32
19+
errno_t err = tmpnam_s(path, path_size);
20+
if (err != 0) {
21+
return NULL;
22+
}
23+
return fopen(path, "wb");
24+
#else
25+
snprintf(path, path_size, "./metadata-marker-XXXXXX");
26+
int fd = mkstemp(path);
27+
if (fd < 0) {
28+
return NULL;
29+
}
30+
return fdopen(fd, "wb");
31+
#endif
32+
}
33+
34+
static void test_trailing_metadata_marker(void) {
35+
char temp_path[64];
36+
FILE *temp = open_temp_file(temp_path, sizeof(temp_path));
37+
if (!temp) {
38+
BAIL_OUT("could not create temp file: %s", strerror(errno));
39+
}
40+
41+
static const unsigned char metadata_marker[] = "\xab\xcd\xefMaxMind.com";
42+
size_t expected = sizeof(metadata_marker) - 1;
43+
if (fwrite(metadata_marker, 1, expected, temp) != expected) {
44+
int saved_errno = errno;
45+
fclose(temp);
46+
unlink(temp_path);
47+
BAIL_OUT("fwrite failed: %s", strerror(saved_errno));
48+
}
49+
if (fclose(temp) != 0) {
50+
int saved_errno = errno;
51+
unlink(temp_path);
52+
BAIL_OUT("fclose failed: %s", strerror(saved_errno));
53+
}
54+
55+
MMDB_s mmdb;
56+
int status = MMDB_open(temp_path, MMDB_MODE_MMAP, &mmdb);
57+
cmp_ok(status,
58+
"==",
59+
MMDB_INVALID_METADATA_ERROR,
60+
"MMDB_open rejects a trailing metadata marker with no metadata");
61+
62+
if (status == MMDB_SUCCESS) {
63+
MMDB_close(&mmdb);
64+
}
65+
66+
unlink(temp_path);
67+
}
68+
69+
int main(void) {
70+
plan(NO_PLAN);
71+
test_trailing_metadata_marker();
72+
done_testing();
73+
}

0 commit comments

Comments
 (0)