Consider this approach to implement a wrapping buffer:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
static int wrapping_buffer(void)
{
char *b1, *b2;
long size;
size = sysconf(_SC_PAGE_SIZE);
if (size == -1) {
perror("_SC_PAGE_SIZE");
return 1;
}
b1 = mmap(NULL, 2 * size, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if (!b1) {
perror("mmap");
return 1;
}
memset(b1, 0, 2 * size);
/* This remaps the upper half of the buffer to the lower half,
The same memory is now accessed by 2 different virtual adresses */
b2 = mremap(b1, 0, size, MREMAP_MAYMOVE|MREMAP_FIXED, b1 + size);
if (b2 != b1 + size) {
perror("mremap");
return 1;
}
sprintf(b1 + size - 16, "this sentence is too long to fit in buffer");
printf("end:\t\"%s\"\n", b1 + size - 16);
printf("start:\t\"%s\"\n", b1);
if (munmap(b2, size) != 0)
perror("munmap(2)");
if (munmap(b1, 2 * size) != 0)
perror("munmap(1)");
return 0;
}
int main(void)
{
return wrapping_buffer();
}
This program compiles and produces output like
end: "this sentence is too long to fit in buffer"
start: " too long to fit in buffer"
IOW, strings written to the buffer that extend over the size of one page are wrapped towards the beginning of the buffer. This is based on mremap(2)'s documented behavior:
If the value of old_size is zero, and old_address refers to a shareable mapping (see mmap(2) MAP_SHARED), then mremap() will create a new mapping of the same pages.
Unfortunately, if I try to run this program under valgrind, it fails in the mremap()
call:
==7217== Memcheck, a memory error detector
==7217== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7217== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==7217== Command: /tmp/wrapping_buffer
==7217==
mremap: Invalid argument
This means that I couldn't use this technique in any program that's supposed to be debugged with valgrind (not talking about this specific code, but other parts of the program that might contain memory leaks). That's unfortunate, because I really like the elegance and efficiency of the remapping technique.
Does anyone have an idea how to make this work under valgrind? Or would I have to hack valgrind itself for that?