@@ -89,7 +89,16 @@ static int copy_file(FILE *dest, const char *source_path) {
8989 return result ;
9090}
9191
92- void test_separator_record_rejected (void ) {
92+ // Mutate node 0 of a copy of the IPv4 test database so that one of its
93+ // 24-bit records holds `record_value`, then assert that all corruption-
94+ // rejection paths fire. `record_offset` is 0 for the left record or 3
95+ // for the right record. `lookup_ip` must traverse the corrupted record
96+ // from node 0 (high bit 0 for left, 1 for right). `label` identifies
97+ // the case in TAP output.
98+ static void check_corrupt_record (const char * label ,
99+ long record_offset ,
100+ unsigned char record_value_be [3 ],
101+ const char * lookup_ip ) {
93102 char * db_file = test_database_path ("MaxMind-DB-test-ipv4-24.mmdb" );
94103 char temp_path [64 ];
95104 FILE * temp = open_temp_file (temp_path , sizeof (temp_path ));
@@ -105,16 +114,13 @@ void test_separator_record_rejected(void) {
105114 BAIL_OUT ("could not copy test database: %s" , strerror (errno ));
106115 }
107116
108- // Overwrite node 0's left record with node_count + 1, which points into
109- // the 16-byte separator between the search tree and data section.
110- if (fseek (temp , 0 , SEEK_SET ) != 0 ) {
117+ if (fseek (temp , record_offset , SEEK_SET ) != 0 ) {
111118 fclose (temp );
112119 unlink (temp_path );
113120 free (db_file );
114121 BAIL_OUT ("fseek failed: %s" , strerror (errno ));
115122 }
116- unsigned char bad_record [3 ] = {0x00 , 0x00 , 0xA4 };
117- if (fwrite (bad_record , 1 , sizeof (bad_record ), temp ) != sizeof (bad_record )) {
123+ if (fwrite (record_value_be , 1 , 3 , temp ) != 3 ) {
118124 fclose (temp );
119125 unlink (temp_path );
120126 free (db_file );
@@ -128,7 +134,7 @@ void test_separator_record_rejected(void) {
128134
129135 MMDB_s mmdb ;
130136 int status = MMDB_open (temp_path , MMDB_MODE_MMAP , & mmdb );
131- cmp_ok (status , "==" , MMDB_SUCCESS , "opened crafted bad search tree MMDB" );
137+ cmp_ok (status , "==" , MMDB_SUCCESS , "%s: opened crafted bad MMDB" , label );
132138 if (status != MMDB_SUCCESS ) {
133139 unlink (temp_path );
134140 free (db_file );
@@ -137,29 +143,48 @@ void test_separator_record_rejected(void) {
137143
138144 MMDB_search_node_s node ;
139145 status = MMDB_read_node (& mmdb , 0 , & node );
140- cmp_ok (
141- status , "==" , MMDB_SUCCESS , "MMDB_read_node succeeds for crafted node" );
142- cmp_ok (node .left_record_type ,
146+ cmp_ok (status ,
143147 "==" ,
144- MMDB_RECORD_TYPE_INVALID ,
145- "MMDB_read_node marks separator record as invalid" );
148+ MMDB_CORRUPT_SEARCH_TREE_ERROR ,
149+ "%s: MMDB_read_node rejects record as corrupt" ,
150+ label );
146151
147152 int gai_error = 0 ;
148153 int mmdb_error = 0 ;
149154 MMDB_lookup_result_s result =
150- MMDB_lookup_string (& mmdb , "1.1.1.1" , & gai_error , & mmdb_error );
151- cmp_ok (gai_error , "==" , 0 , "lookup string parse succeeds" );
155+ MMDB_lookup_string (& mmdb , lookup_ip , & gai_error , & mmdb_error );
156+ cmp_ok (gai_error , "==" , 0 , "%s: lookup string parse succeeds" , label );
152157 cmp_ok (mmdb_error ,
153158 "==" ,
154159 MMDB_CORRUPT_SEARCH_TREE_ERROR ,
155- "MMDB_lookup_string rejects records pointing into the separator" );
156- ok (!result .found_entry , "lookup does not report an entry for corrupt tree" );
160+ "%s: MMDB_lookup_string rejects record" ,
161+ label );
162+ ok (!result .found_entry , "%s: lookup reports no entry" , label );
157163
158164 MMDB_close (& mmdb );
159165 unlink (temp_path );
160166 free (db_file );
161167}
162168
169+ void test_separator_record_rejected (void ) {
170+ // The IPv4 test database has node_count == 163 (0xA3). Records in the
171+ // half-open range [node_count + 1, node_count + 16) point into the
172+ // 16-byte data section separator and must be rejected as corrupt.
173+ unsigned char first_separator_byte [3 ] = {0x00 , 0x00 , 0xA4 };
174+ unsigned char last_separator_byte [3 ] = {0x00 , 0x00 , 0xB2 };
175+
176+ check_corrupt_record ("left record, first separator byte" ,
177+ 0 ,
178+ first_separator_byte ,
179+ "1.1.1.1" );
180+ check_corrupt_record ("right record, first separator byte" ,
181+ 3 ,
182+ first_separator_byte ,
183+ "128.0.0.0" );
184+ check_corrupt_record (
185+ "left record, last separator byte" , 0 , last_separator_byte , "1.1.1.1" );
186+ }
187+
163188int main (void ) {
164189 plan (NO_PLAN );
165190 test_read_node_bounds ();
0 commit comments