Reset (deallocate / nullify) a Fortran allocatable array that has been corrupted

883 views Asked by At

When a situation such as described in Incorrect fortran errors: allocatable array is already allocated; DEALLOCATE points to an array that cannot be deallocated happens (corrupted memory leaves an allocatable array that appears allocated but does not "point" to a valid address), is there anything that can be done within Fortran to cure it, i.e., reset the array as deallocated, without trying to deallocate the memory it points to?

The situation is a Fortran/C program where a piece of C code purposefully corrupts (writes garbage to) allocated memory. This works fine for arrays of normal types. But with an allocatable array of a user-defined type, which includes itself an allocatable component, the garbage written to the portion belonging to the allocatable component means that now the component appears as allocated, even though it's not. Rather than making the C code aware of what it should corrupt or not, I'd prefer fixing it after, but "nullifying" the allocatable component, when I know I don't care about the memory it currently appears to point to. With a pointer, it would be just a matter of nullify, but with an allocatable array?

1

There are 1 answers

4
Vladimir F Героям слава On BEST ANSWER

If the memory is really corrupted as in stack corruption/heap corruption. You cannot do anything. The program is bound to fail because the very low-level information is lost. This is true for any programming language, even C.

If, what is corrupted, is the Fortran array descriptor, you cannot correct it from Fortran. Fortran does not expose these implementation details to Fortran programmers. It is only available via special headers called ISO_Fortran_binding.h from C.

If the only corruption that happened was making Fortran thing that the array is allocated where it isn't, it should be rather simple to revert that from C. All it should be necessary is to change the address of the allocated memory. Allocatable arrays are always contiguous.

One could also try dirty tricks like telling a subroutine that what you are passing is a pointer when it in fact is an allocatable and nullify it. It will likely work in many implementations. But nullifying the address in a controllable way is much cleaner. Even if it is just a one nullifying C function you call from Fortran.

Because you really only want to change the address to 0 and not make any other special stuff with the array extents, strides and other details, it should be simple to do even without the header.

Note that the descriptor will still contain nonsense data in other variables, but those should not matter.


This is a quick and dirty test:

Fortran:

  dimension A(:,:)
  allocatable A
  
  interface
    subroutine write_garbage(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
    subroutine c_null_alloc(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
  end interface
  
  call write_garbage(A)
  
  print *, allocated(A)
  
  call c_null_alloc(A)
  
  print *, allocated(A)
  
end

C:

#include <stdint.h>
void write_garbage(intptr_t* A){
  *A = 999;
}

void c_null_alloc(intptr_t* A){
  *A = 0;
}

result:

> gfortran c_allocatables.c c_allocatables.f90
> ./a.out 
 T
 F

A proper version should use ISO_Fortran_binding.h if your compiler provides it. And implicit none and other boring stuff...


A very dirty (and illegal) hack that I do not recommend at all:

  dimension A(:,:)
  allocatable A
  
  interface
    subroutine write_garbage(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
    subroutine null_alloc(A) bind(C)
      dimension A(:,:)
      allocatable A
    end subroutine
      
  end interface
  
  call write_garbage(A)
  
  print *, allocated(A)
  
  call null_alloc(A)
  
  print *, allocated(A)
  
end

subroutine null_alloc(A) bind(C)
      dimension A(:,:)
      pointer A
      
      A => null()
end subroutine
> gfortran c_allocatables.c c_allocatables.f90
c_allocatables.f90:27:21:

   10 |     subroutine null_alloc(A) bind(C)
      |                         2
......
   27 | subroutine null_alloc(A) bind(C)
      |                     1
Warning: ALLOCATABLE mismatch in argument 'a' between (1) and (2)
> ./a.out 
 T
 F