Skip to content
This repository was archived by the owner on Jun 1, 2022. It is now read-only.

Commit c29ae0d

Browse files
committed
Merge pull request #68 from maxmind/greg/do-not-follow-invalid-pointers
Do not follow invalid pointers when traversing search tree
2 parents bb2153f + fda002b commit c29ae0d

File tree

7 files changed

+109
-35
lines changed

7 files changed

+109
-35
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
sudo: false
2+
13
language: c
24

35
compiler:

ChangeLog

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
* Fixed a MSVC parser stack overflow when parsing `regionName.c` and
44
`timeZone.c`. Fix by elliotlo. GitHub #64.
55
* Updated region codes and timezones.
6+
* When using `GEOIP_MEMORY_CACHE` with an invalid database file, the search
7+
tree traversal could attempt to read memory outside of the memory allocated
8+
for the memory cache, resulting in a segmentation fault. A check was added
9+
to ensure that the traversal code does not try to read beyond the end of the
10+
file, whether in memory, memory mapped, or on disk.
11+
* Previously the return values from file reads were ignored. We now check
12+
these values to ensure that there were no errors.
613

714

815
1.6.6 2015-07-28

apps/geoiplookup6.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ geoiplookup(GeoIP * gi, char *hostname, int i)
150150
printf("%s: IP Address not found\n", GeoIPDBDescription[i]);
151151
}else {
152152
printf("%s: %s\n", GeoIPDBDescription[i], asnum_name);
153-
// _say_range_by_ip(gi, ipnum);
154153
}
155154
}else if (GEOIP_CITY_EDITION_REV0_V6 == i) {
156155
gir = GeoIP_record_by_ipnum_v6(gi, ipnum);

libGeoIP/GeoIP.c

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ char * _GeoIP_iso_8859_1__utf8(const char * iso)
872872
if (p) {
873873
while ( ( c = *iso++ ) ) {
874874
if (c < 0) {
875-
k = 0xc2;
875+
k = (char) 0xc2;
876876
if (c >= -64) {
877877
k++;
878878
}
@@ -959,7 +959,7 @@ static void _setup_segments(GeoIP * gi)
959959
int i, j, segment_record_length;
960960
unsigned char delim[3];
961961
unsigned char buf[LARGE_SEGMENT_RECORD_LENGTH];
962-
ssize_t silence _UNUSED;
962+
963963
int fno = fileno(gi->GeoIPDatabase);
964964

965965
gi->databaseSegments = NULL;
@@ -971,9 +971,13 @@ static void _setup_segments(GeoIP * gi)
971971
return;
972972
}
973973
for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
974-
silence = read(fno, delim, 3);
974+
if (read(fno, delim, 3) != 3) {
975+
return;
976+
}
975977
if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) {
976-
silence = read(fno, &gi->databaseType, 1 );
978+
if (read(fno, &gi->databaseType, 1) != 1) {
979+
return;
980+
}
977981
if (gi->databaseType >= 106) {
978982
/* backwards compatibility with databases from April 2003 and earlier */
979983
gi->databaseType -= 105;
@@ -1027,7 +1031,12 @@ static void _setup_segments(GeoIP * gi)
10271031
gi->databaseSegments[0] = 0;
10281032
segment_record_length = SEGMENT_RECORD_LENGTH;
10291033

1030-
silence = read(fno, buf, segment_record_length );
1034+
if (read(fno, buf,
1035+
segment_record_length) != segment_record_length) {
1036+
free(gi->databaseSegments);
1037+
gi->databaseSegments = NULL;
1038+
return;
1039+
}
10311040
for (j = 0; j < segment_record_length; j++) {
10321041
gi->databaseSegments[0] += (buf[j] << (j * 8));
10331042
}
@@ -1206,7 +1215,7 @@ void _check_mtime(GeoIP *gi)
12061215
gi->index_cache, sizeof(unsigned char) * idx_size );
12071216
if (gi->index_cache != NULL) {
12081217
if (pread(fileno(gi->GeoIPDatabase), gi->index_cache,
1209-
idx_size, 0 ) != idx_size) {
1218+
idx_size, 0) != idx_size) {
12101219
DEBUG_MSGF(
12111220
gi->flags,
12121221
"Error reading file %s where reloading\n",
@@ -1234,22 +1243,31 @@ unsigned int _GeoIP_seek_record_v6_gl(GeoIP *gi, geoipv6_t ipnum,
12341243

12351244
const unsigned char * p;
12361245
int j;
1237-
ssize_t silence _UNUSED;
12381246
int fno = fileno(gi->GeoIPDatabase);
1247+
1248+
unsigned int record_pair_length = gi->record_length * 2;
1249+
12391250
_check_mtime(gi);
12401251
if (GeoIP_teredo(gi) ) {
12411252
__GEOIP_PREPARE_TEREDO(&ipnum);
12421253
}
12431254
for (depth = 127; depth >= 0; depth--) {
1255+
unsigned int byte_offset = record_pair_length * offset;
1256+
if (byte_offset > gi->size - record_pair_length) {
1257+
/* The pointer is invalid */
1258+
break;
1259+
}
12441260
if (gi->cache == NULL && gi->index_cache == NULL) {
12451261
/* read from disk */
1246-
silence = pread(fno, stack_buffer, gi->record_length * 2,
1247-
(long)gi->record_length * 2 * offset );
1262+
if (pread(fno, stack_buffer, record_pair_length,
1263+
(long)byte_offset) != record_pair_length) {
1264+
break;
1265+
}
12481266
} else if (gi->index_cache == NULL) {
12491267
/* simply point to record in memory */
1250-
buf = gi->cache + (long)gi->record_length * 2 * offset;
1268+
buf = gi->cache + (long)byte_offset;
12511269
} else {
1252-
buf = gi->index_cache + (long)gi->record_length * 2 * offset;
1270+
buf = gi->index_cache + (long)byte_offset;
12531271
}
12541272

12551273
if (GEOIP_CHKBIT_V6(depth, ipnum.s6_addr )) {
@@ -1325,22 +1343,31 @@ unsigned int _GeoIP_seek_record_gl(GeoIP *gi, unsigned long ipnum,
13251343
unsigned char stack_buffer[2 * MAX_RECORD_LENGTH];
13261344
const unsigned char *buf = (gi->cache == NULL) ? stack_buffer : NULL;
13271345
unsigned int offset = 0;
1328-
ssize_t silence _UNUSED;
13291346

13301347
const unsigned char * p;
13311348
int j;
13321349
int fno = fileno(gi->GeoIPDatabase);
1350+
1351+
unsigned int record_pair_length = gi->record_length * 2;
1352+
13331353
_check_mtime(gi);
13341354
for (depth = 31; depth >= 0; depth--) {
1355+
unsigned int byte_offset = record_pair_length * offset;
1356+
if (byte_offset > gi->size - record_pair_length) {
1357+
/* The pointer is invalid */
1358+
break;
1359+
}
13351360
if (gi->cache == NULL && gi->index_cache == NULL) {
13361361
/* read from disk */
1337-
silence = pread(fno, stack_buffer, gi->record_length * 2,
1338-
gi->record_length * 2 * offset);
1362+
if (pread(fno, stack_buffer, record_pair_length,
1363+
byte_offset) != record_pair_length) {
1364+
break;
1365+
}
13391366
} else if (gi->index_cache == NULL) {
13401367
/* simply point to record in memory */
1341-
buf = gi->cache + (long)gi->record_length * 2 * offset;
1368+
buf = gi->cache + (long)byte_offset;
13421369
} else {
1343-
buf = gi->index_cache + (long)gi->record_length * 2 * offset;
1370+
buf = gi->index_cache + (long)byte_offset;
13441371
}
13451372

13461373
if (ipnum & (1 << depth)) {
@@ -1508,9 +1535,10 @@ GeoIP * GeoIP_open(const char * filename, int flags)
15081535
free(gi);
15091536
return NULL;
15101537
}
1538+
1539+
gi->size = buf.st_size;
15111540
if (flags & (GEOIP_MEMORY_CACHE | GEOIP_MMAP_CACHE) ) {
15121541
gi->mtime = buf.st_mtime;
1513-
gi->size = buf.st_size;
15141542

15151543
/* MMAP added my Peter Shipley */
15161544
if (flags & GEOIP_MMAP_CACHE) {
@@ -1999,7 +2027,7 @@ char *GeoIP_database_info(GeoIP * gi)
19992027
unsigned char buf[3];
20002028
char *retval;
20012029
int hasStructureInfo = 0;
2002-
ssize_t silence _UNUSED;
2030+
20032031
int fno;
20042032

20052033
if (gi == NULL) {
@@ -2015,7 +2043,9 @@ char *GeoIP_database_info(GeoIP * gi)
20152043

20162044
/* first get past the database structure information */
20172045
for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
2018-
silence = read(fno, buf, 3 );
2046+
if (read(fno, buf, 3 ) != 3) {
2047+
return NULL;
2048+
}
20192049
if (buf[0] == 255 && buf[1] == 255 && buf[2] == 255) {
20202050
hasStructureInfo = 1;
20212051
break;
@@ -2036,13 +2066,17 @@ char *GeoIP_database_info(GeoIP * gi)
20362066
}
20372067

20382068
for (i = 0; i < DATABASE_INFO_MAX_SIZE; i++) {
2039-
silence = read(fno, buf, 3 );
2069+
if (read(fno, buf, 3 ) != 3) {
2070+
return NULL;
2071+
}
20402072
if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0) {
20412073
retval = malloc(sizeof(char) * (i + 1));
20422074
if (retval == NULL) {
20432075
return NULL;
20442076
}
2045-
silence = read(fno, retval, i);
2077+
if (read(fno, retval, i) != i) {
2078+
return NULL;
2079+
}
20462080
retval[i] = '\0';
20472081
return retval;
20482082
}
@@ -2310,7 +2344,6 @@ char *_get_name_gl(GeoIP * gi, unsigned long ipnum, GeoIPLookup * gl)
23102344
char * org_buf, * buf_pointer;
23112345
int record_pointer;
23122346
size_t len;
2313-
ssize_t silence _UNUSED;
23142347

23152348
if (gi->databaseType != GEOIP_ORG_EDITION &&
23162349
gi->databaseType != GEOIP_ISP_EDITION &&
@@ -2341,9 +2374,10 @@ char *_get_name_gl(GeoIP * gi, unsigned long ipnum, GeoIPLookup * gl)
23412374
(2 * gi->record_length - 1) * gi->databaseSegments[0];
23422375

23432376
if (gi->cache == NULL) {
2344-
silence = pread(fileno(
2345-
gi->GeoIPDatabase), buf, MAX_ORG_RECORD_LENGTH,
2346-
record_pointer);
2377+
if (pread(fileno(gi->GeoIPDatabase), buf, MAX_ORG_RECORD_LENGTH,
2378+
record_pointer) == -1) {
2379+
return NULL;
2380+
}
23472381
if (gi->charset == GEOIP_CHARSET_UTF8) {
23482382
org_buf = _GeoIP_iso_8859_1__utf8( (const char * )buf );
23492383
} else {
@@ -2372,7 +2406,6 @@ char *_get_name_v6_gl(GeoIP * gi, geoipv6_t ipnum, GeoIPLookup * gl)
23722406
char * org_buf, * buf_pointer;
23732407
int record_pointer;
23742408
size_t len;
2375-
ssize_t silence _UNUSED;
23762409

23772410
if (
23782411
gi->databaseType != GEOIP_ORG_EDITION_V6 &&
@@ -2400,9 +2433,10 @@ char *_get_name_v6_gl(GeoIP * gi, geoipv6_t ipnum, GeoIPLookup * gl)
24002433
(2 * gi->record_length - 1) * gi->databaseSegments[0];
24012434

24022435
if (gi->cache == NULL) {
2403-
silence = pread(fileno(
2404-
gi->GeoIPDatabase), buf, MAX_ORG_RECORD_LENGTH,
2405-
record_pointer);
2436+
if (pread(fileno(gi->GeoIPDatabase), buf, MAX_ORG_RECORD_LENGTH,
2437+
record_pointer) == -1) {
2438+
return NULL;
2439+
}
24062440
buf[MAX_ORG_RECORD_LENGTH] = 0;
24072441
if (gi->charset == GEOIP_CHARSET_UTF8) {
24082442
org_buf = _GeoIP_iso_8859_1__utf8( (const char * )buf );

libGeoIP/GeoIPCity.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ _extract_record(GeoIP * gi, unsigned int seek_record, int *next_record_ptr)
8989
bytes_read = pread(fileno(
9090
gi->GeoIPDatabase), record_buf,
9191
FULL_RECORD_LENGTH, record_pointer);
92-
if (bytes_read == 0) {
92+
if (bytes_read <= 0) {
9393
/* eof or other error */
9494
free(begin_record_buf);
9595
free(record);

test/Makefile.am

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
check_PROGRAMS = test-geoip
1+
check_PROGRAMS = test-geoip test-geoip-invalid-file
22

33
noinst_PROGRAMS = benchmark test-geoip-region test-geoip-city test-geoip-org test-geoip-domain test-geoip-asnum test-geoip-isp test-geoip-netspeed
44

@@ -9,13 +9,15 @@ EXTRA_PROGRAMS = benchmark \
99
test-geoip-domain \
1010
test-geoip-asnum \
1111
test-geoip-isp \
12-
test-geoip-netspeed
12+
test-geoip-netspeed
1313

1414
LDADD = $(top_builddir)/libGeoIP/libGeoIP.la
1515
AM_CPPFLAGS = -I$(top_srcdir)/libGeoIP -DSRCDIR=\"$(top_srcdir)\" -Wall
1616

1717
test_geoip_SOURCES = test-geoip.c
1818

19+
test_geoip_invalid_file_SOURCES = test-geoip-invalid-file.c
20+
1921
test_geoip_region_SOURCES = test-geoip-region.c
2022

2123
test_geoip_org_SOURCES = test-geoip-org.c
@@ -32,5 +34,5 @@ test_geoip_city_SOURCES = test-geoip-city.c
3234

3335
benchmark_SOURCES = benchmark.c
3436

35-
EXTRA_DIST = Makefile.vc city_test.txt country_test.txt country_test2.txt country_test_name.txt region_test.txt
36-
TESTS = test-geoip
37+
EXTRA_DIST = Makefile.vc city_test.txt country_test.txt country_test2.txt country_test_name.txt region_test.txt
38+
TESTS = test-geoip test-geoip-invalid-file

test/test-geoip-invalid-file.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "GeoIP.h"
2+
3+
int main()
4+
{
5+
GeoIP *gi = GeoIP_open(SRCDIR "/README.md", GEOIP_MEMORY_CACHE);
6+
7+
/* We don't detect invalid files at load, unfortunately. */
8+
if (gi == NULL) {
9+
fprintf(stderr, "Error opening database\n");
10+
return 1;
11+
}
12+
13+
const char *country = GeoIP_country_code_by_addr(gi, "24.24.24.24");
14+
if (country != NULL) {
15+
fprintf(
16+
stderr,
17+
"Received a non-NULL value on an invalid database from GeoIP_country_code_by_addr\n");
18+
return 1;
19+
}
20+
21+
country = GeoIP_country_code_by_addr_v6(gi, "24.24.24.24");
22+
if (country != NULL) {
23+
fprintf(
24+
stderr,
25+
"Received a non-NULL value on an invalid database from GeoIP_country_code_by_addr_v6\n");
26+
return 1;
27+
}
28+
29+
return 0;
30+
}

0 commit comments

Comments
 (0)