Skip to content

Commit 9d39b4b

Browse files
authored
Merge pull request #58525 from mikeash/bitmask-range-checks-5.7
[5.7][RemoteMirror] Add bounds checking to BitMask operations.
2 parents e101e9f + 278edd0 commit 9d39b4b

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)