Solved

Reuse pointer after free() -- double free error

Posted on 2011-03-01
20
678 Views
Last Modified: 2012-05-11
Hi *,

I think it is safe to reuse pointers after freeing them (right?). For exmaple,
float* t = (float*)calloc(100, sizeof(float));
free(t);
t = (float*)calloc(100, sizeof(float));
free(t);

Open in new window

works fine.

EDIT: that is a lie, it also gives a double free. Why?

However, I get a double free error from
float** v;
v = Get_2D_FloatArray(10, 10);
Free_2D_FloatArray(v);
v = Get_2D_FloatArray(10, 10);
Free_2D_FloatArray(v);

Open in new window


where

int** Get_2D_Int-Array(int dim1, int dim2) {
  int** v;
  v = (int**)calloc(dim2, sizeof(int*));
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));
  int i=0;
  for (i=1; i<dim1; i++) {
    v[i] = v[i-1] + dim2;
  }
  return v;
}

void Free_2D_int_Array(int** v) {
  free(v[0]);
  free(v);
  v[0] = NULL;
  v = NULL;
}

Open in new window


Any suggestions?
Cheers,
James
0
Comment
Question by:James_h1023
  • 7
  • 7
  • 3
  • +2
20 Comments
 
LVL 40

Expert Comment

by:evilrix
ID: 35007570
>> I think it is safe to reuse pointers after freeing them (right?).
A pointer is just a data-type, it can be re-assigned a value at any time (regardless of whether you free malloc/calloc allocated memory to it or not). The only requirement if you allocated from the heap is that at some point you free it.... but it don't have to be via the original pointer that the address was assigned too.

If you are getting a double free error you must be calling free on the same address more than once.
0
 
LVL 45

Expert Comment

by:Kdo
ID: 35007576
Hi James,

You can always reuse pointers.  As far as the program is concerned, they are just variables.  (Their contents just happens to be an address in memory instead of a "normal"item like an integer or character.)

It's a good idea to set the pointer to 0 (NULL) after freeing it.  This makes it easy for the program to track valid/obsolete pointers.  Most free() functions disregard the attempt to free memory when the passed value is NULL so if the program does try to free the same memory twice, having set the pointer to NULL after the first call to free() gets you past the error.


Good Luck,
Kent
0
 
LVL 4

Author Comment

by:James_h1023
ID: 35007620
Thanks for quick responses.

I thought it was safe to re-use them. As you say they are just variables.

So, why does my code produce a double free?

Cheers,
James
0
 
LVL 45

Expert Comment

by:Kdo
ID: 35007715
Hi James,

What are you seeing that says "double free"?


Kent
0
 
LVL 40

Expert Comment

by:evilrix
ID: 35007747
>> As you say they are just variables.

Indeed, variables that are specifically designed to hold address values just as (for example) an int is specifically designed to hold a numeric value.

I can see Kent is here so I'll leave you guys to work on this.

Good luck.
0
 
LVL 32

Expert Comment

by:phoffric
ID: 35007929
You must not deference a pointer after freeing it. Swap two lines.
void Free_2D_int_Array(int** v) {  
  free(v[0]);    // OK
  free(v);       // you free v here
  v[0] = NULL;   // now you are dereferencing v after freeing it
  v = NULL;  
}

Open in new window

0
 
LVL 4

Author Comment

by:James_h1023
ID: 35008945
Hmm ok that makes sense.

I have updated my get function to the attached, to check whether I am allocating memory okay, and I am.

Kent: on my second call to Free_2D_FloatArray(v); (line 5 in my first code attachement in first post) I get
*** glibc detected *** double free or corruption (out): 0x ... *

phoffric: I still get my double free error with both the NULL lines commented. But I would like them in, so how could I reorder them?

Cheers,
James
int** Get_2D_Int-Array(int dim1, int dim2) {
  int** v;
  v = (int**)calloc(dim2, sizeof(int*));
  if (v == NULL) {
    return NULL;
  }
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));
  if (v[0] == NULL) {
    return NULL;
  }
  int i=0;
  for (i=1; i<dim1; i++) {
    v[i] = v[i-1] + dim2;
  }
  return v;
}

void Free_2D_int_Array(int** v) {
  free(v[0]);
  free(v);
  v[0] = NULL;
  v = NULL;
}

