0

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?

uncleremus
  • 317
  • 1
  • 11
  • Yes, hacking valgrind seems to be the only option, see e.g. this https://stackoverflow.com/questions/27382653 – n. m. could be an AI Oct 27 '20 at 09:26
  • Thanks. Unfortunately I can't simply remove `MREMAP_FIXED`, as it's the whole point of the code. Basically I'd need valgrind to pass this call to the kernel unmodified. – uncleremus Oct 27 '20 at 10:55
  • I think the needed changes in valgrind might be a lot more complex that just passing the flag to the kernel. See syswrap-generic.c function do_mremap. – phd Oct 28 '20 at 19:55
  • Yeah, I figured that. That's why I want to avoid it :-) – uncleremus Oct 29 '20 at 13:05
  • https://bugs.kde.org/show_bug.cgi?id=274189 you are not alone – PlasmaHH Jun 30 '22 at 11:08

0 Answers0