Skip to content

Commit 8c7598c

Browse files
klauslerAlexisPerry
authored andcommitted
[flang][runtime] Interoperable POINTER deallocation validation (llvm#96100)
Extend the runtime validation of deallocated pointers so that it also works when pointers are allocated &/or deallocated outside Fortran. Previously, bogus runtime errors would be reported for pointers allocated via CFI_allocate() and deallocated in Fortran, and CFI_deallocate() did not check that it was deallocating a whole contiguous pointer that was allocated as such.
1 parent c466750 commit 8c7598c

File tree

4 files changed

+64
-29
lines changed

4 files changed

+64
-29
lines changed

flang/include/flang/Runtime/pointer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ bool RTDECL(PointerIsAssociated)(const Descriptor &);
115115
bool RTDECL(PointerIsAssociatedWith)(
116116
const Descriptor &, const Descriptor *target);
117117

118+
// Fortran POINTERs are allocated with an extra validation word after their
119+
// payloads in order to detect erroneous deallocations later.
120+
RT_API_ATTRS void *AllocateValidatedPointerPayload(std::size_t);
121+
RT_API_ATTRS bool ValidatePointerPayload(const ISO::CFI_cdesc_t &);
122+
118123
} // extern "C"
119124
} // namespace Fortran::runtime
120125
#endif // FORTRAN_RUNTIME_POINTER_H_

flang/runtime/ISO_Fortran_binding.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "terminator.h"
1414
#include "flang/ISO_Fortran_binding_wrapper.h"
1515
#include "flang/Runtime/descriptor.h"
16+
#include "flang/Runtime/pointer.h"
1617
#include "flang/Runtime/type-code.h"
1718
#include <cstdlib>
1819

