5
struct item
{
    int a;
};
int main()
{
    item *a = (item *)malloc(sizeof(item));
    item *b = (item *)malloc(sizeof(item));
    short *c = (short *)b;
    c += 3; 
    memcpy(a, c, sizeof(int));
    free(a);
    free(b);
    return 0;
}

Why does valgrind echo "Invalid read of size 2"? I think it should be size 4.

Example message from Valgrind:

==19134== Invalid read of size 2
==19134== at 0x4C2F7E0: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==19134== by 0x400625: main (main.cpp:19)
==19134== Address 0x51fd096 is 2 bytes after a block of size 4 alloc'd
==19134== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==19134== by 0x4005FC: main (main.cpp:16) 
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
LTzycLT
  • 489
  • 1
  • 5
  • 13
  • 2
    What is `sizeof(int)` with your compiler? Plausible values are 4 (probable) and 2 (less likely, by quite a margin). Your code is invoking undefined behaviour; you are forcing `memcpy()` to access memory way outside the bounds of what was allocated to `b`. There's an element of "it doesn't matter what the size reported is; the code is invalid because it invokes undefined behaviour and therefore any behaviour is OK". I assume the real code has `#include ` and `#include ` in front. Do you get any compilation warnings? – Jonathan Leffler Oct 28 '14 at 04:09
  • 5
    The statement c += 3 is advancing c by 6 bytes (assuming a short is 2 bytes). Perhaps the 4 byte block pointed to by b is located immediately after that pointed to by a, so you have 8 contiguous bytes. Then, memcpy is reading 2 bytes beyond the end of this block. – RJinman Oct 28 '14 at 04:11
  • @RobJinman That was my initial thought but we actually really can't know that...undefined behavior is the right answer. It very well could just be coincidence. – HostileFork says dont trust SE Oct 28 '14 at 04:17
  • @JonathanLeffler Boundaries aside, does casting `item*` to `short*` run afoul of [strict aliasing](http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule)? Would memcpy count as "dereferencing"? – HostileFork says dont trust SE Oct 28 '14 at 04:26
  • @HostileFork: GCC 4.9.1 doesn't think so with `gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -fstrict-aliasing`. Of course, I had to change the structure definition into a `typedef` (the code was originally compiled by a C++ compiler — hence the casts on the results of `malloc()`). (See [GCC Manual](https://gcc.gnu.org/onlinedocs/gcc-4.9.1/gcc/Warning-Options.html#Warning-Options) for information about warning options.) – Jonathan Leffler Oct 28 '14 at 04:41
  • Here are some refrence: value a 0x602010, value b 0x602030, value c 0x602036, sizeof(int) 4 – LTzycLT Oct 28 '14 at 04:52
  • Running your code (compiled for 64-bit on Ubuntu 14.04), `valgrind` reports what I'd expect: `==12694== Invalid read of size 4` `==12694== at 0x4004B0: main (string3.h:51)` `==12694== Address 0x51fd096 is 2 bytes after a block of size 4 alloc'd` `==12694== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)` `==12694== by 0x4004AC: main (d79.c:12)` Can you show the exact output you get from `valgrind`? – Jonathan Leffler Oct 28 '14 at 06:21
  • @JonathanLeffler ==19134== Invalid read of size 2 ==19134== at 0x4C2F7E0: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==19134== by 0x400625: main (main.cpp:19) ==19134== Address 0x51fd096 is 2 bytes after a block of size 4 alloc'd ==19134== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==19134== by 0x4005FC: main (main.cpp:16) – LTzycLT Oct 29 '14 at 13:05
  • In future, please edit such information into the question where it can be formatted, rather than into a comment. I had to put mine in a comment because it isn't part of the question you asked, but you should update your question with the extra info. I've done it for you this time. – Jonathan Leffler Oct 29 '14 at 14:23
  • 2
    You Valgrind reports the same basic problem (reading 2 bytes after the end of a block of 4). I don't have a good explanation for why it is a 2-byte read rather than a 4-byte read; I can only guess that for the combination of versions of GCC and GLIBC and Valgrind in 32-bit mode on your platform, the copy ends up being done as two 2-byte transfers, maybe influenced by the type of `c`, which is a `short *`. I was not able to get a 32-bit compilation to work on my Ubuntu VM; there was a header missing. One day, I'll work out why that is and fix it. – Jonathan Leffler Oct 29 '14 at 14:28
  • @JonathanLeffler Thanks a lot. I have tried on another computer, it echo invalid read size 4 , but when I edit code and expand invalid read size, it always show Invalid read of size 8, which is not expected.I hope this clue can help you. – LTzycLT Oct 30 '14 at 02:51
  • @JonathanLeffler In case it interests you, I had the same problem. See my answer. Bounty, anyone? – Engineer Jun 18 '15 at 19:04
  • While it is not immediately related to the matter, this code will not compile in C. It was apparently compiled by C++ compiler. – AnT stands with Russia Jun 18 '15 at 19:10

1 Answers1

4

I got “Invalid read of size 2” trying to malloc() a 2x2 single channel texture (4 bytes / uint8_ts). I assumed the allocation was too small - word size on the architecture in question is 8 bytes (64-bit) - so I doubled the allocation and it stopped valgrind's complaints. Since malloc() is supposed to be aligned, I was a bit surprised by this (I'm sure it's something that would be obvious to the experts), but maybe it will help someone else. Not obliged to use the extra allocated space, it just needs to be there.

...It's a fix even if it doesn't bring insight. Problem occurred on gcc 4.9.1 (Ubuntu 4.9.1-16ubuntu6).

Engineer
  • 8,529
  • 7
  • 65
  • 105