473,888 Members | 1,528 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

A debugging implementation of malloc

In the C tutorial for lcc-win32, I have a small chapter
about a debugging implementation of malloc.

Here is the code, and the explanations that go with it.

I would appreciate your feedback both about the code and
the associated explanations.

---------------------------------------------------------------------

Instead of using directly malloc/free here are two implementations of
equivalent functions with some added safety features:
o Freeing NULL is allowed and is not an error.
o Double freeing is made impossible.
o Any overwrite immediately at the end of the block is checked for.
o Memory is initialized to zero.
o A count of allocated memory is kept in a global variable.

#include <stdlib.h>
#define MAGIC 0xFFFF
#define SIGNATURE 12345678L
size_t AllocatedMemory ;
void *allocate(size_ t size)
{
register char *r;
register int *ip = NULL;
size += 3 * sizeof(int);
r = malloc(size);
if (r == NULL)
return r;
AllocatedMemory += size;
ip = (int *) r;
// At the start of the block we write the signature
*ip++ = SIGNATURE;
// Then we write the size of the block in bytes
*ip++ = (int) size;
// We zero the data space
memset(ip, 0, size - 3*sizeof(int));
// We write the magic number at the end of the block,
// just behind the data
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;
// Return a pointer to the start of the data area
return (r + 2 * sizeof(int));
}
void release(void *pp)
{
register int *ip = NULL;
int s;
register char *p = pp;
if (p == NULL) // Freeing NULL is allowed
return;
// The start of the block is two integers before the data.
p -= 2 * sizeof(int);
ip = (int *) p;
if (*ip == SIGNATURE) {
// Overwrite the signature so that this block
// can’t be freed again
*ip++ = 0;
s = *ip;
ip = (int *) (&p[s - sizeof(int)]);
if (*ip != MAGIC) {
ErorPrintf(“Ove rwritten block size %d”, s);
return;
}
*ip = 0;
AllocatedMemory -= s;
free(p);
}
else {
/* The block has been overwritten. Complain. */
ErrorPrintf(“Wr ong block passed to release”);
}
}

The allocate function adds to the requested size space for 3 integers.
1) The first is a magic number (a signature) that allows the
identification of this block as a block allocated by our allocation
system.
2) The second is the size of the block. After this two numbers, the data
follows.
3) The data is followed by a third number that is placed at the end of
the block. Any memory overwrite of any block will overwrite probably
this number first. Since the “release” function checks for this, we
will be able to detect when a block has been overwritten.

At any time, the user can ask for the size of total allocated memory
(valid blocks in circulation) by querying the AllocatedMemory variable.

The “release function” accepts NULL (that is ignored). If the pointer
passed to it is not NULL, it will check that it is a valid block, and
that the signature is still there, i.e. that no memory overwrites have
happened during the usage of the block.
Jun 24 '06 #1
41 3367
jacob navia wrote:
In the C tutorial for lcc-win32, I have a small chapter
about a debugging implementation of malloc.

Here is the code, and the explanations that go with it.

I would appreciate your feedback both about the code and
the associated explanations.

Are there any unit tests?

--
Ian Collins.
Jun 24 '06 #2
On 2006-06-24, jacob navia <ja***@jacob.re mcomp.fr> wrote:
In the C tutorial for lcc-win32, I have a small chapter
about a debugging implementation of malloc.

Here is the code, and the explanations that go with it.

I would appreciate your feedback both about the code and
the associated explanations.

---------------------------------------------------------------------

