1

I use malloc to allocate 8192 bytes of memory; malloc returns successfully, but for some reason, I cannot access memory beyond 4205 bytes of that memory chunk.

I've also tried allocating even larger chunks of memory (i.e. 8192 * 2) but still no luck, only the first 4205 byes of memory is accessible :(

Here is the partial code:

int num_ino = 256;
struct inode * ino_table = malloc(8192);
assert(ino_table);
for(int i = 0; i < num_ino; i ++){
  printf("pre core dump %d\n", i);
  memcpy(ino_table + i * sizeof(struct inode), &inotable[i], sizeof(struct inode));
}

Here is what happens in gdb:

Breakpoint 1, unixfilesystem_init (dfd=3) at unixfilesystem.c:54
54        assert(ino_table);

(gdb) p *(ino_table)
$1 = {i_mode = 0, i_nlink = 0 '\000', i_uid = 0 '\000', i_gid = 0 '\000', i_size0 = 0     '\000', i_size1 = 0, i_addr = {0, 0, 0, 0, 0, 0, 0, 0}, 
  i_atime = {0, 0}, i_mtime = {0, 0}}

(gdb) p *(ino_table + 4205)
$2 = {i_mode = 0, i_nlink = 0 '\000', i_uid = 0 '\000', i_gid = 0 '\000', i_size0 = 0     '\000', i_size1 = 0, i_addr = {0, 0, 0, 0, 0, 0, 0, 0}, 
  i_atime = {0, 0}, i_mtime = {0, 0}}

(gdb) p *(ino_table + 8000)
Cannot access memory at address 0x643a30

(gdb) p *(ino_table + 4206)
Cannot access memory at address 0x625ff0
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
heisenbug
  • 13
  • 2
  • 1
    What's the size of `struct inode`? – sharptooth Oct 15 '13 at 08:54
  • Is, perhaps, `struct inode` bigger than a `char` in size? – CB Bailey Oct 15 '13 at 08:55
  • could you add `assert( i * sizeof(struct inode) < 8192 );` :-) – AndersK Oct 15 '13 at 08:56
  • 2
    You want `struct inode *ino_table = malloc(sizeof(struct inode) * 256);` – David Ranieri Oct 15 '13 at 08:57
  • One thing, you should change your `malloc(...)` call to return a `(struct inode*) malloc(...)` – yamafontes Oct 15 '13 at 08:57
  • 2
    @KepaniHaole actually, [no he doesn't want to cast malloc in C](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – WhozCraig Oct 15 '13 at 08:58
  • I'm not one to say I know gdb (because I don't) but I would not be the least bit shocked if the command you're feeding is doing the structured pointer math for you. I.e. you should not address an offset beyond `*(ino_table + N)`, where N is the number of `struct inode` that "fit" in your 8192-byte allocation (with padding). Alter Mann is correct on how the allocation should be done, btw. – WhozCraig Oct 15 '13 at 09:01
  • 1
    If you want to understand pointer arithmetic better, try this: `(gdb) x ino_table + i`. You will see that as you increase `i` by one, the address increases by more than one. – ams Oct 15 '13 at 09:03

3 Answers3

6

When you perform pointer arithmetic on ino_table, the unit is sizeof(struct inode), not byte.

Thus, the

ino_table + i * sizeof(struct inode)

should become

ino_table + i

Finally, I'd change the malloc() like so:

struct inode * ino_table = malloc(num_ino * sizeof(struct inode));
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • +1 I learned gdb does structured pointer math today. Thanks for that to you and the others. I really need to get my arse out of Windows code and back in the world of the living (though their debugger is still the cats pajamas). – WhozCraig Oct 15 '13 at 09:03
  • @WhozCraig this answer has nothing to do with gdb, but with normal C pointer arithmetic ;) – griffin Oct 15 '13 at 09:06
  • @griffin I know. it was the normal C pointer arithmetic that was done *in the gdb print command* that made me smile. – WhozCraig Oct 15 '13 at 09:07
  • 1
    btw: I personally do pointer definitions (matter of taste) this way: type *variable, with no space between the asterisk and the variable, because you also do multiple pointers this way: type *var1, *var2, *var3 - if you instead to type * var1, var2, var3, only the first one is a pointer. But, thats only a matter of taste ;) – griffin Oct 18 '13 at 12:43
4

That seems reasonable,

This command in gdb:

   p *(ino_table + 8000)

Does not say "print the content of ino_table offset by 8000 bytes". It says, "print the content of ino_table offset by 8000 elements".

This means gdb evaluates *(ino_table + 8000) just as C does, which gives you the 8000th element. This is the same as saying p ino_table[8000].

In bytes, that is sizeof(struct inode) * 8000. This will be out of bounds unless sizeof(struct inode) is 1.

When allocating space for an array of elements, one would normally say how many elements is needed, however you have told malloc() to get 8192 bytes. If you want 8192 elements of a struct inode in your ino_table, you would do:

 struct inode * ino_table = malloc(8192 * sizeof *ino_table);

Note that gdb does not do bound checking on your array/buffers. If you successfully can do p *(ino_table + 4205) , that can very well be out of bounds regarding the ino_table, and could be pointing into available memory elsewhere. When gdb finally errors out with Cannot access memory at address 0x643a30, that means you're now trying to access memory that does not exist - i.e. it is not mapped into the address space of your process at all.

nos
  • 223,662
  • 58
  • 417
  • 506
  • +1 assuming you know gdb better than I (which isn't much of a stretch at all) this makes perfect sense. – WhozCraig Oct 15 '13 at 09:01
3

Your problem is most probably pointer arithmetic.

ino_table + i * sizeof(struct inode)

is most probably not what you want, but

ino_table + i

as due to pointer arithmetic ino_table + i is already doing

((char *)ino_table) + i * sizeof(struct inode)

The same applies to gdb, where ino_table + 8000 refers to &ino_table[8000]- the 8000th element of ino_table, which, unless your struct inode is defined to be 1 byte in size, would be outside of the allocated memory.


More genericly speaking:

If you have a pointer P to a type T, then P++ will increment the address stored in P by sizeof(T), and NOT by one byte.


Also, instead of

ino_table + i

you could do

&ino_table[i]

which is preferred by many people (I personally don't really care) because it makes it more obvious that increasing i increases the accessed address by one element, and not one byte.


And for the malloc you could do

malloc(number_of_elements * sizeof(type)) - don't worry about performance difference or code size, as when number_of_elements is constant, the expression should be optimized into one constant number anyway.

Or, if you wanna be extra-careful and/or don't know if you will access parts which you haven't written to, you could use:

calloc(number_of_elements, sizeof(type))

The difference to malloc is that calloc makes sure you get nulled memory (on most implementations by serving you a pointer to a special copy-on-write zeroed memory page of the system), as well as returning memory aligned to sizeof(type) (malloc will normally also return aligned memory, but in the case of malloc, it will probably be aligned to the largest alignable value)


As bounds checking has been mentioned in a different answer: You could have a look at gcc mudflap extension - it doesn't exactly do bounds checking, but many other memory related things, and IMHO is one of the easiest things to set up besides valgrind (which doesn't need any setup, but unfortunately doesn't always work)

griffin
  • 1,261
  • 8
  • 24