-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[flang][runtime] Validate pointer DEALLOCATE #78612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -129,17 +129,38 @@ int RTDEF(PointerAllocate)(Descriptor &pointer, bool hasStat, | |
if (!pointer.IsPointer()) { | ||
return ReturnError(terminator, StatInvalidDescriptor, errMsg, hasStat); | ||
} | ||
int stat{ReturnError(terminator, pointer.Allocate(), errMsg, hasStat)}; | ||
if (stat == StatOk) { | ||
if (const DescriptorAddendum * addendum{pointer.Addendum()}) { | ||
if (const auto *derived{addendum->derivedType()}) { | ||
if (!derived->noInitializationNeeded()) { | ||
stat = Initialize(pointer, *derived, terminator, hasStat, errMsg); | ||
} | ||
std::size_t elementBytes{pointer.ElementBytes()}; | ||
if (static_cast<std::int64_t>(elementBytes) < 0) { | ||
// F'2023 7.4.4.2 p5: "If the character length parameter value evaluates | ||
// to a negative value, the length of character entities declared is zero." | ||
elementBytes = pointer.raw().elem_len = 0; | ||
} | ||
std::size_t byteSize{pointer.Elements() * elementBytes}; | ||
// Add space for a footer to validate during DEALLOCATE. | ||
constexpr std::size_t align{sizeof(std::uintptr_t)}; | ||
byteSize = ((byteSize + align - 1) / align) * align; | ||
std::size_t total{byteSize + sizeof(std::uintptr_t)}; | ||
void *p{std::malloc(total)}; | ||
if (!p) { | ||
return ReturnError(terminator, CFI_ERROR_MEM_ALLOCATION, errMsg, hasStat); | ||
} | ||
pointer.set_base_addr(p); | ||
pointer.SetByteStrides(); | ||
// Fill the footer word with the XOR of the ones' complement of | ||
// the base address, which is a value that would be highly unlikely | ||
// to appear accidentally at the right spot. | ||
std::uintptr_t *footer{ | ||
reinterpret_cast<std::uintptr_t *>(static_cast<char *>(p) + byteSize)}; | ||
*footer = ~reinterpret_cast<std::uintptr_t>(p); | ||
int stat{StatOk}; | ||
if (const DescriptorAddendum * addendum{pointer.Addendum()}) { | ||
if (const auto *derived{addendum->derivedType()}) { | ||
if (!derived->noInitializationNeeded()) { | ||
stat = Initialize(pointer, *derived, terminator, hasStat, errMsg); | ||
} | ||
} | ||
} | ||
return stat; | ||
return ReturnError(terminator, stat, errMsg, hasStat); | ||
} | ||
|
||
int RTDEF(PointerAllocateSource)(Descriptor &pointer, const Descriptor &source, | ||
|
@@ -163,6 +184,18 @@ int RTDEF(PointerDeallocate)(Descriptor &pointer, bool hasStat, | |
if (!pointer.IsAllocated()) { | ||
return ReturnError(terminator, StatBaseNull, errMsg, hasStat); | ||
} | ||
// Validate the footer. This should fail if the pointer doesn't | ||
// span the entire object, or the object was not allocated as a | ||
// pointer. | ||
std::size_t byteSize{pointer.Elements() * pointer.ElementBytes()}; | ||
constexpr std::size_t align{sizeof(std::uintptr_t)}; | ||
byteSize = ((byteSize + align - 1) / align) * align; | ||
void *p{pointer.raw().base_addr}; | ||
std::uintptr_t *footer{ | ||
reinterpret_cast<std::uintptr_t *>(static_cast<char *>(p) + byteSize)}; | ||
if (*footer != ~reinterpret_cast<std::uintptr_t>(p)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Playing the devil's advocate here, there is a slight chance for this But the only safe way I can think of to do the check your patch is adding without this issue would be to maintain some runtime pointer allocation table, and this may be overkill/no very parallelism friendly. |
||
return ReturnError(terminator, StatBadPointerDeallocation, errMsg, hasStat); | ||
} | ||
return ReturnError(terminator, | ||
pointer.Destroy(/*finalize=*/true, /*destroyPointers=*/true, &terminator), | ||
errMsg, hasStat); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be the same check in
genDeallocate
below?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bad deallocations are being caught in my testing without it, but I don't really understand the flow here. Should I just add the check to
genDeallocate()
anyway?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bet it's the presence of
stat=
in my tests that makes the difference.PointerDeallocate()
should be called even withoutstat=
. Will update.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#79702