• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,
77 										const vk::VkDevice				device,
78 										vk::Allocator&					allocator,
79 										const vk::VkBufferCreateInfo&	bufferCreateInfo,
80 										const vk::MemoryRequirement		memoryRequirement);
81 
get(void) const82 	const vk::VkBuffer&	get				(void) const { return *m_buffer; }
operator *(void) const83 	const vk::VkBuffer&	operator*		(void) const { return get(); }
getAllocation(void) const84 	vk::Allocation&		getAllocation	(void) const { return *m_allocation; }
85 
freeAllocation(void)86 	void				freeAllocation	(void) { delete m_allocation.release(); }
87 
88 private:
89 	de::MovePtr<vk::Allocation>		m_allocation;
90 	vk::Move<vk::VkBuffer>			m_buffer;
91 
92 	Buffer(const Buffer&);          // "deleted"
93 	Buffer&							operator=		(const Buffer&);
94 };
95 
Buffer(const DeviceInterface & vk,const VkDevice device,Allocator & allocator,const VkBufferCreateInfo & bufferCreateInfo,const MemoryRequirement memoryRequirement)96 Buffer::Buffer(const DeviceInterface&		vk,
97 			   const VkDevice				device,
98 			   Allocator&					allocator,
99 			   const VkBufferCreateInfo&	bufferCreateInfo,
100 			   const MemoryRequirement		memoryRequirement)
101 {
102 	m_buffer = createBuffer(vk, device, &bufferCreateInfo);
103 	m_allocation = allocator.allocate(getBufferMemoryRequirements(vk, device, *m_buffer), memoryRequirement);
104 	VK_CHECK(vk.bindBufferMemory(device, *m_buffer, m_allocation->getMemory(), m_allocation->getOffset()));
105 }
106 
107 
108 class UseAfterFreeTestCase : public vkt::TestCase
109 {
110 public:
111 	void								initPrograms			(vk::SourceCollections&	sourceCollections) const;
112 	TestInstance*						createInstance			(Context&			context) const;
113 
114 	static UseAfterFreeTestCase*		UBOToSSBOInvertCase		(tcu::TestContext&	testCtx,
115 																const std::string&  name,
116 																const std::string&  description,
117 																const deUint32		numValues,
118 																const tcu::IVec3&	localSize,
119 																const tcu::IVec3&	workSize);
120 
121 	static UseAfterFreeTestCase*		CopyInvertSSBOCase		(tcu::TestContext&	testCtx,
122 																const std::string&	name,
123 																const std::string&	description,
124 																const deUint32		numValues,
125 																const tcu::IVec3&	localSize,
126 																const tcu::IVec3&	workSize);
127 
128 private:
129 										UseAfterFreeTestCase	(tcu::TestContext&	testCtx,
130 																const std::string& name,
131 																const std::string& description,
132 																const deUint32		numValues,
133 																const tcu::IVec3&	localSize,
134 																const tcu::IVec3&	workSize,
135 																const BufferType	bufferType);
136 
137 	const BufferType					m_bufferType;
138 	const deUint32                      m_numValues;
139 	const tcu::IVec3					m_localSize;
140 	const tcu::IVec3					m_workSize;
141 };
142 
143 class UseAfterFreeTestInstance : public PostmortemTestInstance
144 {
145 public:
146 	UseAfterFreeTestInstance(Context&			context,
147 							 const deUint32		numValues,
148 							 const tcu::IVec3&	localSize,
149 							 const tcu::IVec3&	workSize,
150 							 const BufferType	bufferType);
151 
152 	tcu::TestStatus					iterate						(void);
153 
154 private:
155 	const BufferType				m_bufferType;
156 	const deUint32					m_numValues;
157 	const tcu::IVec3				m_localSize;
158 	const tcu::IVec3				m_workSize;
159 };
160 
161 template<typename T, int size>
multiplyComponents(const tcu::Vector<T,size> & v)162 T multiplyComponents(const tcu::Vector<T, size>& v)
163 {
164 	T accum = 1;
165 	for (int i = 0; i < size; ++i)
166 		accum *= v[i];
167 	return accum;
168 }
169 
UseAfterFreeTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const deUint32 numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize,const BufferType bufferType)170 UseAfterFreeTestCase::UseAfterFreeTestCase (tcu::TestContext&	testCtx,
171 											const std::string&	name,
172 											const std::string&	description,
173 											const deUint32		numValues,
174 											const tcu::IVec3&	localSize,
175 											const tcu::IVec3&	workSize,
176 											const BufferType		bufferType)
177 	: TestCase		(testCtx, name, description)
178 	, m_bufferType	(bufferType)
179 	, m_numValues	(numValues)
180 	, m_localSize	(localSize)
181 	, m_workSize	(workSize)
182 {
183 	DE_ASSERT(m_numValues % (multiplyComponents(m_workSize) * multiplyComponents(m_localSize)) == 0);
184 	DE_ASSERT(m_bufferType == BUFFER_TYPE_UNIFORM || m_bufferType == BUFFER_TYPE_SSBO);
185 }
186 
UBOToSSBOInvertCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const deUint32 numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize)187 UseAfterFreeTestCase* UseAfterFreeTestCase::UBOToSSBOInvertCase (tcu::TestContext&	testCtx,
188 																const std::string&	name,
189 																const std::string&	description,
190 																const deUint32		numValues,
191 																const tcu::IVec3&	localSize,
192 																const tcu::IVec3&	workSize)
193 {
194 	return new UseAfterFreeTestCase(testCtx, name, description, numValues, localSize, workSize, BUFFER_TYPE_UNIFORM);
195 }
196 
CopyInvertSSBOCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const deUint32 numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize)197 UseAfterFreeTestCase* UseAfterFreeTestCase::CopyInvertSSBOCase (tcu::TestContext&	testCtx,
198 																const std::string&	name,
199 																const std::string&	description,
200 																const deUint32		numValues,
201 																const tcu::IVec3&	localSize,
202 																const tcu::IVec3&	workSize)
203 {
204 	return new UseAfterFreeTestCase(testCtx, name, description, numValues, localSize, workSize, BUFFER_TYPE_SSBO);
205 }
206 
initPrograms(SourceCollections & sourceCollections) const207 void UseAfterFreeTestCase::initPrograms	(SourceCollections& sourceCollections) const
208 {
209 	std::ostringstream src;
210 	if (m_bufferType == BUFFER_TYPE_UNIFORM)
211 	{
212 		src << "#version 310 es\n"
213 			<< "layout (local_size_x = " << m_localSize.x() << ", local_size_y = " << m_localSize.y() << ", local_size_z = " << m_localSize.z() << ") in;\n"
214 			<< "layout(binding = 0) readonly uniform Input {\n"
215 			<< "    uint values[" << m_numValues << "];\n"
216 			<< "} ub_in;\n"
217 			<< "layout(binding = 1, std140) writeonly buffer Output {\n"
218 			<< "    uint values[" << m_numValues << "];\n"
219 			<< "} sb_out;\n"
220 			<< "void main (void) {\n"
221 			<< "    uvec3 size           = gl_NumWorkGroups * gl_WorkGroupSize;\n"
222 			<< "    uint numValuesPerInv = uint(ub_in.values.length()) / (size.x*size.y*size.z);\n"
223 			<< "    uint groupNdx        = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
224 			<< "    uint offset          = numValuesPerInv*groupNdx;\n"
225 			<< "\n"
226 			<< "    for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
227 			<< "        sb_out.values[offset + ndx] = ~ub_in.values[offset + ndx];\n"
228 			<< "}\n";
229 	}
230 	else if (m_bufferType == BUFFER_TYPE_SSBO)
231 	{
232 		src << "#version 310 es\n"
233 			<< "layout (local_size_x = " << m_localSize.x() << ", local_size_y = " << m_localSize.y() << ", local_size_z = " << m_localSize.z() << ") in;\n"
234 			<< "layout(binding = 0, std140) readonly buffer Input {\n"
235 			<< "    uint values[" << m_numValues << "];\n"
236 			<< "} sb_in;\n"
237 			<< "layout (binding = 1, std140) writeonly buffer Output {\n"
238 			<< "    uint values[" << m_numValues << "];\n"
239 			<< "} sb_out;\n"
240 			<< "void main (void) {\n"
241 			<< "    uvec3 size           = gl_NumWorkGroups * gl_WorkGroupSize;\n"
242 			<< "    uint numValuesPerInv = uint(sb_in.values.length()) / (size.x*size.y*size.z);\n"
243 			<< "    uint groupNdx        = size.x*size.y*gl_GlobalInvocationID.z + size.x*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
244 			<< "    uint offset          = numValuesPerInv*groupNdx;\n"
245 			<< "\n"
246 			<< "    for (uint ndx = 0u; ndx < numValuesPerInv; ndx++)\n"
247 			<< "        sb_out.values[offset + ndx] = ~sb_in.values[offset + ndx];\n"
248 			<< "}\n";
249 	}
250 
251 	sourceCollections.glslSources.add("comp") << glu::ComputeSource(src.str());
252 }
253 
createInstance(Context & context) const254 TestInstance* UseAfterFreeTestCase::createInstance(Context& context) const
255 {
256 	return new UseAfterFreeTestInstance(context, m_numValues, m_localSize, m_workSize, m_bufferType);
257 }
258 
UseAfterFreeTestInstance(Context & context,const deUint32 numValues,const tcu::IVec3 & localSize,const tcu::IVec3 & workSize,const BufferType bufferType)259 UseAfterFreeTestInstance::UseAfterFreeTestInstance (Context&			context,
260 													const deUint32		numValues,
261 													const tcu::IVec3&	localSize,
262 													const tcu::IVec3&	workSize,
263 													const BufferType	bufferType)
264 													: PostmortemTestInstance(context)
265 													, m_bufferType(bufferType)
266 													, m_numValues(numValues)
267 													, m_localSize(localSize)
268 													, m_workSize(workSize)
269 {
270 
271 }
272 
iterate(void)273 tcu::TestStatus UseAfterFreeTestInstance::iterate(void)
274 {
275 	const VkDevice			device				= *m_logicalDevice;
276 	const DeviceInterface&	vk					= m_deviceDriver;
277 	const VkQueue			queue				= m_queue;
278 	const deUint32			queueFamilyIndex	= m_queueFamilyIndex;
279 	Allocator&				allocator			= m_allocator;
280 
281 	// Customize the test based on buffer type
282 
283 	const VkBufferUsageFlags inputBufferUsageFlags = (m_bufferType == BUFFER_TYPE_UNIFORM ? VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
284 	const VkDescriptorType inputBufferDescriptorType = (m_bufferType == BUFFER_TYPE_UNIFORM ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
285 	const deUint32 randomSeed = (m_bufferType == BUFFER_TYPE_UNIFORM ? 0x111223f : 0x124fef);
286 
287 	// Create an input buffer
288 
289 	const VkDeviceSize bufferSizeBytes = sizeof(tcu::UVec4) * m_numValues;
290 	Buffer inputBuffer(vk, device, allocator, makeBufferCreateInfo(bufferSizeBytes, inputBufferUsageFlags), MemoryRequirement::HostVisible);
291 
292 	// Fill the input buffer with data
293 	{
294 		de::Random rnd(randomSeed);
295 		const Allocation& inputBufferAllocation = inputBuffer.getAllocation();
296 		tcu::UVec4* bufferPtr = static_cast<tcu::UVec4*>(inputBufferAllocation.getHostPtr());
297 		for (deUint32 i = 0; i < m_numValues; ++i)
298 			bufferPtr[i].x() = rnd.getUint32();
299 
300 		flushAlloc(vk, device, inputBufferAllocation);
301 	}
302 
303 	// Create an output buffer
304 
305 	Buffer outputBuffer(vk, device, allocator, makeBufferCreateInfo(bufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
306 
307 	// Create descriptor set
308 
309 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(
310 		DescriptorSetLayoutBuilder()
311 		.addSingleBinding(inputBufferDescriptorType, VK_SHADER_STAGE_COMPUTE_BIT)
312 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT)
313 		.build(vk, device));
314 
315 	const Unique<VkDescriptorPool> descriptorPool(
316 		DescriptorPoolBuilder()
317 		.addType(inputBufferDescriptorType)
318 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
319 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
320 
321 	const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
322 
323 	const VkDescriptorBufferInfo inputBufferDescriptorInfo = makeDescriptorBufferInfo(*inputBuffer, 0ull, bufferSizeBytes);
324 	const VkDescriptorBufferInfo outputBufferDescriptorInfo = makeDescriptorBufferInfo(*outputBuffer, 0ull, bufferSizeBytes);
325 	DescriptorSetUpdateBuilder()
326 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), inputBufferDescriptorType, &inputBufferDescriptorInfo)
327 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outputBufferDescriptorInfo)
328 		.update(vk, device);
329 
330 	// Perform the computation
331 
332 	const Unique<VkShaderModule> shaderModule(createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
333 	const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
334 	const Unique<VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
335 
336 	const VkBufferMemoryBarrier hostWriteBarrier = makeBufferMemoryBarrier(VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, *inputBuffer, 0ull, bufferSizeBytes);
337 
338 	const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *outputBuffer, 0ull, bufferSizeBytes);
339 
340 	const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
341 	const Unique<VkCommandBuffer> cmdBuffer(allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
342 
343 	// Start recording commands
344 
345 	beginCommandBuffer(vk, *cmdBuffer);
346 
347 	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
348 	vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
349 
350 	vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &hostWriteBarrier, 0, (const VkImageMemoryBarrier*)DE_NULL);
351 	vk.cmdDispatch(*cmdBuffer, m_workSize.x(), m_workSize.y(), m_workSize.z());
352 	vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &shaderWriteBarrier, 0, (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 }
367 
createUseAfterFreeTests(tcu::TestContext & testCtx)368 tcu::TestCaseGroup* createUseAfterFreeTests(tcu::TestContext& testCtx)
369 {
370 	de::MovePtr<tcu::TestCaseGroup> useAfterFreeGroup(new tcu::TestCaseGroup(testCtx, "use_after_free", "Use buffer after free."));
371 
372 	useAfterFreeGroup->addChild(UseAfterFreeTestCase::UBOToSSBOInvertCase(testCtx, "ubo_to_ssbo_single_invocation", "Copy from UBO to SSBO, inverting bits", 256, tcu::IVec3(1, 1, 1), tcu::IVec3(1, 1, 1)));
373 	useAfterFreeGroup->addChild(UseAfterFreeTestCase::CopyInvertSSBOCase (testCtx, "ssbo_to_ssbo_single_invocation", "Copy from SSBO to SSBO, inverting bits", 256, tcu::IVec3(1, 1, 1), tcu::IVec3(1, 1, 1)));
374 
375 	return useAfterFreeGroup.release();
376 }
377 
378 } // postmortem
379 } // vkt
380