Thursday 15 August 2019

c++ - Double free or corruption (out) on return



I'm doing an assignment that involves calculating pi with threads. I've done this using mutex and it works fine, but I would like to get this version working as well. Here is my code.




#include 
#include
#include
#include
#include

using namespace std;

typedef struct{

int iterations; //How many iterations this thread is going to do
int offset; //The offset multiplier for the calculations (Makes sure each thread calculates a different part of the formula)
}threadParameterList;

vector partialSumList;

void* pi_calc(void* param){
threadParameterList* _param = static_cast(param);
double k = 1.0;
for(int i = _param->iterations * _param->offset + 1; i < _param->iterations * (_param->offset + 1); ++i){

partialSumList[_param->offset] += (double)k*(4.0/((2.0*i)*(2.0*i+1.0)*(2.0*i+2.0)));
k *= -1.0;
}
pthread_exit(0);
}

int main(int argc, char* argv[]){

//Error checking
if(argc != 3){

cout << "error: two parameters required [iterations][threadcount]" << endl;
return -1;
}
if(atoi(argv[1]) <= 0 || atoi(argv[2]) <= 0){
cout << "error: invalid parameter supplied - parameters must be > 0." << endl;
return -1;
}

partialSumList.resize(atoi(argv[2]));


vector threadList (atoi(argv[2]));
vector parameterList (atoi(argv[2]));

int iterations = atoi(argv[1]),
threadCount = atoi(argv[2]);

//Calculate workload for each thread
if(iterations % threadCount == 0){ //Threads divide evenly
for(int i = 0; i < threadCount; ++i){
parameterList[i].iterations = iterations/threadCount;

parameterList[i].offset = i;
pthread_create(&threadList[i], NULL, pi_calc, ¶meterList[i]);
}
void* status;
for(int i = 0; i < threadCount; ++i){
pthread_join(threadList[i], &status);
}

}
else{ //Threads do not divide evenly

for(int i = 0; i < threadCount - 1; ++i){
parameterList[i].iterations = iterations/threadCount;
parameterList[i].offset = i;
pthread_create(&threadList[i], NULL, pi_calc, ¶meterList[i]);
}
//Add the remainder to the last thread
parameterList[threadCount].iterations = (iterations % threadCount) + (iterations / threadCount);
parameterList[threadCount].offset = threadCount - 1;
pthread_create(&threadList[threadCount], NULL, pi_calc, ¶meterList[threadCount]);
void* status;

for(int i = 0; i < threadCount-1; ++i){
pthread_join(threadList[i], &status);
cout << status << endl;
}
}

//calculate pi
double pi = 3.0;
for(int i = 0; i < partialSumList.size(); ++i){
pi += partialSumList[i];

}

cout << "Value of pi: " << setw(15) << setprecision(15) << pi << endl;

return 0;
}


The code works fine in most cases. There are certain combinations of parameters that cause me to get a double free or corruption error on return 0. For example, if I use the parameters 100 and 10 the program creates 10 threads and does 10 iterations of the formula on each thread, works fine. If I use the parameters 10 and 4 the program creates 4 threads that do 2 iterations on 3 threads and 4 on the 4th thread, works fine. However, if I use 5 and 3, the program will correctly calculate the value and even print it out, but I get the error immediately after. This also happens for 17 and 3, and 10 and 3. I tried 15 and 7, but then I get a munmap_chunk(): invalid pointer error when the threads are trying to be joined - although i think that's something for another question.




If I had to guess, it has something to do with pthread_exit deallocating memory and then the same memory trying to be deallocated again on return, since I'm passing the parameter struct as a pointer. I tried a few different things like creating a local copy and defining parameterList as a vector of pointers, but it didn't solve anything. I've also tried eraseing and clearing the vector before return but that didn't help either.


Answer



I see this issue:



You are writing beyond the vector's bounds:



    vector parameterList (atoi(argv[2]));
//...
int threadCount = atoi(argv[2]);
//...

parameterList[threadCount].iterations = (iterations % threadCount) + (iterations / threadCount);
parameterList[threadCount].offset = threadCount - 1;


Accessing parameterList[threadCount] is out of bounds.



I don't see in the code where threadCount is adjusted, so it remains the same value throughout that snippet.



Tip: If the goal is to access the last item in a container, use vector::back(). It works all the time for non-empty vectors.




    parameterList.back().iterations = (iterations % threadCount) + (iterations / threadCount);
parameterList.back().offset = threadCount - 1;

No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...