Memory address truncation issue during dynamic memory allocation

117 Views Asked by At

I have a fortran subroutine (getmem.F) that uses a C program (memalloc.c) for dynamic memory allocation. The following subroutine and functions are a part of a very old and large code that was used for research purposes. I am compiling on a 64 bit machine and I am using a PGI compiler 2020.4 version. The following is the fortran code.

      subroutine getmem(ptr, size)

c-----get pointered memory

cdir$ nolist
      include 'common.h'
      include 'inputcom.h'
cdir$ list

      integer memalloc, memfree
      integer ptr, size, ierr0

c-----allocate 'size' words of memory and send back pointer in 'ptr'

      print *, "Value of ptr and size before memory allocation"
      print *, "ptr:", ptr               ! The value of ptr is 0
      print *, "size:", size             ! The value of size is 1138280

      if (size .lt. 0) then
        ierr0 = memfree (ptr)
      else
        ierr0 = memalloc (ptr, size)
      endif

      print *, "Value of ptr and size after memory allocation"
      print *, "ptr:", ptr               ! The value of ptr is -1431465968 (This is incorrect 
                                         ! value which is the decimal from signed 2's complement for 2863501328)
      print *, "size:", size             ! The value of size is 1138280

      if (ierr0 .ne. 0) then
        call writemsg ('Unable to get memory.')
        write (buffer, 100) size
  100   format ('GETMEM:  size is ', i15, '.')
        call writemsg (buffer)
        stop 'getmem'
      endif

      return
      end

Here is the C code memalloc.c

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h> /* Added by Surya */

int memalloc_ (int *nextptr, int *size)

{
  void *ptr;

  printf("Address of nextptr:%p \nValue of nextptr: %d \n", nextptr, *nextptr);
  /*Output: Address of nextptr: 0x1687ad0 
            Value of nextptr: 0 */

  /* Before Memory Allocation */

  printf("Address pointed by ptr (Value stored in ptr): %p \n", (void*)ptr);
  /*Output: Address pointed by ptr (Value stored in ptr): 0x7fffffff8b40 */   
         
  if (*nextptr == NULL) {
    if ((ptr = (void *) malloc (*size)) == NULL) {
      return(-1);
    }
  }
  else {
    if ((ptr = (void *) realloc (*nextptr, *size)) == NULL) {
      return(-1);
    }
  }
  /* After memory allocation using malloc() */

  printf("Address pointed by ptr (Value stored in ptr): %p \nValue when printed as a long integer %ld \n", (void*)ptr, (int*)ptr);
  /* Output: Address pointed by ptr (Value stored in ptr): 0x2aaaaaad9010
             Value when printed as a long integer: 46912496308240 */

  *nextptr = (int) ptr;

  printf("Address of nextptr:%p \nValue of nextptr: %d \n", nextptr, *nextptr);
  /* Output: Address of nextptr: 0x1687ad0
             Value of nextptr: -1431465968  */

  printf("Value of nextptr in long format: %ld \n", *nextptr);
  /* Output: Value of nextptr in long format: 2863501328  */

  return (0);
}

When I do hexadecimal to decimal conversions and vice versa (https://www.rapidtables.com/convert/number/hex-to-decimal.html), I notice what the issue is. When we do (int) ptr. The hexadecimal value of ptr = 0x2aaaaaad9010 (decimal value is 46912496308240) is truncated to aaad9010 (decimal value is 2863501328). When this gets passed back into the fortran77 code as ptr, the ptr which is later used as pointer to point to memory is unable to find the variable and its value. How do I fix this issue so that the truncation does not happen in the C program?

P.S. This is very old style of coding but it is part of a much larger code (Validated with experiments) of my research.

1

There are 1 best solutions below

9
Lundin On
  • (Bug) In order to pass something back to the caller through a parameter, you must use a pointer to pointer.
  • (Bug) The (int) ptr cast is non-portable and fairly obviously the culprit of your narrowing conversion.
  • (Bug) You can't print the contents of ptr before assigning it to point somewhere. You can't print the contents pointed at by nextptr before assigning a value. You cannot print the value of a null pointer.
  • (Design) There is no reason to pass size as a pointer since you don't change it inside the function.
  • (Design) Casting the result of malloc to void* is pointless.
  • (Design) Casting from void* to void* is pointless.
  • (Design) realloc behaves like malloc if passed a NULL pointer. Since your function apparently assumes that the passed pointer is always initialized as a null pointer during the first call, you can utilize that to simplify the code.
  • (Possible bug) Alternatively, if the passed pointer isn't initialized, your code is completely bugged - you cannot call realloc with an uninitialized pointer as parameter.

With all the bugs and clutter removed, your code might look something like this:

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>

// NOTE: *nextptr must be initialized to null outside this function!
int memalloc_ (int** nextptr, int size)

{
  void *ptr;
  printf("Address of *nextptr:%p\n", (void*)*nextptr);
         
  ptr = realloc(*nextptr, size); 
  if (ptr == NULL) {
      return -1;
  }
  *nextptr = ptr;
  printf("Address pointed by ptr (Value stored in ptr): %p\n", ptr);
  printf("Address of nextptr:%p\n", (void*)*nextptr);
  return 0;
}

This should compile even in old C90.