Poison small JSObject derivatives which only contain pointers
authorjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Jan 2018 03:03:35 +0000 (03:03 +0000)
committerjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Jan 2018 03:03:35 +0000 (03:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=181483
<rdar://problem/36407127>

Reviewed by Mark Lam.

Source/JavaScriptCore:

I wrote a script that finds interesting things to poison or
generally harden. These stood out because they derive from
JSObject and only contain a few pointer or pointer-like fields,
and could therefore just be poisoned. This also requires some
template "improvements" to our poisoning machinery. Worth noting
is that I'm making PoisonedUniquePtr move-assignable and
move-constructible from unique_ptr, which makes it a better
drop-in replacement because we don't need to use
makePoisonedUniquePtr. This means function-locals can be
unique_ptr and get the nice RAII pattern, and once the function is
done you can just move to the class' PoisonedUniquePtr without
worrying.

* API/JSAPIWrapperObject.h:
(JSC::JSAPIWrapperObject::wrappedObject):
* API/JSAPIWrapperObject.mm:
(JSC::JSAPIWrapperObject::JSAPIWrapperObject):
* API/JSCallbackObject.h:
* runtime/ArrayPrototype.h:
* runtime/DateInstance.h:
* runtime/JSArrayBuffer.cpp:
(JSC::JSArrayBuffer::finishCreation):
(JSC::JSArrayBuffer::isShared const):
(JSC::JSArrayBuffer::sharingMode const):
* runtime/JSArrayBuffer.h:
* runtime/JSCPoison.h:

Source/WTF:

The associated JSC poisoning change requires some template
"improvements" to our poisoning machinery. Worth noting is that
I'm making PoisonedUniquePtr move-assignable and
move-constructible from unique_ptr, which makes it a better
drop-in replacement because we don't need to use
makePoisonedUniquePtr. This means function-locals can be
unique_ptr and get the nice RAII pattern, and once the function is
done you can just move to the class' PoisonedUniquePtr without
worrying.

* wtf/Poisoned.h:
(WTF::PoisonedImpl::PoisonedImpl):
* wtf/PoisonedUniquePtr.h:
(WTF::PoisonedUniquePtr::PoisonedUniquePtr):
(WTF::PoisonedUniquePtr::operator=):

Tools:

Test the new move-assign and move-copy from unique_ptr, as well as
nullptr_t ctors.

* TestWebKitAPI/Tests/WTF/Poisoned.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WTF/PoisonedUniquePtr.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WTF/PoisonedUniquePtrForTriviallyDestructibleArrays.cpp:
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226752 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/JavaScriptCore/API/JSAPIWrapperObject.h
Source/JavaScriptCore/API/JSAPIWrapperObject.mm
Source/JavaScriptCore/API/JSCallbackObject.h
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ArrayPrototype.h
Source/JavaScriptCore/runtime/DateInstance.h
Source/JavaScriptCore/runtime/JSArrayBuffer.cpp
Source/JavaScriptCore/runtime/JSArrayBuffer.h
Source/JavaScriptCore/runtime/JSCPoison.h
Source/WTF/ChangeLog
Source/WTF/wtf/Poisoned.h
Source/WTF/wtf/PoisonedUniquePtr.h
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WTF/Poisoned.cpp
Tools/TestWebKitAPI/Tests/WTF/PoisonedUniquePtr.cpp
Tools/TestWebKitAPI/Tests/WTF/PoisonedUniquePtrForTriviallyDestructibleArrays.cpp

index 14194b6..1481b42 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #define JSAPIWrapperObject_h
 
 #include "JSBase.h"
+#include "JSCPoison.h"
 #include "JSDestructibleObject.h"
 #include "WeakReferenceHarvester.h"
+#include <wtf/Poisoned.h>
 
 #if JSC_OBJC_API_ENABLED
 
@@ -41,14 +43,14 @@ public:
     void finishCreation(VM&);
     static void visitChildren(JSCell*, JSC::SlotVisitor&);
     
-    void* wrappedObject() { return m_wrappedObject; }
+    void* wrappedObject() { return m_wrappedObject.unpoisoned(); }
     void setWrappedObject(void*);
 
 protected:
     JSAPIWrapperObject(VM&, Structure*);
 
 private:
-    void* m_wrappedObject;
+    ConstExprPoisoned<JSAPIWrapperObjectPoison, void*> m_wrappedObject;
 };
 
 } // namespace JSC