Open in new window

0
 
LVL 32

Expert Comment

by:phoffric
ID: 35008990
If you swap lines 20 and 21, then that should help.
This way you free v[0], and then set v[0] to NULL. So now you are dereferencing v before you free it - OK.

Then you free v, and then set v to NULL - OK.
0
 
LVL 4

Author Comment

by:James_h1023
ID: 35009067
Thanks.

I have done this, however am still getting the double free problem.

Cheers,
James
0
 
LVL 4

Author Comment

by:James_h1023
ID: 35009091
That is a lie ... I now get free(): invalid next size (fast) on the second Free_ ...
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 32

Expert Comment

by:phoffric
ID: 35009146
What I wrote is a fix to a definite problem that you have, but I just realized that you commented out had commented out lines 21 and 22, so you have more than one problem.

You may have other corruption issues in your larger program, but as a test, could you see if this standalone program runs without error.

#include <stdlib.h>

int** Get_2D_Int_Array(int dim1, int dim2) {  
  int** v;  
  v = (int**)calloc(dim2, sizeof(int*));  
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));  
  int i=0;  
  for (i=1; i<dim1; i++) {  
    v[i] = v[i-1] + dim2;  
  }  
  return v;  
}  
  
void Free_2D_int_Array(int** v) {  
  free(v[0]);  
  v[0] = NULL;  
  free(v);  
  v = NULL;  
}

int main()
{
   int** v;  
   v = Get_2D_Int_Array(10, 10);  
   Free_2D_int_Array(v);  
   v = Get_2D_Int_Array(10, 10);  
   Free_2D_int_Array(v);
}

Open in new window

0
 
LVL 4

Author Comment

