Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pthread_join corrupts pthread_create argument in the stack

I got this code from an advanced Linux programming book. When I try to execute the code under Linux 64bit environment the value of the which_prime variable gets corrupted (changes to 0) after pthread_join() function call.

In this example why the value of which_prime gets corrupted after running pthread_join?

In general can we use the 4th argument passed to pthread_create function safely inside main even if we call other functions like pthread_join()?

#include <pthread.h>
#include <stdio.h>

/* Compute successive prime numbers (very inefficiently). Return the
Nth prime number, where N is the value pointed to by *ARG. */
void* compute_prime (void* arg)
{
    int candidate = 2;

    int n = *((int*) arg);
    while (1) {
        int factor;
        int is_prime = 1;
        /* Test primality by successive division. */
        for (factor = 2; factor < candidate; ++factor)
            if (candidate % factor == 0) {
                is_prime = 0;
                break;
            }
        /* Is this the prime number we’re looking for? */
        if (is_prime) {
            if (--n == 0)
                /* Return the desired prime number as the thread return value. */
                return (void*) candidate;
        }
        ++candidate;
    }
    return NULL;
}

int main ()
{
    pthread_t thread;
    int which_prime = 5000;
    int prime;
    /* Start the computing thread, up to the 5,000th prime number. */
    pthread_create (&thread, NULL, &compute_prime, &which_prime);
    /* Do some other work here... */
    /* Wait for the prime number thread to complete, and get the result. */
    pthread_join (thread, (void*) &prime);
    /* Print the largest prime it computed. */
    printf(“The %dth prime number is %d.\n”, which_prime, prime);
    return 0;
}
like image 995
m-adolf Avatar asked Sep 29 '22 17:09

m-adolf


1 Answers

We've arrived at a point in time where it is no longer safe to convert between an int and a pointer. That's because there are 64-bit systems where a pointer is 64-bits, but an int is only 32-bits.

So assuming 32-bit int and 64-bit pointer, here's what's happening in your code. The second argument to pthread_join is a pointer-to-a-pointer. In other words, you should be passing the address of a pointer (the address of a 64-bit value). Instead, you are passing the address of prime (the address of a 32-bit value). When pthread_join writes the result, it overwrites which_prime, because which_prime follows prime in memory.

To fix the problem, you need to avoid converting between ints and pointers. One way to do that is to avoid using the second parameter of pthread_join, as demonstrated by the following code.

#include <stdio.h>
#include <pthread.h>

#define NUM_THREADS 20

typedef struct
{
    int success;
    int input;
    int output;
} stData;

void *doSomething( void *arg )
{
    stData *dataptr = arg;

    dataptr->success = 1;
    dataptr->output  = dataptr->input * 2;
    return NULL;
}

int main( void )
{
    int i;
    pthread_t id[NUM_THREADS];
    stData data[NUM_THREADS] = {{0}};

    for ( i = 0; i < NUM_THREADS; i++ )
    {
        data[i].input = i + 1;
        pthread_create( &id[i], NULL, doSomething, &data[i] );
    }

    for ( i = 0; i < NUM_THREADS; i++ )
    {
        pthread_join( id[i], NULL );

        if ( data[i].success )
            printf( "thread %2d: input=%2d output=%2d\n", i+1, data[i].input, data[i].output );
        else
            printf( "thread %2d: failed\n", i+1 );
    }

    return 0;
}
like image 110
user3386109 Avatar answered Oct 06 '22 20:10

user3386109