J
Joe keane
Bugs of memory allocation will make you mad.
Bugs *in* memory allocation will put you in the cuckoo people place.
Bugs *in* memory allocation will put you in the cuckoo people place.
Which is why they are exceedingly rare. Nearly all allocation problems are due
to the program storing outside array bounds.
Which is why they are exceedingly rare. Nearly all allocation problems are due
to the program storing outside array bounds.
The only allocator bug I have personally encountered was with
a malloc() implementation that never, never returned NULL. When it
ought to have returned NULL, it crashed the program instead ...
Bugs of memory allocation will make you mad.
Bugs *in* memory allocation will put you in the cuckoo people place.
Goran said:If you don't provide a way to verify that, it didn't happen ;-), and
there was a bug in your code.
I suspect he's referring to the malloc() implementation
on typical Linux systems, which overcommits memory by default.
It can allocate a large chunk of address space for which no actual
memory is available. The memory isn't actually allocated until
the process attempts to access it. If there isn't enough memory
available for the allocation, the "OOM killer" kills some process
(not necessarily the one that did the allocation).
I suspect he's referring to the malloc() implementation
on typical Linux systems, which overcommits memory by default.
It can allocate a large chunk of address space for which no actual
memory is available. The memory isn't actually allocated until
the process attempts to access it. If there isn't enough memory
available for the allocation, the "OOM killer" kills some process
(not necessarily the one that did the allocation).
If you don't provide a way to verify that, it didn't happen ;-), and
there was a bug in your code.
Yeah, it was probably my code. Awfully nice of DEC to fix it
for me by patching VMS' C library, don't you think?
Goran said:That crossed my mind, but what he said doesn't correspond with what
happens: malloc does return something and __doesn't__ crash the
program. OOM killer kills the code upon an attempt to access that
memory.
But given the way he explained it, it's possible that he's affected by
OOM killer, and he forgot, or never knew, what really happened.
If you call malloc() and it overcommits, it won't crash the
program until you access the allocated memory. (The rationale for
overcommitting is that most programs don't actually use most of
the memory the memory the allocate. I find that odd)
Given the post you're responding to, I would find it more likely that
he knows exactly what happened and didn't mention it. Reading his
more recent followup, apparently it was on VMS, not Linux, and was
likely a bug in DEC's C library.
--
Keith Thompson (The_Other_Keith) (e-mail address removed) <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
If you call malloc() and it overcommits, it won't crash the
program until you access the allocated memory. (The rationale for
overcommitting is that most programs don't actually use most of
the memory the memory the allocate. I find that odd)
Yes it is, and they are doing that. Use e.g. MSVC with iterator checking
switched on (this is the default) and accessing e.g. a std::vector out of
bounds will generate a runtime error. With gcc one can use MALLOC_CHECK_
or -lmcheck, these also should catch some out-of-bounds access errors.
For raw pointers it is more difficult, you can use something like
ElectricFence or Valgrind on Linux, but it makes your program to run many
times slower and consume lots of more memory so this is just for
debugging.
If you call malloc() and it overcommits, it won't crash the
program until you access the allocated memory. (The rationale for
overcommitting is that most programs don't actually use most of
the memory the memory the allocate. I find that odd)
Really? The explanation that I'm most familiar with is that most fork
calls are immediately followed by exec, and thus if you're low on
memory, then a large process cannot spawn a new process without
overcommit because the only process create primitive is fork, which
"copies" the memory space of the parent process.
I of course think this is a broken state of affairs for several
reasons. 1- Just introduce a new create process primitive that creates
a process from an executable file with a copy options for specifying
the env vars, the command line, the working dir, etc.
(snip)
That was fixed about 20 years ago. Among others, there is vfork()
"vfork - spawn new process in a virtual memory efficient way"
A simple explanation is that vfork() tells the system that you
expect to call exec() next, and it can optimize for that case.
MSVC also has some debug options that try to catch writes past the
ends of allocated regions as well. IIRC, they overallocate, and put
special bit patterns at the start and end on allocation, and when
freed they check to see if those bit patterns are intact, raising a
fatal error or something if it finds a problem.
Joshua Maurice said:Really? The explanation that I'm most familiar with is that most fork
calls are immediately followed by exec, and thus if you're low on
memory, then a large process cannot spawn a new process without
overcommit because the only process create primitive is fork, which
"copies" the memory space of the parent process.
You're confusing overcommit with copy-on-write. Fork uses COW[*] in
which the parent and child share the physical pages until the child
writes to one - at that point, they child gets a copy (and an allocation
occurs which may fail at that point if memory and swap are exhausted).
Overcommit was allowed to support sparse arrays which are common
with some workloads. [...]
[*] COW came into general use in the SVR3.2/SVR4 timeframe. Linux has always
used COW on fork. The only cost for the child is the page table
(which actually can be quite a bit for a 1TB virtual address space using
4k pages - IIRC about 2GB just for page tables to map that much VA; makes
1G pages much more attractive (drops the page table size to 8k)).
(p.s. see 'posix_spawn').
If it is the implementation I'm thinking of, it would have eventually
returned NULL if you'd had enough swap space available.
There were implementations in the 90's (solaris, AIX) in which, to support
very large sparse arrays, would use lazy allocation with malloc, such that
malloc would allocate virtual address space, not actual memory. When each
page of the allocation was subsequently touched, it would be allocated from
the available memory pool or swap space. If there was insufficient space
available, the access would result in a Segmentation Violation (SIGSEGV).
So, if you completely exhaust the virtual address space, malloc would return
NULL (or if there wasn't a large enough contiguous chunk of VA space), otherwise
you'd potentially get an error sometime later when accessing the allocated
address space.
There was a flag to mallopt(3) if I recall correctly that would affect this
behavior.
I remember long discussions about this behavior at one of the X/Open meetings
in the 90's.
This has been done in hardware as well.
http://en.wikipedia.org/wiki/Bounds_checking mentions VAX, B6500 and
Burroughs. By some reason this approach has not really catched on.
This is exactly what Electric Fence does. It is also slow as hell, I
guess the most slowdown comes from where the array ends in a middle of a
virtual memory page and efence has to decide after a page fault if the
access was legal or not. For small allocations it also produces severe
memory fragmentation.
If you are only interested in arrays, then some compiler support would be
indeed helpful. Electric Fence intercepts all malloc calls and does not
know if these are meant for arrays or not (I guess), so it has to protect
all of them.
However, I would say that the best advice for avoiding out-of-bounds
access errors with raw pointers in C++ is to not use raw pointers.
a nifty tool I have used in some places is object-origin tracking, where
every time the allocator is accessed, it records where it was called
from, and will use this information (combined with some data-forensics)
to try to make an educated guess as to "who done it" (or, IOW, around
where the offending code might be).
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.