Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

bzero(mmap(NULL, 12288, ANON), 8008) raising bus error!

122 views
Skip to first unread message

loozadroog

unread,
May 10, 2011, 6:24:06 PM5/10/11
to
I just posted a version of this program last night in clc,
asking for a general look-through. But now I've run into
a problem when using mmap that doesn't happen with
malloc.

I'm getting a bus error in memset, attempting to clear
the new memory. What could be going wrong?


Some relevant variables from gdb at the point of crash:
(gdb) r
Starting program: /home/olpc/xpost3/proto/v

Program received signal SIGBUS, Bus error.
0xb7ec079f in memset () from /lib/libc.so.6
(gdb) backtrace
#0 0xb7ec079f in memset () from /lib/libc.so.6
#1 0x08048799 in mfalloc (mem=0x8049e90, sz=8008) at v.c:79
#2 0x080487b7 in initmtab (mem=0x8049e90) at v.c:99
#3 0x080489c5 in init () at v.c:162
#4 0x080489f1 in main () at v.c:171
(gdb) up
#1 0x08048799 in mfalloc (mem=0x8049e90, sz=8008) at v.c:79
79 memset(mem->base + adr, 0, sz); /* bus error with MMAP !
*/
(gdb) p adr
$1 = 0
(gdb) p sz
$2 = 8008
(gdb) p pgsz
$3 = 4096
(gdb) p mem->max
$4 = 12288
(gdb) quit


And the naughty program itself:

#define TESTMODULE

#define MMAP
#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#ifdef MMAP
#include <sys/mman.h>
#endif /* otherwise, use malloc/realloc/free */

void error(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}

unsigned pgsz /*= getpagesize()*/;

typedef struct {
unsigned char *base;
unsigned used;
unsigned max;
} mfile;

/* initialize the memory file */
void initmem(mfile *mem) {
#ifdef MMAP
mem->base = mmap(NULL, pgsz, PROT_READ|PROT_WRITE, MAP_SHARED
|MAP_ANONYMOUS,-1,0 );
if (mem->base == MAP_FAILED)
#else
mem->base = malloc(pgsz);
if (mem->base == NULL)
#endif
error("unable to initialize memory file");
mem->used = 0;
mem->max = pgsz;
}

/* destroy memory file */
void exitmem(mfile *mem) {
#ifdef MMAP
munmap(mem->base, mem->max);
#else
free(mem->base);
#endif
mem->base = NULL;
mem->used = 0;
mem->max = 0;
}

/* grow memory by sz */
void growmem(mfile *mem, unsigned sz) {
unsigned char *tmp;
if (sz < pgsz) sz = pgsz;
else sz = (sz/pgsz + 1) * pgsz;
sz += mem->max;
#ifdef MMAP
tmp = mremap(mem->base, mem->max, sz, MREMAP_MAYMOVE);
if (tmp == MAP_FAILED)
#else
tmp = realloc(mem->base, sz);
if (!tmp)
#endif
error("unable to grow memory");
mem->base = tmp;
mem->max = sz;
}

/* allocate memory, returns offset in memory file */
unsigned mfalloc(mfile *mem, unsigned sz) {
unsigned adr = mem->used;
if (sz + mem->used > //=
mem->max) growmem(mem,sz);
mem->used += sz;
memset(mem->base + adr, 0, sz); /* bus error with MMAP ! */
//bzero(mem->base+adr, sizeof(mtab)); /* bus error with MMAP ! */
return adr;
}


#define TABSZ 1000
typedef struct {
unsigned nexttab;
unsigned nextent;
struct {
unsigned adr;
unsigned sz;
/* add fields here for ref counts or marks */
} tab[TABSZ];
} mtab;

/* allocate and initialize a new table */
unsigned initmtab(mfile *mem) {
unsigned adr;
adr = mfalloc(mem, sizeof(mtab));
return adr;
}

/* allocate memory, returns table index */
unsigned mtalloc(mfile *mem, unsigned mtabloc, unsigned sz) {
mtab *tab = (void *)(mem->base + mtabloc);
if (tab->nextent >= TABSZ)
return mtalloc(mem, tab->nexttab, sz);
else {
unsigned ent = tab->nextent;
tab->nextent++;

tab->tab[ent].adr = mfalloc(mem, sz);
tab->tab[ent].sz = sz;

if (tab->nextent == TABSZ) {
tab->nexttab = initmtab(mem);
}
return ent;
}
}

/* fetch a value from a composite object */
void get(mfile *mem, unsigned ent, unsigned offset, unsigned sz, void
*dest) {
mtab *tab;
unsigned mtabloc = 0;
tab = (void *)(mem->base + mtabloc);
while (ent >= TABSZ) {
tab = (void *)(mem->base + mtabloc);
mtabloc = tab->nexttab;
ent -= TABSZ;
}

if (offset*sz + sz > tab->tab[ent].sz) error("get: out of
bounds");

memcpy(dest, mem->base + tab->tab[ent].adr + offset*sz, sz);
}

/* put a value into a composite object */
void put(mfile *mem, unsigned ent, unsigned offset, unsigned sz, void
*src) {
mtab *tab;
unsigned mtabloc = 0;
tab = (void *)(mem->base + mtabloc);
while (ent >= TABSZ) {
tab = (void *)(mem->base + mtabloc);
mtabloc = tab->nexttab;
ent -= TABSZ;
}

if (offset*sz + sz > tab->tab[ent].sz) error("put: out of
bounds");

memcpy(mem->base + tab->tab[ent].adr + offset*sz, src, sz);
}

#ifdef TESTMODULE

mfile mem;

/* initialize everything */
void init(void) {
pgsz = getpagesize();
initmem(&mem);
(void)initmtab(&mem); /*create mtab at address zero */
}

/* destroy everything */
void xit(void) {
exitmem(&mem);
}

int main() {
init();
unsigned adr;
int seven = 7;
int ret;
adr = mtalloc(&mem, 0, sizeof seven);
put(&mem, adr, 0, sizeof seven, &seven);
get(&mem, adr, 0, sizeof seven, &ret);
printf("put seven, got a %d\n", ret);

unsigned adr2;
adr2 = mtalloc(&mem, 0, 8*sizeof seven);
put(&mem, adr2, 6, sizeof seven, &seven);
get(&mem, adr2, 6, sizeof seven, &ret);
printf("put seven in slot 7, got a %d\n", ret);
//get(&mem, adr2, 9, sizeof seven, &ret);
//printf("attempted to retrieve element 10 from an 8-eleemnt
array, got %d \n", ret);

unsigned adr3;
char str[] = "beads in buddha's necklace";
char sret[sizeof str];
adr3 = mtalloc(&mem, 0, strlen(str)+1);
put(&mem, adr3, 0, sizeof str, str);
get(&mem, adr3, 0, sizeof str, sret);
printf("stored and retrieved %s\n", sret);

xit();
return 0;
}
#endif

Ian Collins

unread,
May 10, 2011, 9:24:33 PM5/10/11
to

It looks like you map pgsz (4096) bytes here:

mem->base = mmap(NULL, pgsz, PROT_READ|PROT_WRITE, MAP_SHARED
|MAP_ANONYMOUS,-1,0 );

and attempt to write sz (8008) bytes here:

memset(mem->base + adr, 0, sz);

You have the same error with malloc, you just don't see it because the
write does not enter unmapped memory, it just pisses on the heap!

By the way, why this line?

#define _GNU_SOURCE

--
Ian Collins

loozadroog

unread,
May 10, 2011, 10:22:36 PM5/10/11
to

But gdb tells mem->max is 12288, so mfalloc must have called growmem,
and therefore either mremap or realloc has increased the allocation.
Wouldn't it be a segv if I had overrun the region?
Bus error seems to suggest a problem with the width of the data being
accessed.

> By the way, why this line?
>
> #define _GNU_SOURCE

To activate mremap.

Ian Collins

unread,
May 10, 2011, 10:39:18 PM5/10/11
to

I see, well I couldn't test the code, mremap is Linux specific.

> Wouldn't it be a segv if I had overrun the region?

I would have thought so. Why don't you try something like valgrind to
look for memory access problems?

> Bus error seems to suggest a problem with the width of the data being
> accessed.

memset doesn't have any alignment restrictions.

>> By the way, why this line?
>>
>> #define _GNU_SOURCE
>
> To activate mremap.

So I see!

--
Ian Collins

loozadroog

unread,
May 11, 2011, 2:02:53 AM5/11/11
to
On May 10, 9:39 pm, Ian Collins <ian-n...@hotmail.com> wrote:
> On 05/11/11 02:22 PM, loozadroog wrote:
>
>
>
> > On May 10, 8:24 pm, Ian Collins<ian-n...@hotmail.com>  wrote:
> >> On 05/11/11 10:24 AM, loozadroog wrote:
>
> >>> I just posted a version of this program last night in clc,
> >>> asking for a general look-through. But now I've run into
> >>> a problem when using mmap that doesn't happen with
> >>> malloc.
>
> >>> I'm getting a bus error in memset, attempting to clear
> >>> the new memory. What could be going wrong?
[snip]

>
> >> It looks like you map pgsz (4096) bytes here:
>
> >> mem->base = mmap(NULL, pgsz, PROT_READ|PROT_WRITE, MAP_SHARED
> >>               |MAP_ANONYMOUS,-1,0 );
>
> >> and attempt to write sz (8008) bytes here:
>
> >> memset(mem->base + adr, 0, sz);
>
> >> You have the same error with malloc, you just don't see it because the
> >> write does not enter unmapped memory, it just pisses on the heap!
>
> > But gdb tells mem->max is 12288, so mfalloc must have called growmem,
> > and therefore either mremap or realloc has increased the allocation.
>
> I see, well I couldn't test the code, mremap is Linux specific.

What can you do on other systems. MAP_AUTOGROW?

> > Wouldn't it be a segv if I had overrun the region?
>
> I would have thought so.  Why don't you try something like valgrind to
> look for memory access problems?

It looks like it's giving what perror probably would've shown, but
little else.
To rule out memset itself, I used this replacement:

void zeromem(void *ptr, unsigned sz) {
unsigned char *bptr = ptr;
unsigned i;
for (i=0; i<sz; i++)
bptr[i] = 0;
}

And I put calls to this dump function around the growmem text:

void pr_mfile(mfile *mem) {
printf("mem: { .base = %p, .used = 0x%x, .max = 0x%x };\n", mem-
>base, mem->used, mem->max);
}


652(1)12:48 AM:proto 0> valgrind --leak-check=yes v
==8095== Memcheck, a memory error detector.
==8095== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et
al.
==8095== Using LibVEX rev 1854, a library for dynamic binary
translation.
==8095== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==8095== Using valgrind-3.3.1-Debian, a dynamic binary instrumentation
framework.
==8095== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et
al.
==8095== For more details, rerun with: -v
==8095==
mem: { .base = 0x4026000, .used = 0x0, .max = 0x1000 };
mem: { .base = 0x4028000, .used = 0x0, .max = 0x3000 };
==8095==
==8095== Process terminating with default action of signal 7 (SIGBUS)
==8095== Non-existent physical address at address 0x4029000
==8095== at 0x8048612: zeromem (v.c:36)
==8095== by 0x80487D6: mfalloc (v.c:93)
==8095== by 0x80487F4: initmtab (v.c:114)
==8095== by 0x8048A02: init (v.c:177)
==8095== by 0x8048A2E: main (v.c:186)
==8095==
==8095== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 12 from
1)
==8095== malloc/free: in use at exit: 0 bytes in 0 blocks.
==8095== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==8095== For counts of detected errors, rerun with: -v
==8095== All heap blocks were freed -- no leaks are possible.
Bus error
653(1)12:49 AM:proto 135>

I don't get it. If I've got 3000_16 bytes starting at 4028000_16,
then address 4029000_16 should be right in the middle there.
And all the tricky indexing I was afraid of seems fine.

Alan Curry

unread,
May 11, 2011, 2:40:59 AM5/11/11
to
In article <6e42b7e2-8f29-4c41...@m10g2000yqd.googlegroups.com>,

loozadroog <mij...@yahoo.com> wrote:
>I just posted a version of this program last night in clc,
>asking for a general look-through. But now I've run into
>a problem when using mmap that doesn't happen with
>malloc.
>
>I'm getting a bus error in memset, attempting to clear
>the new memory. What could be going wrong?

The problem seems to be that a growing mremap doesn't work on shared
anonymous mappings. This might be by design, but it seems fishy to me since
mremap doesn't return an error but instead gives you some pages that you
can't use.

I think you'd be justified in reporting this as a kernel bug. If it turns out
to be an intentional restriction it should at least be mentioned in the
mremap man page.

Here's a much smaller demo program. If MAP_SHARED is changed to MAP_PRIVATE,
there's no problem. If a file is provided for backing store instead of
MAP_ANONYMOUS, there's no problem. The combination of shared and anonymous
causes the SIGBUS.

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>

int main(void)
{
int pgsz;
void *p;

pgsz = getpagesize();
printf("pgsz = %d\n", pgsz);

p = mmap(NULL, pgsz, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
printf("p before growth = %p\n", p);

p = mremap(p, pgsz, pgsz*2, MREMAP_MAYMOVE);
printf("p after growth = %p\n", p);

/* try to access the last byte of the original allocation */
printf("p[%d] = %d\n", pgsz-1, ((volatile char *)p)[pgsz-1]);

/* try to access the first byte of the new allocation */
printf("p[%d] = %d\n", pgsz, ((volatile char *)p)[pgsz]);

return 0;
}

--
Alan Curry

loozadroog

unread,
May 11, 2011, 8:38:32 PM5/11/11
to
On May 11, 1:40 am, pac...@kosh.dhis.org (Alan Curry) wrote:
> In article <6e42b7e2-8f29-4c41-98eb-3caa4108f...@m10g2000yqd.googlegroups.com>,

Thanks; I'm writing up the report now. On further thought, it doesn't
seem
like a moved shared mapping makes any sense at all. After a fork, any
other thread trying to share the space is gonna lose it if it has
moved.
By definition, their pointers become invalidated.
So I think it ought to return MAP_FAILED, retaining the original
mapping.
And it'd be nice if the manpage said: don't do this.

I don't know why I chose MAP_SHARED anyway. There's only one process.
Maybe I had idle dreams of some forkable debugging thread. I do
eventually
want to use this with multitasking, but in a single-thread with
longjmps.
So MAP_PRIVATE should work just fine for me here(as it has in the
past).

Thanks again, Alan. I'll put your program (and name) and a link to
this thread
in the report.

0 new messages