Skip to content

Commit 278edd0

Browse files
committed
[RemoteMirror] Add bounds checking to BitMask operations.
If we encounter any multi-paylaod enum descriptors with bad data, we can end up writing off the end of the bitmask allocation, causing heap corruption. Add range checks to let us fail gracefully instead. rdar://91423283 (cherry picked from commit a6de05b298278f5d3cc6e6f4b98994a458c28f47)
1 parent eaf561d commit 278edd0

File tree

1 file changed

+69
-10
lines changed

1 file changed

+69
-10
lines changed

stdlib/public/Reflection/TypeLowering.cpp

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#if SWIFT_ENABLE_REFLECTION
2222

23+
#include "llvm/Support/MathExtras.h"
2324
#include "swift/ABI/Enum.h"
2425
#include "swift/ABI/MetadataValues.h"
2526
#include "swift/Reflection/TypeLowering.h"
@@ -645,6 +646,8 @@ class SimpleMultiPayloadEnumTypeInfo: public EnumTypeInfo {
645646
// A variable-length bitmap used to track "spare bits" for general multi-payload
646647
// enums.
647648
class BitMask {
649+
static constexpr unsigned maxSize = 128 * 1024 * 1024; // 128MB
650+
648651
unsigned size; // Size of mask in bytes
649652
uint8_t *mask;
650653
public:
@@ -654,18 +657,72 @@ class BitMask {
654657
// Construct a bitmask of the appropriate number of bytes
655658
// initialized to all bits set
656659
BitMask(unsigned sizeInBytes): size(sizeInBytes) {
657-
assert(sizeInBytes < std::numeric_limits<uint32_t>::max());
660+
// Gracefully fail by constructing an empty mask if we exceed the size
661+
// limit.
662+
if (size > maxSize) {
663+
size = 0;
664+
mask = nullptr;
665+
return;
666+
}
667+
658668
mask = (uint8_t *)malloc(size);
669+
670+
if (!mask) {
671+
// Malloc might fail if size is large due to some bad data. Assert in
672+
// asserts builds, and fail gracefully in non-asserts builds by
673+
// constructing an empty BitMask.
674+
assert(false && "Failed to allocate BitMask");
675+
size = 0;
676+
return;
677+
}
678+
659679
memset(mask, 0xff, size);
660680
}
661681
// Construct a bitmask of the appropriate number of bytes
662682
// initialized with bits from the specified buffer
663-
BitMask(unsigned sizeInBytes, const uint8_t *initialValue, unsigned initialValueBytes, unsigned offset)
664-
: size(sizeInBytes)
665-
{
666-
assert(sizeInBytes < std::numeric_limits<uint32_t>::max());
667-
assert(initialValueBytes + offset <= sizeInBytes);
683+
BitMask(unsigned sizeInBytes, const uint8_t *initialValue,
684+
unsigned initialValueBytes, unsigned offset)
685+
: size(sizeInBytes) {
686+
// Gracefully fail by constructing an empty mask if we exceed the size
687+
// limit.
688+
if (size > maxSize) {
689+
size = 0;
690+
mask = nullptr;
691+
return;
692+
}
693+
694+
// Bad data could cause the initial value location to be off the end of our
695+
// size. If initialValueBytes + offset is beyond sizeInBytes (or overflows),
696+
// assert in asserts builds, and fail gracefully in non-asserts builds by
697+
// constructing an empty BitMask.
698+
bool overflowed = false;
699+
unsigned initialValueEnd =
700+
llvm::SaturatingAdd(initialValueBytes, offset, &overflowed);
701+
if (overflowed) {
702+
assert(false && "initialValueBytes + offset overflowed");
703+
size = 0;
704+
mask = nullptr;
705+
return;
706+
}
707+
assert(initialValueEnd <= sizeInBytes);
708+
if (initialValueEnd > size) {
709+
assert(false && "initialValueBytes + offset is greater than size");
710+
size = 0;
711+
mask = nullptr;
712+
return;
713+
}
714+
668715
mask = (uint8_t *)calloc(1, size);
716+
717+
if (!mask) {
718+
// Malloc might fail if size is large due to some bad data. Assert in
719+
// asserts builds, and fail gracefully in non-asserts builds by
720+
// constructing an empty BitMask.
721+
assert(false && "Failed to allocate BitMask");
722+
size = 0;
723+
return;
724+
}
725+
669726
memcpy(mask + offset, initialValue, initialValueBytes);
670727
}
671728
// Move constructor moves ownership and zeros the src
@@ -864,10 +921,12 @@ class BitMask {
864921

865922
void andNotMask(void *maskData, unsigned len, unsigned offset) {
866923
assert(offset < size);
867-
unsigned common = std::min(len, size - offset);
868-
uint8_t *maskBytes = (uint8_t *)maskData;
869-
for (unsigned i = 0; i < common; ++i) {
870-
mask[i + offset] &= ~maskBytes[i];
924+
if (offset < size) {
925+
unsigned common = std::min(len, size - offset);
926+
uint8_t *maskBytes = (uint8_t *)maskData;
927+
for (unsigned i = 0; i < common; ++i) {
928+
mask[i + offset] &= ~maskBytes[i];
929+
}
871930
}
872931
}
873932
};

0 commit comments

Comments
 (0)