Modified: trunk/Source/WTF/ChangeLog (143399 => 143400)
--- trunk/Source/WTF/ChangeLog 2013-02-19 23:58:25 UTC (rev 143399)
+++ trunk/Source/WTF/ChangeLog 2013-02-20 00:13:26 UTC (rev 143400)
@@ -1,3 +1,30 @@
+2013-02-19 Oliver Hunt <oli...@apple.com>
+
+ Moar hardening
+ https://bugs.webkit.org/show_bug.cgi?id=110275
+
+ Reviewed by Anders Carlsson.
+
+ We now poison objects when they get freed, and verify that
+ any object that is being freed is not poisoned. If the
+ object looks like it's poisoned we validate the freelist,
+ and ensure the object is not already present. If it is
+ we crash.
+
+ On allocation, we ensure that the object being allocated
+ is poisoned, then clear the poisoning fields.
+
+ * wtf/FastMalloc.cpp:
+ (WTF::internalEntropyValue):
+ (WTF):
+ (WTF::freedObjectStartPoison):
+ (WTF::freedObjectEndPoison):
+ (TCMalloc_ThreadCache_FreeList):
+ (WTF::TCMalloc_ThreadCache_FreeList::Validate):
+ (WTF::TCMalloc_Central_FreeList::Populate):
+ (WTF::TCMalloc_ThreadCache::Allocate):
+ (WTF::TCMalloc_ThreadCache::Deallocate):
+
2013-02-18 Darin Adler <da...@apple.com>
Style tweaks to StringHasher.h
Modified: trunk/Source/WTF/wtf/FastMalloc.cpp (143399 => 143400)
--- trunk/Source/WTF/wtf/FastMalloc.cpp 2013-02-19 23:58:25 UTC (rev 143399)
+++ trunk/Source/WTF/wtf/FastMalloc.cpp 2013-02-20 00:13:26 UTC (rev 143400)
@@ -544,7 +544,8 @@
}
};
-static ALWAYS_INLINE uintptr_t internalEntropyValue() {
+static ALWAYS_INLINE uintptr_t internalEntropyValue()
+{
static uintptr_t value = EntropySource<sizeof(uintptr_t)>::value();
ASSERT(value);
return value;
@@ -554,12 +555,65 @@
#define ROTATE_VALUE(value, amount) (((value) >> (amount)) | ((value) << (sizeof(value) * 8 - (amount))))
#define XOR_MASK_PTR_WITH_KEY(ptr, key, entropy) (reinterpret_cast<typeof(ptr)>(reinterpret_cast<uintptr_t>(ptr)^(ROTATE_VALUE(reinterpret_cast<uintptr_t>(key), MaskKeyShift)^entropy)))
+
+static ALWAYS_INLINE uint32_t freedObjectStartPoison()
+{
+ static uint32_t value = EntropySource<sizeof(uint32_t)>::value();
+ ASSERT(value);
+ return value;
+}
+
+static ALWAYS_INLINE uint32_t freedObjectEndPoison()
+{
+ static uint32_t value = EntropySource<sizeof(uint32_t)>::value();
+ ASSERT(value);
+ return value;
+}
+
+#define PTR_TO_UINT32(ptr) static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr))
+#define END_POISON_INDEX(allocationSize) (((allocationSize) - sizeof(uint32_t)) / sizeof(uint32_t))
+#define POISON_ALLOCATION(allocation, allocationSize) do { \
+ reinterpret_cast<uint32_t*>(allocation)[0] = 1; \
+ reinterpret_cast<uint32_t*>(allocation)[1] = 1; \
+ if (allocationSize < 4 * sizeof(uint32_t)) \
+ break; \
+ reinterpret_cast<uint32_t*>(allocation)[2] = 1; \
+ reinterpret_cast<uint32_t*>(allocation)[END_POISON_INDEX(allocationSize)] = 1; \
+} while (false);
+
+#define POISON_DEALLOCATION_EXPLICIT(allocation, allocationSize, startPoison, endPoison) do { \
+ if (allocationSize < 4 * sizeof(uint32_t)) \
+ break; \
+ reinterpret_cast<uint32_t*>(allocation)[2] = (startPoison) ^ PTR_TO_UINT32(allocation); \
+ reinterpret_cast<uint32_t*>(allocation)[END_POISON_INDEX(allocationSize)] = (endPoison) ^ PTR_TO_UINT32(allocation); \
+} while (false)
+
+#define POISON_DEALLOCATION(allocation, allocationSize) \
+ POISON_DEALLOCATION_EXPLICIT(allocation, allocationSize, freedObjectStartPoison(), freedObjectEndPoison())
+
+#define MAY_BE_POISONED(allocation, allocationSize) (((allocationSize) >= 4 * sizeof(uint32_t)) && ( \
+ (reinterpret_cast<uint32_t*>(allocation)[2] == (freedObjectStartPoison() ^ PTR_TO_UINT32(allocation))) || \
+ (reinterpret_cast<uint32_t*>(allocation)[END_POISON_INDEX(allocationSize)] == (freedObjectEndPoison() ^ PTR_TO_UINT32(allocation))) \
+))
+
+#define IS_DEFINITELY_POISONED(allocation, allocationSize) (((allocationSize) < 4 * sizeof(uint32_t)) || ( \
+ (reinterpret_cast<uint32_t*>(allocation)[2] == (freedObjectStartPoison() ^ PTR_TO_UINT32(allocation))) && \
+ (reinterpret_cast<uint32_t*>(allocation)[END_POISON_INDEX(allocationSize)] == (freedObjectEndPoison() ^ PTR_TO_UINT32(allocation))) \
+))
+
#else
+
+#define POISON_ALLOCATION(allocation, allocationSize)
+#define POISON_DEALLOCATION(allocation, allocationSize)
+#define POISON_DEALLOCATION_EXPLICIT(allocation, allocationSize, startPoison, endPoison)
+#define MAY_BE_POISONED(allocation, allocationSize) (false)
+#define IS_DEFINITELY_POISONED(allocation, allocationSize) (true)
#define XOR_MASK_PTR_WITH_KEY(ptr, key, entropy) (((void)entropy), ((void)key), ptr)
+
#define HARDENING_ENTROPY 0
+
#endif
-
//-------------------------------------------------------------------
// Configuration
//-------------------------------------------------------------------
@@ -2530,6 +2584,17 @@
return SLL_Pop(&list_, entropy_).value();
}
+ // Runs through the linked list to ensure that
+ // we can do that, and ensures that 'missing'
+ // is not present
+ NEVER_INLINE void Validate(HardenedSLL missing) {
+ HardenedSLL node = list_;
+ while (node) {
+ RELEASE_ASSERT(node != missing);
+ node = SLL_Next(node, entropy_);
+ }
+ }
+
#ifdef WTF_CHANGES
template <class Finder, class Reader>
void enumerateFreeObjects(Finder& finder, const Reader& reader)
@@ -3041,9 +3106,15 @@
const size_t size = ByteSizeForClass(size_class_);
char* ptr = start + (npages << kPageShift) - ((npages << kPageShift) % size);
int num = 0;
+#if ENABLE(TCMALLOC_HARDENING)
+ uint32_t startPoison = freedObjectStartPoison();
+ uint32_t endPoison = freedObjectEndPoison();
+#endif
+
while (ptr > start) {
ptr -= size;
HardenedSLL node = HardenedSLL::create(ptr);
+ POISON_DEALLOCATION_EXPLICIT(ptr, size, startPoison, endPoison);
SLL_SetNext(node, head, entropy_);
head = node;
num++;
@@ -3115,12 +3186,22 @@
if (list->empty()) return NULL;
}
size_ -= allocationSize;
- return list->Pop();
+ void* result = list->Pop();
+ if (!result)
+ return 0;
+ RELEASE_ASSERT(IS_DEFINITELY_POISONED(result, allocationSize));
+ POISON_ALLOCATION(result, allocationSize);
+ return result;
}
inline void TCMalloc_ThreadCache::Deallocate(HardenedSLL ptr, size_t cl) {
- size_ += ByteSizeForClass(cl);
+ size_t allocationSize = ByteSizeForClass(cl);
+ size_ += allocationSize;
FreeList* list = &list_[cl];
+ if (MAY_BE_POISONED(ptr.value(), allocationSize))
+ list->Validate(ptr);
+
+ POISON_DEALLOCATION(ptr.value(), allocationSize);
list->Push(ptr);
// If enough data is free, put back into central cache
if (list->length() > kMaxFreeListLength) {