Chromium Code Reviews| Index: Source/bindings/v8/ScriptWrappable.h |
| diff --git a/Source/bindings/v8/ScriptWrappable.h b/Source/bindings/v8/ScriptWrappable.h |
| index a5738c455e9f663bc71ffd408c7b97064a38e6df..6c1d8f577e2caffb1bccbade775deb73196d404a 100644 |
| --- a/Source/bindings/v8/ScriptWrappable.h |
| +++ b/Source/bindings/v8/ScriptWrappable.h |
| @@ -41,72 +41,131 @@ namespace WebCore { |
| class ScriptWrappable { |
| friend class WeakHandleListener<ScriptWrappable>; |
| public: |
| - ScriptWrappable() |
| - { |
| -#ifndef NDEBUG |
| - m_init = false; |
| -#endif |
| - } |
| + ScriptWrappable() : m_maskedStorage(0) { } |
| - template <class C> static void init(C *object) |
| + // Wrappables need to be initialized with their most derrived type for which |
| + // bindings exist, in much the same way that certain other types need to be |
| + // adopted and so forth. The overloaded scriptwrappable_init() functions are |
| + // implemented by the generated V8 bindings code. |
| + template <class C> static void init(C* object) |
| { |
| -#ifndef NDEBUG |
| - object->m_init = true; |
| -#endif |
| + extern void scriptwrappable_init(C*); // Workaround for lack of extern template prior to C++11. |
|
abarth-chromium
2013/04/11 22:59:33
Why do we need to extern for this function? I gue
|
| + scriptwrappable_init(object); |
| } |
| v8::Handle<v8::Object> wrapper() const |
| { |
| - return v8::Handle<v8::Object>(maskOrUnmaskPointer(*m_maskedWrapper)); |
| + v8::Object* object = containsWrapper() ? reinterpret_cast<v8::Object*>(maskOrUnmaskValue(m_maskedStorage)) : 0; |
| + return v8::Handle<v8::Object>(object); |
| } |
| void setWrapper(v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperConfiguration& configuration) |
| { |
| - ASSERT(m_maskedWrapper.IsEmpty()); |
| + ASSERT(!containsWrapper()); |
| v8::Persistent<v8::Object> persistent = v8::Persistent<v8::Object>::New(isolate, wrapper); |
| configuration.configureWrapper(persistent, isolate); |
| WeakHandleListener<ScriptWrappable>::makeWeak(isolate, persistent, this); |
| - m_maskedWrapper = maskOrUnmaskPointer(*persistent); |
| + m_maskedStorage = maskOrUnmaskValue(reinterpret_cast<uintptr_t>(*persistent)); |
| + } |
| + |
| + const WrapperTypeInfo* typeInfo() |
| + { |
| + if (containsTypeInfo()) |
| + return reinterpret_cast<const WrapperTypeInfo*>(m_maskedStorage & ~1); |
| + |
| + if (containsWrapper()) |
| + return toWrapperTypeInfo(wrapper()); |
| + |
| + return 0; |
| + } |
| + |
| + void setTypeInfo(const WrapperTypeInfo* info) |
| + { |
| + m_maskedStorage = (reinterpret_cast<uintptr_t>(info) | 1); |
| } |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM); |
| - info.ignoreMember(m_maskedWrapper); |
| + info.ignoreMember(m_maskedStorage); |
| + } |
| + |
| + static bool wrapperCanBeStoredInObject(const void*) { return false; } |
| + static bool wrapperCanBeStoredInObject(const ScriptWrappable*) { return true; } |
| + |
| + static v8::Handle<v8::Object> getWrapperFromObject(void*) |
| + { |
| + ASSERT_NOT_REACHED(); |
| + return v8::Handle<v8::Object>(); |
| + } |
| + |
| + static v8::Handle<v8::Object> getWrapperFromObject(ScriptWrappable* object) |
| + { |
| + return object->wrapper(); |
| + } |
| + |
| + static void setWrapperInObject(void*, v8::Handle<v8::Object>, v8::Isolate*, const WrapperConfiguration&) |
| + { |
| + ASSERT_NOT_REACHED(); |
| + } |
| + |
| + static void setWrapperInObject(ScriptWrappable* object, v8::Handle<v8::Object> wrapper, v8::Isolate* isolate, const WrapperConfiguration& configuration) |
| + { |
| + object->setWrapper(wrapper, isolate, configuration); |
| + } |
| + |
| + static const WrapperTypeInfo* getTypeInfoFromObject(void* object) |
| + { |
| + ASSERT_NOT_REACHED(); |
| + return 0; |
| + } |
| + |
| + static const WrapperTypeInfo* getTypeInfoFromObject(ScriptWrappable* object) |
| + { |
| + return object->typeInfo(); |
| + } |
| + |
| + static void setTypeInfoInObject(void* object, const WrapperTypeInfo* info) |
| + { |
| + ASSERT_NOT_REACHED(); |
| + } |
| + |
| + static void setTypeInfoInObject(ScriptWrappable* object, const WrapperTypeInfo* info) |
| + { |
| + object->setTypeInfo(info); |
| } |
| protected: |
| ~ScriptWrappable() |
| { |
| -#ifndef NDEBUG |
| - ASSERT(m_init); |
| -#endif |
| + ASSERT(m_maskedStorage); // Assert initialization via init() even if not subsequently wrapped. |
| + m_maskedStorage = 0; // Break UAF attempts to wrap. |
| } |
| private: |
| - inline void disposeWrapper(v8::Persistent<v8::Value> value, v8::Isolate* isolate) |
| + inline bool containsTypeInfo() const { return (m_maskedStorage & 1) == 1; } |
| + inline bool containsWrapper() const { return m_maskedStorage && ((m_maskedStorage & 1) == 0); } |
| + |
| + static inline uintptr_t maskOrUnmaskValue(uintptr_t value) |
| { |
| - ASSERT(!m_maskedWrapper.IsEmpty()); |
| - ASSERT(*value == maskOrUnmaskPointer(*m_maskedWrapper)); |
| - value.Dispose(isolate); |
| - m_maskedWrapper.Clear(); |
| + const uintptr_t randomMask = ~((reinterpret_cast<uintptr_t>(&WebCoreMemoryTypes::DOM) >> 13) | 1); // Entropy via ASLR, bottom bit clear. |
| + return (value ^ randomMask) & (!value - 1); // Preserve null without branching. |
| } |
| - static inline v8::Object* maskOrUnmaskPointer(const v8::Object* object) |
| + inline void disposeWrapper(v8::Persistent<v8::Value> value, v8::Isolate* isolate, const WrapperTypeInfo* info) |
| { |
| - const uintptr_t objectPointer = reinterpret_cast<uintptr_t>(object); |
| - const uintptr_t randomMask = ~(reinterpret_cast<uintptr_t>(&WebCoreMemoryTypes::DOM) >> 13); // Entropy via ASLR. |
| - return reinterpret_cast<v8::Object*>((objectPointer ^ randomMask) & (!objectPointer - 1)); // Preserve null without branching. |
| + ASSERT(containsWrapper()); |
| + ASSERT(reinterpret_cast<uintptr_t>(*value) == maskOrUnmaskValue(m_maskedStorage)); |
| + value.Dispose(isolate); |
| + setTypeInfo(info); |
| } |
| - // Stores a masked wrapper to prevent attackers from overwriting this field |
| - // with a phony wrapper. |
| - v8::Persistent<v8::Object> m_maskedWrapper; |
| - |
| -#ifndef NDEBUG |
| - bool m_init; |
| -#endif |
| - |
| + // If zero, then this contains nothing, otherwise: |
| + // If the bottom bit it clear, then this contains a masked pointer to a wrapper object. |
| + // If the bottom bit is set, then this contains a pointer to the wrapper type info in the remaining bits. |
| + // Masking wrappers prevents attackers from overwriting this field with pointers to sprayed data. |
| + // Pointers to (and inside) WrapperTypeInfo are already protected by ASLR. |
| + uintptr_t m_maskedStorage; |
| }; |
| template<> |
| @@ -121,7 +180,7 @@ inline void WeakHandleListener<ScriptWrappable>::callback(v8::Isolate* isolate, |
| WrapperTypeInfo* info = toWrapperTypeInfo(wrapper); |
| ASSERT(info->derefObjectFunction); |
| - key->disposeWrapper(value, isolate); |
| + key->disposeWrapper(value, isolate, info); |
| // FIXME: I noticed that 50%~ of minor GC cycle times can be consumed |
| // inside key->deref(), which causes Node destructions. We should |
| // make Node destructions incremental. |