@@ -92,7 +92,16 @@ static int copy_file(FILE *dest, const char *source_path) {
9292 return result ;
9393}
9494
95- void test_separator_record_rejected (void ) {
95+ // Mutate node 0 of a copy of the IPv4 test database so that one of its
96+ // 24-bit records holds `record_value`, then assert that all corruption-
97+ // rejection paths fire. `record_offset` is 0 for the left record or 3
98+ // for the right record. `lookup_ip` must traverse the corrupted record
99+ // from node 0 (high bit 0 for left, 1 for right). `label` identifies
100+ // the case in TAP output.
101+ static void check_corrupt_record (const char * label ,
102+ long record_offset ,
103+ unsigned char record_value_be [3 ],
104+ const char * lookup_ip ) {
96105 char * db_file = test_database_path ("MaxMind-DB-test-ipv4-24.mmdb" );
97106 char temp_path [64 ];
98107 FILE * temp = open_temp_file (temp_path , sizeof (temp_path ));
@@ -108,16 +117,13 @@ void test_separator_record_rejected(void) {
108117 BAIL_OUT ("could not copy test database: %s" , strerror (errno ));
109118 }
110119
111- // Overwrite node 0's left record with node_count + 1, which points into
112- // the 16-byte separator between the search tree and data section.
113- if (fseek (temp , 0 , SEEK_SET ) != 0 ) {
120+ if (fseek (temp , record_offset , SEEK_SET ) != 0 ) {
114121 fclose (temp );
115122 unlink (temp_path );
116123 free (db_file );
117124 BAIL_OUT ("fseek failed: %s" , strerror (errno ));
118125 }
119- unsigned char bad_record [3 ] = {0x00 , 0x00 , 0xA4 };
120- if (fwrite (bad_record , 1 , sizeof (bad_record ), temp ) != sizeof (bad_record )) {
126+ if (fwrite (record_value_be , 1 , 3 , temp ) != 3 ) {
121127 fclose (temp );
122128 unlink (temp_path );
123129 free (db_file );
@@ -131,7 +137,7 @@ void test_separator_record_rejected(void) {
131137
132138 MMDB_s mmdb ;
133139 int status = MMDB_open (temp_path , MMDB_MODE_MMAP , & mmdb );
134- cmp_ok (status , "==" , MMDB_SUCCESS , "opened crafted bad search tree MMDB" );
140+ cmp_ok (status , "==" , MMDB_SUCCESS , "%s: opened crafted bad MMDB" , label );
135141 if (status != MMDB_SUCCESS ) {
136142 unlink (temp_path );
137143 free (db_file );
@@ -140,29 +146,48 @@ void test_separator_record_rejected(void) {
140146
141147 MMDB_search_node_s node ;
142148 status = MMDB_read_node (& mmdb , 0 , & node );
143- cmp_ok (
144- status , "==" , MMDB_SUCCESS , "MMDB_read_node succeeds for crafted node" );
145- cmp_ok (node .left_record_type ,
149+ cmp_ok (status ,
146150 "==" ,
147- MMDB_RECORD_TYPE_INVALID ,
148- "MMDB_read_node marks separator record as invalid" );
151+ MMDB_CORRUPT_SEARCH_TREE_ERROR ,
152+ "%s: MMDB_read_node rejects record as corrupt" ,
153+ label );
149154
150155 int gai_error = 0 ;
151156 int mmdb_error = 0 ;
152157 MMDB_lookup_result_s result =
153- MMDB_lookup_string (& mmdb , "1.1.1.1" , & gai_error , & mmdb_error );
154- cmp_ok (gai_error , "==" , 0 , "lookup string parse succeeds" );
158+ MMDB_lookup_string (& mmdb , lookup_ip , & gai_error , & mmdb_error );
159+ cmp_ok (gai_error , "==" , 0 , "%s: lookup string parse succeeds" , label );
155160 cmp_ok (mmdb_error ,
156161 "==" ,
157162 MMDB_CORRUPT_SEARCH_TREE_ERROR ,
158- "MMDB_lookup_string rejects records pointing into the separator" );
159- ok (!result .found_entry , "lookup does not report an entry for corrupt tree" );
163+ "%s: MMDB_lookup_string rejects record" ,
164+ label );
165+ ok (!result .found_entry , "%s: lookup reports no entry" , label );
160166
161167 MMDB_close (& mmdb );
162168 unlink (temp_path );
163169 free (db_file );
164170}
165171
172+ void test_separator_record_rejected (void ) {
173+ // The IPv4 test database has node_count == 163 (0xA3). Records in the
174+ // half-open range [node_count + 1, node_count + 16) point into the
175+ // 16-byte data section separator and must be rejected as corrupt.
176+ unsigned char first_separator_byte [3 ] = {0x00 , 0x00 , 0xA4 };
177+ unsigned char last_separator_byte [3 ] = {0x00 , 0x00 , 0xB2 };
178+
179+ check_corrupt_record ("left record, first separator byte" ,
180+ 0 ,
181+ first_separator_byte ,
182+ "1.1.1.1" );
183+ check_corrupt_record ("right record, first separator byte" ,
184+ 3 ,
185+ first_separator_byte ,
186+ "128.0.0.0" );
187+ check_corrupt_record (
188+ "left record, last separator byte" , 0 , last_separator_byte , "1.1.1.1" );
189+ }
190+
166191int main (void ) {
167192 plan (NO_PLAN );
168193 test_read_node_bounds ();
0 commit comments