Consecutive mmap call never return contiguous address

0

The function page_allocate work. It does return address to mapped pages with the specified alignment. However consecutive call using 64k and 1024k are never contiguous. Why?

  • ./mmap 0x00001000 //4k
  • ./mmap 0x00002000 //8k
  • ./mmap 0x00004000 //16k
  • ./mmap 0x00008000 //32k
  • ./mmap 0x00010000 //64k bad? Can't get contiguous range within consecutive allocation
  • ./mmap 0x00020000 //128k
  • ./mmap 0x00040000 //256k
  • ./mmap 0x00080000 //512k
  • ./mmap 0x00100000 //1024k bad?
  • ./mmap 0x00200000 //2048k
  • ./mmap 0x00400000 //4096k
  • ./mmap 0x00800000 //8192k
  • ./mmap 0x01000000 //16384k
  • ./mmap 0x10000000 //256Mb
  • ./mmap 0x100000000 //4096mb

Here is included a sample program. Use gcc -Wall -g mmap.c -o mmap to compile.

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define _GNU_SOURCE /* MAP_ANONYMOUS */
#include <sys/mman.h>
#include <unistd.h> /* sysconf */

static size_t sys_page_size = 0;

void
page_init(void)
{
    int sc;

    if(!sys_page_size)
    {
        sc = sysconf(_SC_PAGESIZE);
        if(sc == -1)
        {
            sys_page_size = 0x0000000000001000; /* Default to 4Kb */
        }
        else
        {
            sys_page_size = sc;
        }
    }
}


void *
page_allocate(size_t request,
              size_t alignment)
{
    size_t size;
    size_t slop;
    void  *addr;

    /* Round up to page size multiple.
    */
    request   = (request   + (sys_page_size - 1)) & ~(sys_page_size -1);
    alignment = (alignment + (sys_page_size - 1)) & ~(sys_page_size -1);

    size      = request + alignment;


    /* Maybe we get lucky.
    */
    addr      = mmap(NULL,
                     request,
                     PROT_READ|PROT_WRITE,
                     MAP_PRIVATE|MAP_ANONYMOUS,
                     -1,
                     0);

    if(!((uintptr_t)addr & (alignment - 1)))
    {
        return addr;
    }
    munmap(addr, request);



    addr      = mmap(NULL,
                     size,
                     PROT_READ|PROT_WRITE,
                     MAP_PRIVATE|MAP_ANONYMOUS,
                     -1,
                     0);
    slop      = (uintptr_t)addr & (request - 1);


    #define POINTER_OFFSET(addr, offset) ((void *)((uintptr_t)(addr) + (offset)))
    if(slop)
    {
        munmap(addr, request - slop);
        munmap(POINTER_OFFSET(addr, size - slop), slop);
        addr = POINTER_OFFSET(addr, request - slop);
    }
    else
    {
        munmap(POINTER_OFFSET(addr, request), request);
    }


    return addr;
}

int
main(int    argc,
     char **argv)
{
    size_t size;

    void *cmp = NULL;
    void *ptr[16];
    int   i;

    page_init();


    if(argc == 2)
    {
        size = strtol(argv[1], NULL, 16);
    }
    else
    {
        size = 0x00001000;
    }


    for(i = 0;
        i < 16;
        i ++)
    { 
        ptr[i] = page_allocate(size, size);
    }

    for(i = 0;
        i < 16;
        i ++)
    {
        printf("%2i: %p-%p %s\n",
               i,
               ptr[i],
               (void *)((uintptr_t)ptr[i] + size - 1),
               (llabs(ptr[i] - cmp) == size) ? "contiguous" : "");

        cmp = ptr[i];
    }


    return 1;
}
c
alignment
mmap
asked on Stack Overflow Aug 20, 2014 by Philip J. Fry

1 Answer

1

You should never expect mmap to provide contiguous addresses on its own, but you could attempt to get them by requesting an address that would make the new mapping contiguous as long as the address range is free, and omitting MAP_FIXED so that the requested address is only used as a hint (with MAP_FIXED, it would replace what's already there, which is definitely not what you want).

I suspect the reason you're seeing mmap return contiguous maps for some calls, but not all, is that the kernel is trying to balance the cost of making new VM areas with the desire to have addresses be unpredictable (ASLR).


User contributions licensed under CC BY-SA 3.0