On Wed, Jan 20, 2021 at 07:04:08AM +0100, Christian Jullien wrote:
> Hi,
>
> I'm running OpenBSD 6.8 on arm :
>
> $ sysctl | grep hw
> hw.machine=armv7
> hw.model=ARM Cortex-A8 r3p2
> hw.ncpu=1
> hw.byteorder=1234
> hw.pagesize=4096
> hw.disknames=<edited>
> hw.diskcount=2
> hw.cpuspeed=0
> hw.product=TI AM335x BeagleBone Black
> hw.serialno==<edited>
> hw.physmem=477257728
> hw.usermem=477241344
> hw.ncpufound=1
> hw.allowpowerdown=1
> hw.ncpuonline=1
>
> $ uname -a
> OpenBSD <edited> 6.8 GENERIC#353 armv7
> $ clang -v
> OpenBSD clang version 10.0.1
> Target: armv7-unknown-openbsd6.8-gnueabi
> Thread model: posix
> InstalledDir: /usr/bin
>
> And I'm trying to port my OpenLisp ISLISP compiler on it (which is quite
> different than OpenLISP, I have anteriority of the name). It needs a portion
> of memory reserved at a fixed address to allow to expand memory on demand
> and to save/restore images.
> This code works on all systems I know (> 160 GNU triplets) and it works for
> years on OpenBSD x86/x64.
>
> On arm, the portion of code below, sometimes does a coredump after mmap
> successfully returned the desired memory mapped region.
> Changing the start address or memory size (down to 0x200000 == 2Mb) does not
> change the issue.
> I can understand that mmap can fail for some given values but not why munmap
> fails after mmap success.
This is not a bug.
mmap with MAP_FIXED replaces any existing mapping. The unmap then zaps that
mapping, which could contain code or data used by the process. Due to
address space randomization of various things, it depens on luck.
The proper way is to not depend on a fixed address. If your image dump
contains addresses, make them relative. For OpenLisp that might be a
huge task though. But depending on fixed addresses is not going to
work in the end.
An approach that does not do damage but won't succeeed all the time:
Try without MAP_FIXED. The hint will be used and if it overlaps with
an existing mapping, mmap will return a different address or MAP_FAILED
if it could not. Then check the address and if it isn't the expected
address, bail out. It is better to fail than to cause memory corruption.
-Otto
>
> Here is the code snippet:
>
> $ more foo.c
> #include <signal.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <setjmp.h>
> #include <string.h>
> #include <sys/mman.h>
>
> void
> olsystrybase(void *start, size_t size) {
> int fd = -1;
> void *mem;
>
> printf("Trying mmap start=%p, %lx, fd=%d\n", start, size, fd);
> mem = mmap(start,
> size,
> PROT_READ | PROT_WRITE,
> (MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE),
> fd,
> 0);
> printf("mmap returns: start=%p, mem=%p, size=%lx fd=%d\n", start, mem,
> size, fd);
>
> if (mem == (void *)MAP_FAILED) {
> return;
> }
>
> printf("Going to munmap: %p, %lx\n", mem, size);
> (void)munmap(mem, size);
> printf("munmap done: %lx\n", size);
> }
>
> int
> main() {
> olsystrybase((void*)0x50000000, 0x880000);
> }
>
> To reproduce (it may succeed from 1 to ~10 before hanging on the system I
> use):
>
> $ clang -o foo foo.c; while (./foo); do done
> Trying mmap start=0x50000000, 880000, fd=-1
> mmap returns: start=0x50000000, mem=0x50000000, size=880000 fd=-1
> Going to munmap: 0x50000000, 880000
> munmap done: 880000
> Trying mmap start=0x50000000, 880000, fd=-1
> mmap returns: start=0x50000000, mem=0x50000000, size=880000 fd=-1
> Going to munmap: 0x50000000, 880000
> munmap done: 880000
> Trying mmap start=0x50000000, 880000, fd=-1
> mmap returns: start=0x50000000, mem=0x50000000, size=880000 fd=-1
> Going to munmap: 0x50000000, 880000
> munmap done: 880000
> Trying mmap start=0x50000000, 880000, fd=-1
> mmap returns: start=0x50000000, mem=0x50000000, size=880000 fd=-1
> Going to munmap: 0x50000000, 880000
> munmap done: 880000
> Trying mmap start=0x50000000, 880000, fd=-1
> Segmentation fault (core dumped)
>