@@ -75,7 +76,7 @@ RT_API_ATTRS int CFI_allocate(CFI_cdesc_t *descriptor,
7576
dim->sm = byteSize;
7677
byteSize *= extent;
7778
}
78-
void *p{byteSize ? std::malloc(byteSize) : std::malloc(1)};
79+
void *p{runtime::AllocateValidatedPointerPayload(byteSize)};
7980
if (!p && byteSize) {
8081
return CFI_ERROR_MEM_ALLOCATION;
8182
}
@@ -91,8 +92,11 @@ RT_API_ATTRS int CFI_deallocate(CFI_cdesc_t *descriptor) {
9192
if (descriptor->version != CFI_VERSION) {
9293
return CFI_INVALID_DESCRIPTOR;
9394
}
94-
if (descriptor->attribute != CFI_attribute_allocatable &&
95-
descriptor->attribute != CFI_attribute_pointer) {
95+
if (descriptor->attribute == CFI_attribute_pointer) {
96+
if (!runtime::ValidatePointerPayload(*descriptor)) {
97+
return CFI_INVALID_DESCRIPTOR;
98+
}
99+
} else if (descriptor->attribute != CFI_attribute_allocatable) {
96100
// Non-interoperable object
97101
return CFI_INVALID_DESCRIPTOR;
98102
}

flang/runtime/descriptor.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,16 @@ RT_API_ATTRS int Descriptor::Destroy(
199199
}
200200
}
201201

202-
RT_API_ATTRS int Descriptor::Deallocate() { return ISO::CFI_deallocate(&raw_); }
202+
RT_API_ATTRS int Descriptor::Deallocate() {
203+
ISO::CFI_cdesc_t &descriptor{raw()};
204+
if (!descriptor.base_addr) {
205+
return CFI_ERROR_BASE_ADDR_NULL;
206+
} else {
207+
std::free(descriptor.base_addr);
208+
descriptor.base_addr = nullptr;
209+
return CFI_SUCCESS;
210+
}
211+
}
203212

204213
RT_API_ATTRS bool Descriptor::DecrementSubscripts(
205214
SubscriptValue *subscript, const int *permutation) const {

flang/runtime/pointer.cpp

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,23 @@ void RTDEF(PointerAssociateRemapping)(Descriptor &pointer,
124124
}
125125
}
126126

127+
RT_API_ATTRS void *AllocateValidatedPointerPayload(std::size_t byteSize) {
128+
// Add space for a footer to validate during deallocation.
129+
constexpr std::size_t align{sizeof(std::uintptr_t)};
130+
byteSize = ((byteSize / align) + 1) * align;
131+
std::size_t total{byteSize + sizeof(std::uintptr_t)};
132+
void *p{std::malloc(total)};
133+
if (p) {
134+
// Fill the footer word with the XOR of the ones' complement of
135+
// the base address, which is a value that would be highly unlikely
136+
// to appear accidentally at the right spot.
137+
std::uintptr_t *footer{
138+
reinterpret_cast<std::uintptr_t *>(static_cast<char *>(p) + byteSize)};
139+
*footer = ~reinterpret_cast<std::uintptr_t>(p);
140+
}
141+
return p;
142+
}
143+
127144
int RTDEF(PointerAllocate)(Descriptor &pointer, bool hasStat,
128145
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
129146
Terminator terminator{sourceFile, sourceLine};
@@ -137,22 +154,12 @@ int RTDEF(PointerAllocate)(Descriptor &pointer, bool hasStat,
137154
elementBytes = pointer.raw().elem_len = 0;
138155
}
139156
std::size_t byteSize{pointer.Elements() * elementBytes};
140-
// Add space for a footer to validate during DEALLOCATE.
141-
constexpr std::size_t align{sizeof(std::uintptr_t)};
142-
byteSize = ((byteSize + align - 1) / align) * align;
143-
std::size_t total{byteSize + sizeof(std::uintptr_t)};
144-
void *p{std::malloc(total)};
157+
void *p{AllocateValidatedPointerPayload(byteSize)};
145158
if (!p) {
146159
return ReturnError(terminator, CFI_ERROR_MEM_ALLOCATION, errMsg, hasStat);
147160
}
148161
pointer.set_base_addr(p);
149162
pointer.SetByteStrides();
150-
// Fill the footer word with the XOR of the ones' complement of
151-
// the base address, which is a value that would be highly unlikely
152-
// to appear accidentally at the right spot.
153-
std::uintptr_t *footer{
154-
reinterpret_cast<std::uintptr_t *>(static_cast<char *>(p) + byteSize)};
155-
*footer = ~reinterpret_cast<std::uintptr_t>(p);
156163
int stat{StatOk};
157164
if (const DescriptorAddendum * addendum{pointer.Addendum()}) {
158165
if (const auto *derived{addendum->derivedType()}) {
@@ -176,6 +183,27 @@ int RTDEF(PointerAllocateSource)(Descriptor &pointer, const Descriptor &source,
176183
return stat;
177184
}
178185

186+
static RT_API_ATTRS std::size_t GetByteSize(
187+
const ISO::CFI_cdesc_t &descriptor) {
188+
std::size_t rank{descriptor.rank};
189+
const ISO::CFI_dim_t *dim{descriptor.dim};
190+
std::size_t byteSize{descriptor.elem_len};
191+
for (std::size_t j{0}; j < rank; ++j) {
192+
byteSize *= dim[j].extent;
193+
}
194+
return byteSize;
195+
}
196+
197+
bool RT_API_ATTRS ValidatePointerPayload(const ISO::CFI_cdesc_t &desc) {
198+
std::size_t byteSize{GetByteSize(desc)};
199+
constexpr std::size_t align{sizeof(std::uintptr_t)};
200+
byteSize = ((byteSize / align) + 1) * align;
201+
const void *p{desc.base_addr};
202+
const std::uintptr_t *footer{reinterpret_cast<const std::uintptr_t *>(
203+
static_cast<const char *>(p) + byteSize)};
204+
return *footer == ~reinterpret_cast<std::uintptr_t>(p);
205+
}
206+
179207
int RTDEF(PointerDeallocate)(Descriptor &pointer, bool hasStat,
180208
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
181209
Terminator terminator{sourceFile, sourceLine};
@@ -185,20 +213,9 @@ int RTDEF(PointerDeallocate)(Descriptor &pointer, bool hasStat,
185213
if (!pointer.IsAllocated()) {
186214
return ReturnError(terminator, StatBaseNull, errMsg, hasStat);
187215
}
188-
if (executionEnvironment.checkPointerDeallocation) {
189-
// Validate the footer. This should fail if the pointer doesn't
190-
// span the entire object, or the object was not allocated as a
191-
// pointer.
192-
std::size_t byteSize{pointer.Elements() * pointer.ElementBytes()};
193-
constexpr std::size_t align{sizeof(std::uintptr_t)};
194-
byteSize = ((byteSize + align - 1) / align) * align;
195-
void *p{pointer.raw().base_addr};
196-
std::uintptr_t *footer{
197-
reinterpret_cast<std::uintptr_t *>(static_cast<char *>(p) + byteSize)};
198-
if (*footer != ~reinterpret_cast<std::uintptr_t>(p)) {
199-
return ReturnError(
200-
terminator, StatBadPointerDeallocation, errMsg, hasStat);
201-
}
216+
if (executionEnvironment.checkPointerDeallocation &&
217+
!ValidatePointerPayload(pointer.raw())) {
218+
return ReturnError(terminator, StatBadPointerDeallocation, errMsg, hasStat);
202219
}
203220
return ReturnError(terminator,
204221
pointer.Destroy(/*finalize=*/true, /*destroyPointers=*/true, &terminator),

0 commit comments

Comments
 (0)