index 46dc2a7..979ff9b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -80,7 +80,6 @@ Structure* JSCallbackObject<JSAPIWrapperObject>::createStructure(VM& vm, JSGloba
 
 JSAPIWrapperObject::JSAPIWrapperObject(VM& vm, Structure* structure)
     : Base(vm, structure)
-    , m_wrappedObject(0)
 {
 }
 
index 5a00645..2df1d67 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
  *
  * Redistribution and use in source and binary forms, with or without
 #ifndef JSCallbackObject_h
 #define JSCallbackObject_h
 
+#include "JSCPoison.h"
 #include "JSCPoisonedPtr.h"
 #include "JSObjectRef.h"
 #include "JSValueRef.h"
 #include "JSObject.h"
+#include <wtf/PoisonedUniquePtr.h>
 
 namespace JSC {
 
@@ -233,7 +235,7 @@ private:
     static EncodedJSValue staticFunctionGetter(ExecState*, EncodedJSValue, PropertyName);
     static EncodedJSValue callbackGetter(ExecState*, EncodedJSValue, PropertyName);
 
-    std::unique_ptr<JSCallbackObjectData> m_callbackObjectData;
+    WTF::PoisonedUniquePtr<JSCallbackObjectPoison, JSCallbackObjectData> m_callbackObjectData;
     PoisonedClassInfoPtr m_classInfo;
 };
 
index a8caa15..9ccee9b 100644 (file)
@@ -1,3 +1,38 @@
+2018-01-10  JF Bastien  <jfbastien@apple.com>
+
+        Poison small JSObject derivatives which only contain pointers
+        https://bugs.webkit.org/show_bug.cgi?id=181483
+        <rdar://problem/36407127>
+
+        Reviewed by Mark Lam.
+
+        I wrote a script that finds interesting things to poison or
+        generally harden. These stood out because they derive from
+        JSObject and only contain a few pointer or pointer-like fields,
+        and could therefore just be poisoned. This also requires some
+        template "improvements" to our poisoning machinery. Worth noting
+        is that I'm making PoisonedUniquePtr move-assignable and
+        move-constructible from unique_ptr, which makes it a better
+        drop-in replacement because we don't need to use
+        makePoisonedUniquePtr. This means function-locals can be
+        unique_ptr and get the nice RAII pattern, and once the function is
+        done you can just move to the class' PoisonedUniquePtr without
+        worrying.
+
+        * API/JSAPIWrapperObject.h:
+        (JSC::JSAPIWrapperObject::wrappedObject):
+        * API/JSAPIWrapperObject.mm:
+        (JSC::JSAPIWrapperObject::JSAPIWrapperObject):
+        * API/JSCallbackObject.h:
+        * runtime/ArrayPrototype.h:
+        * runtime/DateInstance.h:
+        * runtime/JSArrayBuffer.cpp:
+        (JSC::JSArrayBuffer::finishCreation):
+        (JSC::JSArrayBuffer::isShared const):
+        (JSC::JSArrayBuffer::sharingMode const):
+        * runtime/JSArrayBuffer.h:
+        * runtime/JSCPoison.h:
+
 2018-01-10  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r226667 and r226673.
index 291c2e3..25587d8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2007, 2011, 2015 Apple Inc. All rights reserved.
+ *  Copyright (C) 2007-2018 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -21,6 +21,8 @@
 #pragma once
 
 #include "JSArray.h"
+#include "JSCPoison.h"
+#include <wtf/PoisonedUniquePtr.h>
 
 namespace JSC {
 
@@ -60,8 +62,8 @@ protected:
 private:
     // This bit is set if any user modifies the constructor property Array.prototype. This is used to optimize species creation for JSArrays.
     friend ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
-    std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint;
-    std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorSpeciesWatchpoint;
+    PoisonedUniquePtr<ArrayPrototypePoison, ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint;
+    PoisonedUniquePtr<ArrayPrototypePoison, ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorSpeciesWatchpoint;
 };
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
index 3a6a187..3f11ae3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2008 Apple Inc. All rights reserved.
+ *  Copyright (C) 2008-2018 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -20,6 +20,7 @@
 
 #pragma once
 
+#include "JSCPoison.h"
 #include "JSWrapperObject.h"
 
 namespace JSC {
@@ -76,7 +77,7 @@ private:
     JS_EXPORT_PRIVATE const GregorianDateTime* calculateGregorianDateTime(ExecState*) const;
     JS_EXPORT_PRIVATE const GregorianDateTime* calculateGregorianDateTimeUTC(ExecState*) const;
 
-    mutable RefPtr<DateInstanceData> m_data;
+    mutable PoisonedRefPtr<DateInstancePoison, DateInstanceData> m_data;
 };
 
 DateInstance* asDateInstance(JSValue);
index 6798da5..3a64c34 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -46,8 +46,8 @@ void JSArrayBuffer::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
     // This probably causes GCs in the various VMs to overcount the impact of the array buffer.
-    vm.heap.addReference(this, m_impl);
-    vm.m_typedArrayController->registerWrapper(globalObject, m_impl, this);
+    vm.heap.addReference(this, impl());
+    vm.m_typedArrayController->registerWrapper(globalObject, impl(), this);
 }
 
 JSArrayBuffer* JSArrayBuffer::create(
@@ -70,12 +70,12 @@ Structure* JSArrayBuffer::createStructure(
 
 bool JSArrayBuffer::isShared() const
 {
-    return m_impl->isShared();
+    return impl()->isShared();
 }
 
 ArrayBufferSharingMode JSArrayBuffer::sharingMode() const
 {
-    return m_impl->sharingMode();
+    return impl()->sharingMode();
 }
 
 size_t JSArrayBuffer::estimatedSize(JSCell* cell)
index 91427a9..820c585 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,7 +26,9 @@
 #pragma once
 
 #include "ArrayBuffer.h"
+#include "JSCPoison.h"
 #include "JSObject.h"
+#include <wtf/Poisoned.h>
 
 namespace JSC {
 
@@ -43,7 +45,7 @@ public:
     // This function will register the new wrapper with the vm's TypedArrayController.
     JS_EXPORT_PRIVATE static JSArrayBuffer* create(VM&, Structure*, RefPtr<ArrayBuffer>&&);
 
-    ArrayBuffer* impl() const { return m_impl; }
+    ArrayBuffer* impl() const { return m_impl.unpoisoned(); }
     
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
 
@@ -59,7 +61,7 @@ protected:
     static size_t estimatedSize(JSCell*);
 
 private:
-    ArrayBuffer* m_impl;
+    ConstExprPoisoned<JSArrayBufferPoison, ArrayBuffer*> m_impl;
 };
 
 inline ArrayBuffer* toPossiblySharedArrayBuffer(VM& vm, JSValue value)
index 952c27b..ced0120 100644 (file)
@@ -35,7 +35,12 @@ enum Poison {
     // Add new poison keys below in alphabetical order. The order doesn't really
     // matter, but we might as well keep them in alphabetically order for
     // greater readability.
+    ArrayPrototypePoison,
     CodeBlockPoison,
+    DateInstancePoison,
+    JSAPIWrapperObjectPoison,
+    JSArrayBufferPoison,
+    JSCallbackObjectPoison,
     JSGlobalObjectPoison,
     JSScriptFetchParametersPoison,
     JSScriptFetcherPoison,
index d96ac5f..cd726f1 100644 (file)
@@ -1,3 +1,27 @@
+2018-01-10  JF Bastien  <jfbastien@apple.com>
+
+        Poison small JSObject derivatives which only contain pointers
+        https://bugs.webkit.org/show_bug.cgi?id=181483
+        <rdar://problem/36407127>
+
+        Reviewed by Mark Lam.
+
+        The associated JSC poisoning change requires some template
+        "improvements" to our poisoning machinery. Worth noting is that
+        I'm making PoisonedUniquePtr move-assignable and
+        move-constructible from unique_ptr, which makes it a better
+        drop-in replacement because we don't need to use
+        makePoisonedUniquePtr. This means function-locals can be
+        unique_ptr and get the nice RAII pattern, and once the function is
+        done you can just move to the class' PoisonedUniquePtr without
+        worrying.
+
+        * wtf/Poisoned.h:
+        (WTF::PoisonedImpl::PoisonedImpl):
+        * wtf/PoisonedUniquePtr.h:
+        (WTF::PoisonedUniquePtr::PoisonedUniquePtr):
+        (WTF::PoisonedUniquePtr::operator=):
+
 2018-01-10  Don Olmstead  <don.olmstead@sony.com>
 
         Add nullptr_t specialization of poison
index 394943c..90a45ab 100644 (file)
 
 #pragma once
 
-#include <utility>
 #include <wtf/Assertions.h>
 
+#include <cstddef>
+#include <utility>
+
 #define ENABLE_POISON 1
 #define ENABLE_POISON_ASSERTS 0
 
@@ -93,6 +95,8 @@ class PoisonedImpl {
 public:
     PoisonedImpl() { }
 
+    PoisonedImpl(std::nullptr_t) { }
+
     PoisonedImpl(T ptr)
         : m_poisonedBits(poison(ptr))
     { }
index be6ba0a..df9c2f0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,6 +28,9 @@
 #include <wtf/FastMalloc.h>
 #include <wtf/Poisoned.h>
 
+#include <cstddef>
+#include <memory>
+
 namespace WTF {
 
 template<uint32_t key, typename T, typename Enable = void>
@@ -36,8 +39,15 @@ class PoisonedUniquePtr : public ConstExprPoisoned<key, T*> {
     using Base = ConstExprPoisoned<key, T*>;
 public:
     PoisonedUniquePtr() = default;
+    PoisonedUniquePtr(std::nullptr_t) : Base() { }
     PoisonedUniquePtr(T* ptr) : Base(ptr) { }
     PoisonedUniquePtr(PoisonedUniquePtr&& ptr) : Base(WTFMove(ptr)) { ptr.clearWithoutDestroy(); }
+    PoisonedUniquePtr(std::unique_ptr<T>&& unique) : PoisonedUniquePtr(unique.release()) { }
+
+    template<typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
+    PoisonedUniquePtr(std::unique_ptr<U>&& unique)
+        : Base(unique.release())
+    { }
 
     template<uint32_t key2, typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
     PoisonedUniquePtr(PoisonedUniquePtr<key2, U>&& ptr)
@@ -65,6 +75,21 @@ public:
         return *this;
     }
 
+    PoisonedUniquePtr& operator=(std::unique_ptr<T>&& unique)
+    {
+        this->clear();
+        this->Base::operator=(unique.release());
+        return *this;
+    }
+
+    template<typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
+    PoisonedUniquePtr& operator=(std::unique_ptr<U>&& unique)
+    {
+        this->clear();
+        this->Base::operator=(unique.release());
+        return *this;
+    }
+
     template<uint32_t key2, typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
     PoisonedUniquePtr& operator=(PoisonedUniquePtr<key2, U>&& ptr)
     {
@@ -113,8 +138,10 @@ class PoisonedUniquePtr<key, T[]> : public ConstExprPoisoned<key, T*> {
     using Base = ConstExprPoisoned<key, T*>;
 public:
     PoisonedUniquePtr() = default;
+    PoisonedUniquePtr(std::nullptr_t) : Base() { }
     PoisonedUniquePtr(T* ptr) : Base(ptr) { }
     PoisonedUniquePtr(PoisonedUniquePtr&& ptr) : Base(WTFMove(ptr)) { ptr.clearWithoutDestroy(); }
+    PoisonedUniquePtr(std::unique_ptr<T[]>&& unique) : PoisonedUniquePtr(unique.release()) { }
 
     template<uint32_t key2>
     PoisonedUniquePtr(PoisonedUniquePtr<key2, T[]>&& ptr)
@@ -144,6 +171,13 @@ public:
         }
         return *this;
     }
+    
+    PoisonedUniquePtr& operator=(std::unique_ptr<T[]>&& unique)
+    {
+        this->clear();
+        this->Base::operator=(unique.release());
+        return *this;
+    }
 
     template<uint32_t key2>
     PoisonedUniquePtr& operator=(PoisonedUniquePtr<key2, T[]>&& ptr)
index 616605f..23b16ed 100644 (file)
@@ -1,3 +1,21 @@
+2018-01-10  JF Bastien  <jfbastien@apple.com>
+
+        Poison small JSObject derivatives which only contain pointers
+        https://bugs.webkit.org/show_bug.cgi?id=181483
+        <rdar://problem/36407127>
+
+        Reviewed by Mark Lam.
+
+        Test the new move-assign and move-copy from unique_ptr, as well as
+        nullptr_t ctors.
+
+        * TestWebKitAPI/Tests/WTF/Poisoned.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WTF/PoisonedUniquePtr.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WTF/PoisonedUniquePtrForTriviallyDestructibleArrays.cpp:
+        (TestWebKitAPI::TEST):
+
 2018-01-10  Chris Dumez  <cdumez@apple.com>
 
         Multiple http/wpt/beacon/contentextensions/ test are flaky.
index 1c52155..dce7451 100644 (file)
@@ -53,8 +53,15 @@ TEST(WTF_Poisoned, Basic)
     initializeTestPoison();
     DerivedRefLogger a("a");
 
-    Poisoned<g_testPoisonA, RefLogger*> empty;
-    ASSERT_EQ(nullptr, empty.unpoisoned());
+    {
+        Poisoned<g_testPoisonA, RefLogger*> empty;
+        ASSERT_EQ(nullptr, empty.unpoisoned());
+    }
+
+    {
+        Poisoned<g_testPoisonA, RefLogger*> empty(nullptr);
+        ASSERT_EQ(nullptr, empty.unpoisoned());
+    }
 
     {
         Poisoned<g_testPoisonA, RefLogger*> ptr(&a);
index 4af4a3e..5595fed 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -66,6 +66,11 @@ TEST(WTF_PoisonedUniquePtr, Basic)
         ASSERT_EQ(nullptr, empty.unpoisoned());
         ASSERT_EQ(0u, empty.bits());
     }
+    {
+        PoisonedUniquePtr<PoisonA, Logger> empty(nullptr);
+        ASSERT_EQ(nullptr, empty.unpoisoned());
+        ASSERT_EQ(0u, empty.bits());
+    }
 
     {
         int aDestructCount = 0;
@@ -162,6 +167,26 @@ TEST(WTF_PoisonedUniquePtr, Basic)
             ASSERT_EQ(bName, &ptr->name);
         }
         ASSERT_EQ(1, bDestructCount);
+
+        int uniqueDestructCount = 0;
+        const char* uniqueName = "unique";
+        {
+            PoisonedUniquePtr<PoisonA, DerivedLogger> ptr = std::make_unique<DerivedLogger>(uniqueName, uniqueDestructCount);
+            ASSERT_EQ(0, uniqueDestructCount);
+            ASSERT_TRUE(nullptr != ptr.unpoisoned());
+            ASSERT_EQ(uniqueName, &ptr->name);
+        }
+        ASSERT_EQ(1, uniqueDestructCount);
+
+        int uniqueDerivedDestructCount = 0;
+        const char* uniqueDerivedName = "unique derived";
+        {
+            PoisonedUniquePtr<PoisonA, Logger> ptr = std::make_unique<DerivedLogger>(uniqueDerivedName, uniqueDerivedDestructCount);
+            ASSERT_EQ(0, uniqueDerivedDestructCount);
+            ASSERT_TRUE(nullptr != ptr.unpoisoned());
+            ASSERT_EQ(uniqueDerivedName, &ptr->name);
+        }
+        ASSERT_EQ(1, uniqueDerivedDestructCount);
     }
 
     {
index 678a8d7..b2f3cd5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -53,6 +53,11 @@ TEST(WTF_PoisonedUniquePtrForTriviallyDestructibleArrays, Basic)
         ASSERT_EQ(nullptr, empty.unpoisoned());
         ASSERT_EQ(0u, empty.bits());
     }
+    {
+        PoisonedUniquePtr<PoisonA, int[]> empty(nullptr);
+        ASSERT_EQ(nullptr, empty.unpoisoned());
+        ASSERT_EQ(0u, empty.bits());
+    }
 
     {
         auto* a = new int[arraySize];