1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2020 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Experimental crash postmortem use after free tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktTestCase.hpp"
25 #include "vktTestCaseUtil.hpp"
26 #include "vktTestGroupUtil.hpp"
27 #include "vktCustomInstancesDevices.hpp"
28 #include "vktPostmortemTests.hpp"
29 #include "vktPostmortemUseAfterFreeTests.hpp"
30 
31 #include "vkDefs.hpp"
32 #include "vkRef.hpp"
33 #include "vkRefUtil.hpp"
34 #include "vkPlatform.hpp"
35 #include "vkPrograms.hpp"
36 #include "vkRefUtil.hpp"
37 #include "vkMemUtil.hpp"
38 #include "vkBarrierUtil.hpp"
39 #include "vkQueryUtil.hpp"
40 #include "vkBuilderUtil.hpp"
41 #include "vkTypeUtil.hpp"
42 #include "vkDeviceUtil.hpp"
43 #include "vkCmdUtil.hpp"
44 #include "vkObjUtil.hpp"
45 #include "vkBufferWithMemory.hpp"
46 
47 #include "tcuCommandLine.hpp"
48 #include "tcuTestLog.hpp"
49 
50 #include "deStringUtil.hpp"
51 #include "deUniquePtr.hpp"
52 #include "deRandom.hpp"
53 #include "vktPostmortemUtil.hpp"
54 
55 #include <vector>
56 #include <memory>
57 
58 using namespace vk;
59 
60 namespace vkt
61 {
62 namespace postmortem
63 {
64 namespace
65 {
66 
67 enum BufferType
68 {
69     BUFFER_TYPE_UNIFORM = 0,
70     BUFFER_TYPE_SSBO,
71 };
72 
73 class Buffer
74 {
75 public:
76     Buffer(const vk::DeviceInterface &vk, const vk::VkDevice device, vk::Allocator &allocator,
77            const vk::VkBufferCreateInfo &bufferCreateInfo, const vk::MemoryRequirement memoryRequirement);
78 
get(void) const79     const vk::VkBuffer &get(void) const
80     {
81         return *m_buffer;
82     }
operator *(void) const83     const vk::VkBuffer &operator*(void) const
84     {
85         return get();
86     }
getAllocation(void) const87     vk::Allocation &getAllocation(void) const
88     {
89         return *m_allocation;
90     }
91 
freeAllocation(void)92     void freeAllocation(void)
93     {
94         delete m_allocation.release();
95     }
96 
97 private:
98     de::MovePtr<vk::Allocation> m_allocation;
99     vk::Move<vk::VkBuffer> m_buffer;
100 
101     Buffer(const Buffer &); // "deleted"
102     Buffer &operator=(const Buffer &);
103 };
104 
Buffer(const DeviceInterface & vk,const VkDevice device,Allocator & allocator,const VkBufferCreateInfo & bufferCreateInfo,const MemoryRequirement memoryRequirement)105 Buffer::Buffer(const DeviceInterface &vk, const VkDevice device, Allocator &allocator,
106                const VkBufferCreateInfo &bufferCreateInfo, const MemoryRequirement memoryRequirement)
107 {
108     m_buffer     = createBuffer(vk, device, &bufferCreateInfo);
109     m_allocation = allocator.allocate(getBufferMemoryRequirements(vk, device, *m_buffer), memoryRequirement);
110     VK_CHECK(vk.bindBufferMemory(device, *m_buffer, m_allocation->getMemory(), m_allocation->getOffset()));
111 }
112 
113 class UseAfterFreeTestCase : public vkt::TestCase
114 {
115 public:
116     void initPrograms(vk::SourceCollections &sourceCollections) const;
117     TestInstance *createInstance(Context &context) const;
118 
119     static UseAfterFreeTestCase *UBOToSSBOInvertCase(tcu::TestContext &testCtx, const std::string &name,
120                                                      const uint32_t numValues, const tcu::IVec3 &localSize,
121                                                      const tcu::IVec3 &workSize);
122 
123     static UseAfterFreeTestCase *CopyInvertSSBOCase(tcu::TestContext &testCtx, const std::string &name,
124                                                     const uint32_t numValues, const tcu::IVec3 &localSize,
125                                                     const tcu::IVec3 &workSize);
126 
127 private:
128     UseAfterFreeTestCase(tcu::TestContext &testCtx, const std::string &name, const uint32_t numValues,
129                          const tcu::IVec3 &localSize, const tcu::IVec3 &workSize, const BufferType bufferType);
130 
131     const BufferType m_bufferType;
132     const uint32_t m_numValues;
133     const tcu::IVec3 m_localSize;
134     const tcu::IVec3 m_workSize;
135 };
136 
137 class UseAfterFreeTestInstance : public PostmortemTestInstance
138 {
139 public:
140     UseAfterFreeTestInstance(Context &context, const uint32_t numValues, const tcu::IVec3 &localSize,
141                              const tcu::IVec3 &workSize, const BufferType bufferType);
142 
143     tcu::TestStatus iterate(void);
144 
145 private:
146     const BufferType m_bufferType;
147     const uint32_t m_numValues;
148     const tcu::IVec3 m_localSize;
149     const tcu::IVec3 m_workSize;
150 };
151 
152 template <typename T, int size>
multiplyComponents(const tcu::Vector<T,size> & v)153 T multiplyComponents(const tcu::Vector<T, size> &v)
154 {
155     T accum = 1;
156     for (int i = 0; i < size; ++i)
157         accum *= v[i];
158     return accum;
159 }
160 
UseAfterFreeTestCase(tcu::TestContext & testCtx,const std::string & name,const uint32_t numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize,const BufferType bufferType)161 UseAfterFreeTestCase::UseAfterFreeTestCase(tcu::TestContext &testCtx, const std::string &name, const uint32_t numValues,
162                                            const tcu::IVec3 &localSize, const tcu::IVec3 &workSize,
163                                            const BufferType bufferType)
164     : TestCase(testCtx, name)
165     , m_bufferType(bufferType)
166     , m_numValues(numValues)
167     , m_localSize(localSize)
168     , m_workSize(workSize)
169 {
170     DE_ASSERT(m_numValues % (multiplyComponents(m_workSize) * multiplyComponents(m_localSize)) == 0);
171     DE_ASSERT(m_bufferType == BUFFER_TYPE_UNIFORM || m_bufferType == BUFFER_TYPE_SSBO);
172 }
173 
UBOToSSBOInvertCase(tcu::TestContext & testCtx,const std::string & name,const uint32_t numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize)174 UseAfterFreeTestCase *UseAfterFreeTestCase::UBOToSSBOInvertCase(tcu::TestContext &testCtx, const std::string &name,
175                                                                 const uint32_t numValues, const tcu::IVec3 &localSize,
176                                                                 const tcu::IVec3 &workSize)
177 {
178     return new UseAfterFreeTestCase(testCtx, name, numValues, localSize, workSize, BUFFER_TYPE_UNIFORM);
179 }
180 
CopyInvertSSBOCase(tcu::TestContext & testCtx,const std::string & name,const uint32_t numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize)181 UseAfterFreeTestCase *UseAfterFreeTestCase::CopyInvertSSBOCase(tcu::TestContext &testCtx, const std::string &name,
182                                                                const uint32_t numValues, const tcu::IVec3 &localSize,
183                                                                const tcu::IVec3 &workSize)
184 {
185     return new UseAfterFreeTestCase(testCtx, name, numValues, localSize, workSize, BUFFER_TYPE_SSBO);
186 }
187 
initPrograms(SourceCollections & sourceCollections) const188 void UseAfterFreeTestCase::initPrograms(SourceCollections &sourceCollections) const
189 {
190     std::ostringstream src;
191     if (m_bufferType == BUFFER_TYPE_UNIFORM)
192     {
193         src << "#version 310 es\n"
194             << "layout (local_size_x = " << m_localSize.x() << ", local_size_y = " << m_localSize.y()
195             << ", local_size_z = " << m_localSize.z() << ") in;\n"
196             << "layout(binding = 0) readonly uniform Input {\n"
197             << "    uint values[" << m_numValues << "];\n"
198             << "} ub_in;\n"
199             << "layout(binding = 1, std140) writeonly buffer Output {\n"
200             << "    uint values[" << m_numValues << "];\n"
201             << "} sb_out;\n"
202             << "void main (void) {\n"
203             << "    uvec3 size           = gl_NumWorkGroups * gl_WorkGroupSize;\n"
204             << "    uint numValuesPerInv = uint(ub_in.values.length()) / (size.x*size.y*size.z);\n"
205             << "    uint groupNdx        = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
206                "gl_GlobalInvocationID.x;\n"
207             << "    uint offset          = numValuesPerInv*groupNdx;\n"
208             << "\n"
209             << "    for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
210             << "        sb_out.values[offset + ndx] = ~ub_in.values[offset + ndx];\n"
211             << "}\n";
212     }
213     else if (m_bufferType == BUFFER_TYPE_SSBO)
214     {
215         src << "#version 310 es\n"
216             << "layout (local_size_x = " << m_localSize.x() << ", local_size_y = " << m_localSize.y()
217             << ", local_size_z = " << m_localSize.z() << ") in;\n"
218             << "layout(binding = 0, std140) readonly buffer Input {\n"
219             << "    uint values[" << m_numValues << "];\n"
220             << "} sb_in;\n"
221             << "layout (binding = 1, std140) writeonly buffer Output {\n"
222             << "    uint values[" << m_numValues << "];\n"
223             << "} sb_out;\n"
224             << "void main (void) {\n"
225             << "    uvec3 size           = gl_NumWorkGroups * gl_WorkGroupSize;\n"
226             << "    uint numValuesPerInv = uint(sb_in.values.length()) / (size.x*size.y*size.z);\n"
227             << "    uint groupNdx        = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + "
228                "gl_GlobalInvocationID.x;\n"
229             << "    uint offset          = numValuesPerInv*groupNdx;\n"
230             << "\n"
231             << "    for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
232             << "        sb_out.values[offset + ndx] = ~sb_in.values[offset + ndx];\n"
233             << "}\n";
234     }
235 
236     sourceCollections.glslSources.add("comp") << glu::ComputeSource(src.str());
237 }
238 
createInstance(Context & context) const239 TestInstance *UseAfterFreeTestCase::createInstance(Context &context) const
240 {
241     return new UseAfterFreeTestInstance(context, m_numValues, m_localSize, m_workSize, m_bufferType);
242 }
243 
UseAfterFreeTestInstance(Context & context,const uint32_t numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize,const BufferType bufferType)244 UseAfterFreeTestInstance::UseAfterFreeTestInstance(Context &context, const uint32_t numValues,
245                                                    const tcu::IVec3 &localSize, const tcu::IVec3 &workSize,
246                                                    const BufferType bufferType)
247     : PostmortemTestInstance(context)
248     , m_bufferType(bufferType)
249     , m_numValues(numValues)
250     , m_localSize(localSize)
251     , m_workSize(workSize)
252 {
253 }
254 
iterate(void)255 tcu::TestStatus UseAfterFreeTestInstance::iterate(void)
256 {
257     const VkDevice device           = *m_logicalDevice;
258     const DeviceInterface &vk       = m_deviceDriver;
259     const VkQueue queue             = m_queue;
260     const uint32_t queueFamilyIndex = m_queueFamilyIndex;
261     Allocator &allocator            = m_allocator;
262 
263     // Customize the test based on buffer type
264 
265     const VkBufferUsageFlags inputBufferUsageFlags =
266         (m_bufferType == BUFFER_TYPE_UNIFORM ? VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
267     const VkDescriptorType inputBufferDescriptorType =
268         (m_bufferType == BUFFER_TYPE_UNIFORM ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
269     const uint32_t randomSeed = (m_bufferType == BUFFER_TYPE_UNIFORM ? 0x111223f : 0x124fef);
270 
271     // Create an input buffer
272 
273     const VkDeviceSize bufferSizeBytes = sizeof(tcu::UVec4) * m_numValues;
274     Buffer inputBuffer(vk, device, allocator, makeBufferCreateInfo(bufferSizeBytes, inputBufferUsageFlags),
275                        MemoryRequirement::HostVisible);
276 
277     // Fill the input buffer with data
278     {
279         de::Random rnd(randomSeed);
280         const Allocation &inputBufferAllocation = inputBuffer.getAllocation();
281         tcu::UVec4 *bufferPtr                   = static_cast<tcu::UVec4 *>(inputBufferAllocation.getHostPtr());
282         for (uint32_t i = 0; i < m_numValues; ++i)
283             bufferPtr[i].x() = rnd.getUint32();
284 
285         flushAlloc(vk, device, inputBufferAllocation);
286     }
287 
288     // Create an output buffer
289 
290     Buffer outputBuffer(vk, device, allocator,
291                         makeBufferCreateInfo(bufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
292                         MemoryRequirement::HostVisible);
293 
294     // Create descriptor set
295 
296     const Unique<VkDescriptorSetLayout> descriptorSetLayout(
297         DescriptorSetLayoutBuilder()
298             .addSingleBinding(inputBufferDescriptorType, VK_SHADER_STAGE_COMPUTE_BIT)
299             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT)
300             .build(vk, device));
301 
302     const Unique<VkDescriptorPool> descriptorPool(
303         DescriptorPoolBuilder()
304             .addType(inputBufferDescriptorType)
305             .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
306             .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
307 
308     const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
309 
310     const VkDescriptorBufferInfo inputBufferDescriptorInfo =
311         makeDescriptorBufferInfo(*inputBuffer, 0ull, bufferSizeBytes);
312     const VkDescriptorBufferInfo outputBufferDescriptorInfo =
313         makeDescriptorBufferInfo(*outputBuffer, 0ull, bufferSizeBytes);
314     DescriptorSetUpdateBuilder()
315         .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), inputBufferDescriptorType,
316                      &inputBufferDescriptorInfo)
317         .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u),
318                      VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outputBufferDescriptorInfo)
319         .update(vk, device);
320 
321     // Perform the computation
322 
323     const Unique<VkShaderModule> shaderModule(
324         createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
325     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
326     const Unique<VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
327 
328     const VkBufferMemoryBarrier hostWriteBarrier = makeBufferMemoryBarrier(
329         VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, *inputBuffer, 0ull, bufferSizeBytes);
330 
331     const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
332         VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *outputBuffer, 0ull, bufferSizeBytes);
333 
334     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
335     const Unique<VkCommandBuffer> cmdBuffer(
336         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
337 
338     // Start recording commands
339 
340     beginCommandBuffer(vk, *cmdBuffer);
341 
342     vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
343     vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(),
344                              0u, DE_NULL);
345 
346     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
347                           (VkDependencyFlags)0, 0, (const VkMemoryBarrier *)DE_NULL, 1, &hostWriteBarrier, 0,
348                           (const VkImageMemoryBarrier *)DE_NULL);
349     vk.cmdDispatch(*cmdBuffer, m_workSize.x(), m_workSize.y(), m_workSize.z());
350     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
351                           (VkDependencyFlags)0, 0, (const VkMemoryBarrier *)DE_NULL, 1, &shaderWriteBarrier, 0,
352                           (const VkImageMemoryBarrier *)DE_NULL);
353 
354     endCommandBuffer(vk, *cmdBuffer);
355 
356     // Free the memory backing the buffer
357     inputBuffer.freeAllocation();
358     outputBuffer.freeAllocation();
359 
360     // Wait for completion
361     submitCommandsAndWait(vk, device, queue, *cmdBuffer);
362 
363     // Pointers are invalid, so nothing to verify
364     return tcu::TestStatus::pass("Test succeeded without device loss");
365 }
366 } // namespace
367 
createUseAfterFreeTests(tcu::TestContext & testCtx)368 tcu::TestCaseGroup *createUseAfterFreeTests(tcu::TestContext &testCtx)
369 {
370     // Use buffer after free.
371     de::MovePtr<tcu::TestCaseGroup> useAfterFreeGroup(new tcu::TestCaseGroup(testCtx, "use_after_free"));
372 
373     // Copy from UBO to SSBO, inverting bits
374     useAfterFreeGroup->addChild(UseAfterFreeTestCase::UBOToSSBOInvertCase(testCtx, "ubo_to_ssbo_single_invocation", 256,
375                                                                           tcu::IVec3(1, 1, 1), tcu::IVec3(1, 1, 1)));
376     // Copy from SSBO to SSBO, inverting bits
377     useAfterFreeGroup->addChild(UseAfterFreeTestCase::CopyInvertSSBOCase(testCtx, "ssbo_to_ssbo_single_invocation", 256,
378                                                                          tcu::IVec3(1, 1, 1), tcu::IVec3(1, 1, 1)));
379 
380     return useAfterFreeGroup.release();
381 }
382 
383 } // namespace postmortem
384 } // namespace vkt
385