#define _GNU_SOURCE
#include <stdio.h>
#include "tests/sys_mman.h"
#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <syscall.h>
#ifndef REMAP_FIXED
#define MREMAP_FIXED 2
#endif
static int PAGE;
void mapanon_fixed ( void* start, size_t length )
{
void* r = mmap(start, length, PROT_NONE,
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0,0);
assert(r != MAP_FAILED);
assert(r == start);
}
void unmap_and_check ( void* start, size_t length )
{
int r = munmap( start, length );
assert(r == 0);
}
char* workingarea = NULL;
char* try_dst = NULL;
// set up working area so expansion limit is 20*PAGE
//
// | 10 | 20 | 10 | 60 |
// | pre | src | FREE | post |
//
// A suitable attempted fixed dst is workingarea + 150*PAGE.
char* setup ( void* other_stuff, int other_len )
{
if (!workingarea) {
workingarea = mmap(0, 200*PAGE, PROT_NONE,
MAP_ANONYMOUS|MAP_PRIVATE, 0,0);
assert(workingarea);
try_dst = workingarea + 150*PAGE;
unmap_and_check(workingarea, 200*PAGE);
}
if (other_stuff) {
unmap_and_check(other_stuff, other_len);
}
// get rid of the old working area
unmap_and_check( workingarea, 200*PAGE);
// pre block
mapanon_fixed( workingarea + 0*PAGE, 9*PAGE);
// the area
mapanon_fixed( workingarea + 10*PAGE, 20*PAGE );
// upper half
mapanon_fixed( workingarea + 40*PAGE, 60*PAGE );
return workingarea + 10*PAGE;
}
/* show the working area */
void show ( void )
{
int i,r __attribute__((unused));
for (i = 0; i < 200; i++) {
r = mprotect( workingarea + i * PAGE, PAGE, PROT_NONE );
// We used to print 'X' or '.' according to the mprotect result, but the
// results are too variable and the test was never reliable. So now we
// just always print '.'. At least this test gives mremap a thorough
// working out and so will detect egregious problems like crashes.
//printf("%c", r == 0 ? 'X' : '.');
printf(".");
if (i == 49 || i == 99 || i == 149) printf("\n");
}
printf("\n");
}
char* dst = NULL;
char* src = NULL;
char* dst_impossible = NULL;
char* identify ( char* p )
{
if (p == dst) return "dst";
if (p == src) return "src";
if (p == dst_impossible) return "dst_imp!";
if (p == try_dst) return "dst_poss";
return "other";
}
int main ( void )
{
int alocal, maymove, fixed, nsi, dstpossible;
int newsizes[6] = { 19, 20, 21, 29, 30, 31 };
char* tidythis = NULL;
int tidylen = 0;
int firsttime = 1;
char buf[100];
dst_impossible = (char*)(&alocal) + 500 * 1000 * 1000;
PAGE = sysconf(_SC_PAGESIZE);
for (maymove = 0; maymove <= 1 ; maymove++) {
for (fixed = 0; fixed <= 1; fixed++) {
printf("\n");
for (nsi = 0; nsi < 6; nsi++) {
for (dstpossible = 0; dstpossible <= 1; dstpossible++) {
char* r;
int newsize = newsizes[nsi] * PAGE;
int flags = (maymove ? MREMAP_MAYMOVE : 0) |
(fixed ? MREMAP_FIXED : 0);
dst = dstpossible ? try_dst : dst_impossible;
src = setup( tidythis, tidylen );
if (firsttime) {
printf("dst_possible = %p\n", try_dst );
printf("dst_impossible = %p\n", dst_impossible );
printf(" src = %p\n", src);
printf("\n");
sprintf(buf, "cat /proc/%d/maps", getpid());
if (0) system(buf);
firsttime = 0;
}
printf("maymv %d fixed %d newsz %2d dstpo %d dst %p -> ",
maymove, fixed, newsizes[nsi], dstpossible, dst );
r = (char*)
syscall(__NR_mremap, src, 20*PAGE, newsize, flags, dst, 0 );
// We used to print the address or error, but that was also unreliable.
//if (r == MAP_FAILED)
// printf("error %d\n", errno);
//else
// printf("%p (== %s)\n", r, identify(r));
printf("\n");
if (1) {
show();
printf("\n");
}
if (r != MAP_FAILED) {
if (r != src && r != try_dst && r != dst_impossible) {
tidythis = r;
tidylen = newsize;
}
}
}
}
}
}
return 0;
}