by:James_h1023
ID: 35009227
I've found it ...
it works as long as the dimensions are the same both times ... so 10, 10 and 10, 10, however I am (my fault, I didn't include it in here) doing 10, 10 the first time and 1000, 10 the second time. Does this make a difference?

The simple example you gave works for, as I say, the same dimensions, but not for different ones.

Cheers,
James
0
 
LVL 45

Expert Comment

by:Kdo
ID: 35009900
Hi James,

The size doesn't matter, as long as the program only references the memory in the current size.  That is if you allocate 1,000 bytes, free it, allocate 1,000,000 bytes, free it, and the allocation 5,000 bytes, byte 100,000 is valid only between the 2nd and 3rd allocations.


Kent
0
 
LVL 32

Expert Comment

by:phoffric
ID: 35009941
In my earlier post, I just immediately noticed a pointer problem which is now cleared up.

>>  I am doing 10, 10 the first time and 1000, 10 the second time. Does this make a difference?
The problem is not that you are doing these two things in succession. You have a problem even if you do not do 10, 10. Just do 1000, 10.

Now, what debugger do you have available? Do you have one that can show multiple memory regions? That would help you.

This single line results in erroneous code (which may or not show up as an immediate error - platform dependent):
   v = Get_2D_Int_Array(1000, 10);

Open in new window

In Get_2D_Int_Array, dim1 is 1000 and dim2 is 10. Here you are allocating a region suitable for 10 pointers.
    v = (int**)calloc(dim2, sizeof(int*));

Open in new window

But then here you are setting 1000 pointers:
     for (i=1; i<dim1; i++)
          v[i] = v[i-1] + dim2;

Open in new window

which goes beyond the allocated region.
0
 
LVL 32

Expert Comment

by:phoffric
ID: 35010225
The main problem, then, is not freeing twice, but overwriting memory that was not allocated to you.

When dealing with memory allocation errors, the actual errors reported can be very misleading, and the detection of a problem will vary from one system to another. In fact, it is possible that some errors will not be immediately detected until you make a small change to a different part of the program.

Now doing this probably will not cause an error:
   v = Get_2D_Int_Array(10, 10);  
   Free_2D_int_Array(v);  
   v = Get_2D_Int_Array(10, 1000);  
   Free_2D_int_Array(v);

Open in new window

I did notice one other interesting syntactical issue. In Free_2D_int_Array, you set v to NULL; but v is an auto variable in Free_2D_int_Array's stack. So, when you return to main, the v there should not be affected.

You should consider another design for dynamically creating 2d arrays. Here is one approach:  http://rdsrc.us/IApazh

You can get a good deal of useful practical information from this EE search: http://rdsrc.us/CxPzLP
0
 
LVL 32

Expert Comment

by:phoffric
ID: 35012037
Often, we see a 2d matrix described as nRows x nCols, which would correspond to your dim1 and dim2, respectively. Then to set up the matrix using your code, first allocate enough space for nRows row pointers. So, v[ i ] are pointers to the start of each row. You can allocate v[0] as you have done. But, in the loop, you have to set the remaining (nRows - 1) row pointers.
0
 
LVL 4

Author Comment

by:James_h1023
ID: 35015770
Morning (here),

Many thanks for your responses.

I have swapped dim1 and dim2, and indeed that did fix my error, and now I have the attached (I also fixed my automatic variable problem, I think?)

I used this approach over the one you linked for less allocations and more contiguous memory (this is related to a previous question, where I must have change the 3d version to a 2d version incorrectly, i.e. having dim1 and dim2 around the wrong way: previous question.

But it is still "double freeing" randomly. Some times it works fine, other times not, other times fine.

Firstly, I suppose, is the code attached fine now? I may be doing something somewhere else (although i'm pretty sure not).

I have gdb available, will that help? I must admit the only thing I can do with it, is put breaks in and evaluate variables!

Cheers,
James
int** Get_2D_Int-Array(int dim1, int dim2) {
  int** v;
  v = (int**)calloc(dim1, sizeof(int*));
  if (v == NULL) {
    return NULL;
  }
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));
  if (v[0] == NULL) {
    return NULL;
  }
  int i=0;
  for (i=1; i<dim2; i++) {
    v[i] = v[i-1] + dim2;
  }
  return v;
}

void Free_2D_int_Array(int*** v) {
  free(*v[0]);
  free(*v);
  *v[0] = NULL;
  *v = NULL;
}

Open in new window

0
 
LVL 32

Accepted Solution

by:
phoffric earned 500 total points
ID: 35015984
gdb should help (I never use it; I prefer graphical debugger such as ddd or netbeans - in one case, I found ddd better in identifying a problem). The data graphical features of ddd are very useful. If you do not have it, I recommend that you download it.

Your Free_2D_int_Array still has problems in that after you free, you are derefercing.

I'm not really sure that you need to set v to NULL or to fix the autovariable issue that I brought up. But if you really want to have the caller see a null pointer, then rather than deal with *** (I think ** is already complicated enough), why not just return the pointer:
 int** Free_2D_int_Array(int** v) {    
   free(v[0]);    
   v[0] = NULL;    
   free(v);    
   v = NULL;    
   return v;
}
// main():
...
  v = Free_2D_int_Array(v);  // after return from function, v will be NULL
}  

Open in new window

As mentioned earlier, I like to think of a 2d array as a matrix having nRows and nCols (replacing dim1 and dim2, respectively).
How many row pointers have you allocated for v in line 3?
            v = (int**)calloc(dim1, sizeof(int*));
When writing v[ i ], what is the range of valid i ?

In lines 12-13 you are looping over nCol pointers:
           for (i=1; i<dim2; i++) {
                  v[ i ] = v[i-1] + dim2;
Compare this with my earlier comment, "But, in the loop, you have to set the remaining (nRows - 1) row pointers."
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35027821
in line 12 the for loop should go from 1 to dim1 - 1 (and not dim2) cause you allocated dim1 "rows".

in line 20 and 21 you shouldn't dereference cause the pointers were freed.

look at phoffric's code it is fine. or omit the setting to NULL. it makes not so much sense cause it is local variables only.

Sara

0
 
LVL 4

Author Closing Comment

by:James_h1023
ID: 35226379
Apologies for the sever delay.

Many thanks,
James
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Summary: This tutorial covers some basics of pointer, pointer arithmetic and function pointer. What is a pointer: A pointer is a variable which holds an address. This address might be address of another variable/address of devices/address of fu…
Windows programmers of the C/C++ variety, how many of you realise that since Window 9x Microsoft has been lying to you about what constitutes Unicode (http://en.wikipedia.org/wiki/Unicode)? They will have you believe that Unicode requires you to use…
The goal of this video is to provide viewers with basic examples to understand how to use strings and some functions related to them in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use conditional statements in the C programming language.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now