Take this (contrived) example:
void* resource1;
void* resource2;
while(true){
int input = getInputFromUser();
switch(input){
case 1: resource1 = malloc(500); break;
case 2: resource2 = resource1; break;
case 3: useResource(resource1); useResource(resource2); break;
}
}
When should free be called? before malloc and assign to resource1
we can't because it might be copied to resource2
, before assigning to resource2
we can't because we may have gotten 2 from the user twice without a intervening 1.
The only way to be sure is to test resource1 and resource2 to see if they are not equal in cases 1 and 2 and free the old value if they were not. This is essentially reference counting where you know there are only 2 possible references.
To understand how alignment affects things, let's look at a larger context.
First, as you note, 2600 bytes of UTF-8 (or any kind of data) will indeed take 2600 bytes.
If you allocate 2600 bytes from the heap using malloc(2600)
e.g. in C, then since malloc does not accept alignment information, it will not know that you're intent is to store only individual bytes — it assumes worst case, which is that you're using the memory for the largest native type that the processor supports. In the case of 64-bit processor that is going to be 16 bytes, which is rather large.
So, the memory allocator locates free memory that matches the 16-byte alignment (and at least 2600 bytes in length). A later memory allocation via malloc
will also be rounded up to 16-byte alignment, so there will be a small gap between the 2600 byte chunk and the next memory block returned by malloc, because 2600 is an exact multiple of 8 but not of 16. (There are also potentially other overheads associate with each malloc block as well.)
Both Linux & Windows offer an aligned malloc; however, Linux explicit states that the minimum alignment is pointer size. Even on Windows, which doesn't say, it is clear from the documentation that the authors expect larger alignments to be requested, not smaller alignments.
C will create structures with proper field alignment for the target platform, meaning that it will insert unused pad bytes within a struct
if the preceding fields do not layout such that the proper alignment can be had. For example:
struct S {
char c;
int i;
}
Struct S
declares c
, as a 1-byte item, and it will be at offset 0 in the struct. The field i
, is, let's say, a 4-byte item. After c
the next available offset is 1 but that is not suitably aligned for a 4-byte value, so the compiler will insert 3 padding bytes and use offset 4 for i
, making the size of the struct sizeof(struct S)
is 8, even though it only stores 5 bytes of information.
Let's also talk about endian-ness. If you have a string of bytes, then each succesive byte is stored at the next higher byte address (just add 1 to the address to get to the next one) — However, big-endian machines store the 4 bytes needed to make up a 4-byte word reversed from little-endian machines. So, if you wanted to use word-sized access on your string "abcd", you would see a difference between a big and little-endian machine: the big-endian machine would give you 'abcd' whereas the little-endian machine will give you 'dcba'.
In summary, it is generally best not to use the same memory both as bytes and as words (at the same time): if it holds bytes, then use byte-sized access, and if it holds words, then use word-sized access. Note this will naturally happen unless you do "bad things" like cast pointers to a type other than what they originally pointed to. (There are times when you might need to, and, authors of routines like memcpy
and memmove
play some tricks for performance.) We can also note that it is not even possible to mix byte-sized and word-sized accesses (for the same data/object/array) in a language like Java (without resorting to serialization) since it doesn't offer the low-level feature of casting pointers.
The compiler and runtime (e.g. malloc) cooperate to make sure your data is properly aligned (perhaps even if over-aligned). For example, the stack, before main
, should be initially aligned (by the runtime) to at least a 16-byte boundary, and then the compiler can create stack frames that are rounded up to a 16-byte size so the stack and all local variables remain aligned during function calls. Global variables get similar treatment, and we have already discussed heap allocations.
Best Answer
No, because a good kernel wipes the contents of memory before it is issued to a process to protect against exactly the kind of attack you propose.
On Unixy systems, memory is allocated to processes by extending what's called the program break, which is the limit of virtually-addressable space a process can use. A process tells the kernel it wants to extend its addressable space, and the kernel will allow it if memory is available or the call will fail if not. (The name of the
brk()
system call comes from this concept.)In practice, large blocks of freed memory don't often butt up against the program break, which is what would be required for a process to return memory to the kernel by shrinking the program break. This is, of course, all dependent on your system's implementation of
malloc()
andfree()
. If you have sources available, they'll tell you whether or not memory is ever returned.There are no security implications for
malloc()
not initializing memory because anything it got viabrk()
will have been scrubbed and anything previouslyfree()
d will have been written by the same process.