views:

239

answers:

7

Can accessing (for read only) memory freed cause an access violation, and, if so, under what circumstances?

+7  A: 

You're asking "can" and not "will", so your answer is yes. It is undefined behavior to point to memory not owned by your program, therefore anything could happen.

Will it? Depends. That is very OS specific. You might be able to get away with it, but obviously you cannot depend on this. Trying to dereference it could cause an exception, because the OS has reclaimed the memory for it's own uses. (again, OS specific).

GMan
+1, just one little nitpick: You say "It is undefined behavior to point to memory not owned by your program", which might be misunderstood, since it's perfectly fine to "point" to anything you want (in the sense of a pointer having that address as its value). It's only a problem when you actually try to do something with that address.
Edan Maor
Actually, I'm pretty sure it's undefined to even point to it. At least with arrays it is, you can point from 0 to n + 1 (that is, an array with 8 elements can be accessed 0 through 7, plus 8 as well, though dereferencing this is undefined).
GMan
That said, if someone will standardese can confirm this, I'll gladly change my answer.
GMan
This might sound too pedantic to some people, but in C/C++ just a mere act of *forming* an invalid pointer theoretically leads to undefined behavior. No access attempt is necessary.
AndreyT
Freeing memory usually doesn't return the memory to the OS (see AndreyT's answer). When it does, the OS will unmap the page from the program's address space and any access will cause an exception. This generally has nothing to do with "could be allocated for some other program".
Artelius
Good point, fixed.
GMan
From C99 draft para 6.5.6 on "Additive Operators": "If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated." C89 para 3.3.6 has similar wording.
Tim
+1  A: 

It's certainly allowed to; the C standard says straightforwardly that behavior is undefined if "The value of a pointer that refers to space deallocated by a call to the free or realloc function is used". In practice, owing to the way OSs work, you're more likely to get garbage than a crash, but quite simply you're not allowed to assume anything about what happens when you invoke undefined behavior.

hobbs
+3  A: 

On Windows: Managing Virtual Memory in Win32

Free, Reserved, and Committed Virtual Memory

Every address in a process can be thought of as either free, reserved, or committed at any given time. A process begins with all addresses free, meaning they are free to be committed to memory or reserved for future use. Before any free address may be used, it must first be allocated as reserved or committed. Attempting to access an address that is either reserved or free generates an access violation exception.

Nick D
+6  A: 

Yes, it can. "Access violation" ("segmentation fault", etc) is the response that is normally generated by OS/hardware when the process attempts to access (even just for reading) memory that is known to OS as "empty", "freed" or inaccessible for some other reason. The key moment here is that the OS/hardware must know that the memory is free. Memory management functions of C Standard Library don't necessarily return the 'free'd memory back to OS. They might (and will) keep it for future allocations. So in some cases accessing 'free'd memory will not result in "Access Violation" since from the OS's/hardware's point of view this memory has not been really freed. However, at some point the Standard Library might decide to return the collected free memory back to OS, after which an attempt to access that memory will normally result in "Access Violation".

AndreyT
Even that is totally "implementation detail" territory. You could be running C on one of those exotic platforms that tags and memory manages every single allocation and generates an exception without fail the moment you access a single *bit* of invalid memory. Of course you're not, but in this case there's no reason to *assume* you're not. :)
hobbs
I understand :) I just thought that once we are talking about such a specific wording as "Access Violation", we are already in implementation- and platform- specific terriotory.
AndreyT
A: 

If you're asking this because you've profiled your code and found that accessing freed memory would provide a significant performance boost, then the answer is very rarely, if the freed block is small. If you want to be sure, provide your own alternative implementation of malloc() and free().

Artelius
+1  A: 

freed memory doesn't belong to you anymore, exactly, corresponding physical memory page is out of your process address space which might have been remapped to other process address space already after your freeing, and that address you accessing have not been allocated physical page and do mapping yet; so "Access violation" or "segfault" will happen if access it even for reading only. it is triggered by the processor hardware in general, e.g. GP#, not by OS.

though if the specific physical page which owns your freed memory is still under controlling of your task context, say partial of the page is still used by your process, then "Access violation" or "segfault" may not occur.

EffoStaff Effo
+2  A: 

Unlikely

Memory managers can theoretically return space to the OS but rarely if ever do so. And without returning the space all the way to the kernel, the MMU will never be involved and so a fault is not possible.

The problem is fragmentation. Allocation of variably-sized blocks is inefficient, and general purpose allocators cannot move allocated blocks around because they don't know what points to what, so they can't coalesce blocks unless they happen to be adjacent.

Measurements indicate that fragmentation overhead tends to be about 50% in steady-state processes, so with every-other-block untouchable it's impossible to return pages unless they are much smaller than blocks, and they generally are not.

Also, the book-keeping challenge of returning pages embedded within the heap is daunting, so most memory managers don't even have the capability, even in the unlikely case that they would have the opportunity.

Finally, the traditional process model was not a sparse object. This kind of low-level software is conservatively developed and lives for a long time. An allocator developed today just might attempt sparse allocation but most subsystems just use whatever is already in the C library, and that is not something that's wise to rewrite casually.

DigitalRoss
This is actuall very implementation-dependent. Standard Library implementation that comes with MSVC++ on Windows, for example, has a very "thin" implementation of `malloc`. WinAPI already provides a set of functions - Heap API - that perform effective application-level dynaic memory management, so `malloc`/`realloc`/`free` there is just a very thin adapter that immediately delegates the calls to Heap API. In this implementtaion the memory is technically "returned to OS" always and immediately :)
AndreyT
The win32 heap API was just to ease the Win16 transition. In Win16, "global allocations" really allocated an LDT descriptor and did hideous segmentation things, and the whole system ran in one address space. In Win32 the heap API is purely local.
DigitalRoss
OpenBSD returns immediately blocks >= pagesize. Dlmalloc returns immediately blocks >= some configured size (128 Kb by default in some places). Dlmalloc returns memory if the top free block exceeds some determined size. So, returning memory to the OS is much more likely than you seem to think.
ninjalj