Instead of using directly malloc/free here are two implementations of
equivalent functions with some added safety features:
o Freeing NULL is allowed and is not an error.
free already lets you free(NULL) without an error doesn't it?
#include <stdlib.h>
#define MAGIC 0xFFFF
#define SIGNATURE 12345678L
size_t AllocatedMemory ;
void *allocate(size_ t size)
{
register char *r;
register int *ip = NULL;
size += 3 * sizeof(int);
r = malloc(size);
if (r == NULL)
return r;
AllocatedMemory += size;
ip = (int *) r;
// At the start of the block we write the signature
*ip++ = SIGNATURE;
// Then we write the size of the block in bytes
*ip++ = (int) size;
You might as well store the size in your "metadata" as a size_t instead
of truncating to int-- no real harm in it, and people do allocate big
blocks sometimes.
// We zero the data space
memset(ip, 0, size - 3*sizeof(int));
// We write the magic number at the end of the block,
// just behind the data
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;


This will likely cause alignment problems-- suppose size were 17, and
sizeof (int) 4, malloc returns 16-byte aligned pointers, and that on the
target machine ints have to be stored at 4-byte aligned addresses.

r is 0x10 (say). &r[size - 4] will be 0x1d, which is not 4-byte aligned (0x1d %
4 is 1)

Better to make your magic number guard band something like an array of
char, so it can always go at the end of a block, whatever the size of
the block.
Jun 24 '06 #3
jacob navia wrote:
void release(void *pp)
{
register int *ip = NULL;
int s;
register char *p = pp;
if (p == NULL) // Freeing NULL is allowed
return;
// The start of the block is two integers before the data.
p -= 2 * sizeof(int);
Maybe consider an alignment check (p divisible by sizeof(int)) and check
for p > 2*sizeof(int) before the subtraction. Paranoid, put protects
against release( 7 );
ip = (int *) p;
if (*ip == SIGNATURE) {
// Overwrite the signature so that this block
// can’t be freed again
*ip++ = 0;
s = *ip;
I'd bring s (and call it size) into the if{} scope.
ip = (int *) (&p[s - sizeof(int)]);


Another paranoid alignment check here?

--
Ian Collins.
Jun 24 '06 #4
On Sat, 24 Jun 2006 23:00:35 +0200, jacob navia
<ja***@jacob.re mcomp.fr> wrote:
In the C tutorial for lcc-win32, I have a small chapter
about a debugging implementation of malloc.

Here is the code, and the explanations that go with it.

I would appreciate your feedback both about the code and
the associated explanations.

---------------------------------------------------------------------

Instead of using directly malloc/free here are two implementations of
equivalent functions with some added safety features:
o Freeing NULL is allowed and is not an error.
o Double freeing is made impossible.
o Any overwrite immediately at the end of the block is checked for.
o Memory is initialized to zero.
o A count of allocated memory is kept in a global variable.

#include <stdlib.h>
#define MAGIC 0xFFFF
#define SIGNATURE 12345678L
size_t AllocatedMemory ;
void *allocate(size_ t size)
{
register char *r;
register int *ip = NULL;
size += 3 * sizeof(int);
r = malloc(size);
if (r == NULL)
return r;
AllocatedMemory += size;
At this point r contains the address of a block of memory properly
aligned for any possible object.
ip = (int *) r;
// At the start of the block we write the signature
*ip++ = SIGNATURE;
If sizeof(int)*CHA R_BIT < 32, SIGNATURE will not fit.
// Then we write the size of the block in bytes
*ip++ = (int) size;
// We zero the data space
memset(ip, 0, size - 3*sizeof(int));
It is legal for the user to malloc 0 bytes. I could not find a
description of what memset does if the third argument is 0. Let's
hope it checks first.
// We write the magic number at the end of the block,
// just behind the data
ip = (int *) (&r[size - sizeof(int)]);
There is no guarantee that size provided by the user is a multiple of
sizeof(int). If it is not, then this address calculation produces an
misaligned result.
*ip = MAGIC;
And this attempt to store into the "misaligned int" invokes undefined
behavior.
// Return a pointer to the start of the data area
return (r + 2 * sizeof(int));
Here you give the user an address two int into the block of memory. If
double requires 8 byte alignment and sizeof(int) is 2, the address you
return is not suitable for storing a double. Similarly if sizeof(long
long) is 16 and sizeof(int) is 4 (probably a more likely situation
with new systems).
}

<snip>
Remove del for email
Jun 24 '06 #5
Barry Schwarz wrote:

At this point r contains the address of a block of memory properly
aligned for any possible object.

Is there a portable way of finding out what this alignment is?

--
Ian Collins.
Jun 24 '06 #6
jacob navia <ja***@jacob.re mcomp.fr> writes:
In the C tutorial for lcc-win32, I have a small chapter
about a debugging implementation of malloc.

Here is the code, and the explanations that go with it.

I would appreciate your feedback both about the code and
the associated explanations.

---------------------------------------------------------------------

Instead of using directly malloc/free here are two implementations of
equivalent functions with some added safety features:
o Freeing NULL is allowed and is not an error.
That's not an added safety feature; free(NULL) is explicitly required
to do nothing.
o Double freeing is made impossible.
Well, unlikely.
o Any overwrite immediately at the end of the block is checked for.
o Memory is initialized to zero.
Arguably, that could mask certain errors. For example, if I use the
standard malloc() function, and I attempt to access the allocated
memory before I've initialized it, I've invoked undefined behavior.
By initializing the memory to zero, you make such buggy code behave
consistently, making it more difficult to track down the error.

You might consider setting the allocated memory to a known bad value,
perhaps all-ones, or 0xDEADBEEF, or 0xE5E5E5E5.
o A count of allocated memory is kept in a global variable.

#include <stdlib.h>
#define MAGIC 0xFFFF
#define SIGNATURE 12345678L
size_t AllocatedMemory ;
void *allocate(size_ t size)
{
register char *r;
register int *ip = NULL;
The common wisdom is that "register" is more likely to interfere with
the compiler's optimizer than to actually make the code run faster. I
don't know how true that is. Declaring something as "register"
*should* be harmless as long as you don't try to take its address, but
it's a little odd to see it in new code.
size += 3 * sizeof(int);
r = malloc(size);
if (r == NULL)
return r;
AllocatedMemory += size;
ip = (int *) r;
// At the start of the block we write the signature
*ip++ = SIGNATURE;
// Then we write the size of the block in bytes
*ip++ = (int) size;
Why do you store the size as an int? (And why the unnecessary cast?)
It seems far more natural, more correct, and potentially more useful,
to store it as a size_t.
// We zero the data space
memset(ip, 0, size - 3*sizeof(int));
// We write the magic number at the end of the block,
// just behind the data
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;
// Return a pointer to the start of the data area
return (r + 2 * sizeof(int));
r is correctly aligned for any type. You assume here that
r + 2 * sizeof(int) is also correctly aligned for any type. That's
very likely to be true (and there's no portable way to check it),
but you should at least document the assumption.
}
void release(void *pp)
{
register int *ip = NULL;
int s;
register char *p = pp;
if (p == NULL) // Freeing NULL is allowed
return;
// The start of the block is two integers before the data.
p -= 2 * sizeof(int);
ip = (int *) p;
if (*ip == SIGNATURE) {
// Overwrite the signature so that this block
// can’t be freed again
*ip++ = 0;
s = *ip;
ip = (int *) (&p[s - sizeof(int)]);
if (*ip != MAGIC) {
ErorPrintf(“Ove rwritten block size %d”, s);
Typo: that should be "ErrorPrint f", not "ErorPrintf ". Also, your
quotation marks show up in my newsreader as "\223" and "\224" (I think
those are Windows-specific character codes). Both of these lead me to
suspect that you didn't copy this from an actual compilable source
file. (No need, I think, to go into the reasons why this is a Bad
Idea.)
return;
}
*ip = 0;
AllocatedMemory -= s;
free(p);
}
else {
/* The block has been overwritten. Complain. */
ErrorPrintf(“Wr ong block passed to release”);
}
}


I don't know what ErrorPrintf does, but printing error messages from a
memory allocation function seems like a bad idea. If it doesn't abort
the program, the caller won't know that the call failed; if it does,
the caller can't handle the problem (perhaps by attempting to do some
cleanup before terminating the program).

Since this is intended to be a replacement for free(), which has no
way to indicate a failure, it's difficult to come up with a good way
to indicate an error. Perhaps the best compromise would be to have
release() return a status value, 0 for success and any of several
defined error codes on failure. Or it could set errno.

[...]

--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 25 '06 #7
>Barry Schwarz wrote:
At this point [where r is the result of a malloc() call] r contains
the address of a block of memory properly aligned for any possible
object.

In article <4g************ *@individual.ne t>
Ian Collins <ia******@hotma il.com> wrote:Is there a portable way of finding out what this alignment is?


No. I consider this a flaw in the C standards.

If there *were* a portable way to find this, you could write portable
"augmented malloc and free" routines along the lines of those Mr Navia
has provided (with more work to deal with alignment, of course). But
there is not, so you cannot.

If you want to go ahead and write such routines, probably the best
approach is to mark any alignment assumptions you make, and try to
write the code such that, for porting to a new system with different
alignment requirements, changing one or two "#define"s is all that
is needed.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Jun 25 '06 #8
Chris Torek wrote:
Barry Schwarz wrote:
At this point [where r is the result of a malloc() call] r contains
the address of a block of memory properly aligned for any possible
object.


In article <4g************ *@individual.ne t>
Ian Collins <ia******@hotma il.com> wrote:
Is there a portable way of finding out what this alignment is?

No. I consider this a flaw in the C standards.

Thanks, I didn't think so.
If there *were* a portable way to find this, you could write portable
"augmented malloc and free" routines along the lines of those Mr Navia
has provided (with more work to deal with alignment, of course). But
there is not, so you cannot.
I've used max(sizeof(void *), sizeof(long double)) as a 'fairly safe'
value for alignment in similar situations.
If you want to go ahead and write such routines, probably the best
approach is to mark any alignment assumptions you make, and try to
write the code such that, for porting to a new system with different
alignment requirements, changing one or two "#define"s is all that
is needed.


I agree.

--
Ian Collins.
Jun 25 '06 #9
Ian Collins wrote:

I've used max(sizeof(void *), sizeof(long double)) as a 'fairly safe'
value for alignment in similar situations.

Oops, looking back I used max(sizeof(void *), sizeof(double)) . The
targets I was using didn't have long double and I don't use them anyway...

Where long double does exist, you'd have to use the lowest common
multiple of sizeof(void*) and sizeof(long double).

--
Ian Collins.
Jun 25 '06 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

12
7614
by: Anil | last post by:
Hi, Where can I get the source code that has the implementation of the below C operators/functions: malloc sizeof new strcmp strtok
83
4019
by: achintmehta | last post by:
I am working on MontaVista Linux on a PPC board. The compiler I am using is a gnu cross compiler cross compiler (ppc-82xx-gcc) The application/software that is being run, is messing up the memory due to which a subsequent malloc fails. When the application core is dumped I do not get any backtrace in the code (although the application is compiled with debug symbols, with -g option). The only thing I get is the address of the function...
4
1628
by: Sheldon | last post by:
Hi, I would like to view the contents of structure as part of a debugging routine. The structure is represented by a pointer ptr->data. I would like to view the first element in the array, data. This is part of debugging this program. A problem occured when a 2D binary array: ptr->data was copied to the another array in another structure via memcpy(). Since the codes is big, I will only post the relevant parts I feel is needed - I...
3
3528
by: C++Liliput | last post by:
Hi, I was looking at the implementation of operator new and operator new in gcc source code and found that the implementation is exactly the same. The only difference is that the size_t argument passed to the operators is calculated correctly during runtime and passed in. Inside the implementation both operators (new and new) do a simple malloc(). Ditto for operator delete/delete. I have two questions: 1) Who passes in the size_t argument...
33
2893
by: fmassei | last post by:
Hello! I made a short piece of code that I find very useful for debugging, and I wanted to ask you if it is correct, somehow acceptable or if I simply reinvented the wheel. To deal with some bad bugs caused by memory leaks I ended up with this simple solution: I made one header file that, when included, replaces the malloc/calloc/realloc/free functions with some other functions that do the actual job and insert (or remove) the pointers to...
0
9961
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9800
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
11178
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10777
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10882
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9597
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
7148
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5817
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
3
3251
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.