Skip to content

Issue with memory mapped GeoIP DB files in Linux? #7669

@pbchou

Description

@pbchou

Hi. I would appreciate any second opinions on this issue. I noticed that the current MaxMind enabled plugins (geoip_acl, header_rewrite, and maxmind_acl) all use the memory mapped method to open GeoIP DB files using the MaxMind libraries. The geoip_acl and header_rewrite plugins use the old geopi-api-c that allows either loading the DB into memory (GEOIP_MEMORY_CACHE) or using a memory map (GEOIP_MMAP_CACHE). The newer maxmind_acl plugin uses the newer libmaxminddb that only allows the memory map mode (MMDB_MODE_MMAP).

The potential issue is whether over-writing the DB files on the disk is visible to the MaxMind libraries. My testing on Linux says that it is although the Linux man page says that the behavior is unspecified --

 MAP_PRIVATE
              Create a private copy-on-write mapping.  Updates to the mapping are not visible to other processes  mapping  the
              same  file,  and  are not carried through to the underlying file.  It is unspecified whether changes made to the
              file after the mmap() call are visible in the mapped region.

Since changes are visible -- this can interfere with any in-progress look-ups when the over-write happens. In addition, if the newer DB file is larger than the older DB file, then pointers to areas of the file extending beyond the original file size may be considered by the MaxMind libraries to be corrupted DB pointers. I think the pointer vs size checking would prevent any segmentation faults at least. So in general, you would need to close and re-open the DB files if they are over-written on disk.

So should we --

  • consider a documentation update for this issue?
  • consider loading the DB files into memory?
  • is this an issue on other platforms?

Test program I used on Linux --

1. Create data file of "0" of 4096 x 5 size.

while true ; do echo -n "0" ; done | dd if=/dev/stdin of=test-0-05.dat iflag=fullblock bs=4096 count=5

2. Create date file of "1" of 4096 x 10 size.

while true ; do echo -n "1" ; done | dd if=/dev/stdin of=test-1-10.dat iflag=fullblock bs=4096 count=10

3. Test with mmap() with PROT_READ MAP_PRIVATE ( used in geoip-api-c ) --

System Page Size: 4096
Trial 00000:
Reached page:  0 address:      0 first-byte: 0
Reached page:  1 address:   4096 first-byte: 0
Reached page:  2 address:   8192 first-byte: 0
Reached page:  3 address:  12288 first-byte: 0
Reached page:  4 address:  16384 first-byte: 0
Length = 20480 last-byte-of-file = 0
Trial 00001:
Reached page:  0 address:      0 first-byte: 0
Reached page:  1 address:   4096 first-byte: 0
Reached page:  2 address:   8192 first-byte: 0
Reached page:  3 address:  12288 first-byte: 0
Reached page:  4 address:  16384 first-byte: 0
Length = 20480 last-byte-of-file = 0
--- replacing test.dat file : 0-file to 1-file ---
Trial 00002:
Reached page:  0 address:      0 first-byte: 1
Reached page:  1 address:   4096 first-byte: 1
Reached page:  2 address:   8192 first-byte: 1
Reached page:  3 address:  12288 first-byte: 1
Reached page:  4 address:  16384 first-byte: 1
Reached page:  5 address:  20480 first-byte: 
Reached page:  6 address:  24576 first-byte: h
Reached page:  7 address:  28672 first-byte: �
Segmentation fault (core dumped)

4. Test with mmap() with PROT_READ MAP_SHARED ( used in libmaxminddb ) --

System Page Size: 4096
Trial 00000:
Reached page:  0 address:      0 first-byte: 0
Reached page:  1 address:   4096 first-byte: 0
Reached page:  2 address:   8192 first-byte: 0
Reached page:  3 address:  12288 first-byte: 0
Reached page:  4 address:  16384 first-byte: 0
Length = 20480 last-byte-of-file = 0
Trial 00001:
Reached page:  0 address:      0 first-byte: 0
Reached page:  1 address:   4096 first-byte: 0
Reached page:  2 address:   8192 first-byte: 0
Reached page:  3 address:  12288 first-byte: 0
Reached page:  4 address:  16384 first-byte: 0
Length = 20480 last-byte-of-file = 0
--- replacing test.dat file : 0-file to 1-file ---
Trial 00002:
Reached page:  0 address:      0 first-byte: 1
Reached page:  1 address:   4096 first-byte: 1
Reached page:  2 address:   8192 first-byte: 1
Reached page:  3 address:  12288 first-byte: 1
Reached page:  4 address:  16384 first-byte: 1
Reached page:  5 address:  20480 first-byte: 
Reached page:  6 address:  24576 first-byte: h
Reached page:  7 address:  28672 first-byte: �
Segmentation fault (core dumped)
  1. Test Program Listing --
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  char *addr;
  FILE *fd;
  int length;
  int x;
  int count = 0;
  int chksum = 0;

  system("cp -f test-0-05.dat test.dat");
  length = 4096 * 5;

  printf("System Page Size: %ld\n", sysconf(_SC_PAGE_SIZE));

  fd = fopen("test.dat", "rb");
  addr = mmap(NULL, length, PROT_READ, MAP_SHARED, fileno(fd), 0);

  while (1) {
    if (count == 2) {
      system("cp -f test-1-10.dat test.dat");
      printf("--- replacing test.dat file : 0-file to 1-file ---\n");
      length = 4096 * 10;
      sleep(2);
    }
    printf("Trial %05d:\n", count);
    for (x = 0; x < length; x++) {
      if (x % 4096 == 0) {
        printf("Reached page: %2d address: %6d first-byte: %c\n",
               (int)(x / 4096), x, addr[x]);
      }
    }
    printf("Length = %d last-byte-of-file = %c\n", length, addr[length - 1]);
    sleep(2);
    count++;
  }

  return 0;
}

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions