1

I'm trying to use the sbrk system call to ask for one memory page and divide that page into small blocks, but my code always hits some invalid memory:

void sbrkBlocks() {
    int *b = sbrk(0);
    if(sbrk(sysconf(_SC_PAGESIZE)) == (void *)-1) {
        printf("sbrk failed\n");
        return NULL;
    }
    void *bound =b + sysconf(_SC_PAGESIZE);
    while (b + 16 <=bound) {
       *b = 1;
        b+= 16;
    }
}

Like if I get sbrk(0) at 0x804d000, the bound after sbrk(one_page_size) will be 0x8055000, but the code will get a segmentation fault at 0x804e000.

honk
  • 9,137
  • 11
  • 75
  • 83
qr rq
  • 13
  • 3
  • is this for an appl or a kernel module? – Umamahesh P Mar 11 '16 at 15:53
  • This is for implementing malloc, I modified my actual code so it would be easier for understaning. – qr rq Mar 11 '16 at 15:57
  • 2
    On my system, `sysconf(_SC_PAGESIZE)` returns 4096, which I think is pretty typical. If yours is the same then you have performed your bound computation wrongly: for an initial break at `0x804d000`, the new break should be at `0x804e000`, exactly where you get the segfault. – John Bollinger Mar 11 '16 at 16:04
  • 2
    If you're implementing an allocator yourself, its usually better to stick to `char *` and `void *`. This allows you to be explicit about how large chunks are, and specify _exactly_ where to put the bits you use for book-keeping. – bnaecker Mar 11 '16 at 16:08
  • Thank you, problem solved, I added wrong number to bound. – qr rq Mar 11 '16 at 16:17
  • If any of the answers were helpful, it is polite to mark one of them as accepted. – davmac Mar 16 '16 at 11:45

2 Answers2

5

You're doing your pointer arithmetic incorrectly. sysconf(_SC_PAGESIZE) returns the size in bytes of a page. Adding that b, which is pointer-to-int, adds sizeof(int) * _SC_PAGESIZE to it.

Run this:

int *b = sbrk(0);
printf("b = %p\n", b);
printf("PAGESIZE = %d\n", sysconf(_SC_PAGESIZE));
void *bound = b + sysconf(_SC_PAGESIZE);
printf("bound = %p\n", bound);
printf("bound - b = %d\n", (char *) bound - (char *) b);

You should get something like:

b = 0x10c37d000
PAGESIZE = 4096
bound = 0x10c37e000
bound - b = 16384

You're just writing past the end of the boundary you actually did allocate.

bnaecker
  • 6,152
  • 1
  • 20
  • 33
1

Note that, on some systems, sbrk doesn't return aligned pointers and you should check the actual pointer after calling sbrk:

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

int main(void) {
    int *l = sbrk(0);
    sbrk(getpagesize());
    int *u = sbrk(0);  // do this to get the actual aligned pointer
    //int *u = l + getpagesize(); // may fail, may not be accurate
    printf("l=%p, u=%p\n", l, u);
    while (l + 16 < u) {
       *l  = 1;
        l += 16;
        printf("l=%p\n", l);
    }
    return 0;
}
bishop
  • 37,830
  • 11
  • 104
  • 139
  • 2
    If he were performing an incorrectly-aligned write to valid memory -- on a system where it matters -- then wouldn't he expect to get a bus error instead of a segmentation fault? – John Bollinger Mar 11 '16 at 16:10
  • 1
    Thank you, although that's not the cause of my problem, but I will add the check you mentioned. – qr rq Mar 11 '16 at 16:16
  • @JohnBollinger Maybe... Probably. I've seen the same (bad) code generate `SIGBUS` and `SIGSEGV` depending upon what machine it ran. – bishop Mar 11 '16 at 16:17