Skip to content

Commit 24e9e4f

Browse files
committed
Fix various unsafe vertex operations
This aims to reduce; Freezes / crashes on alt-tab: Early checks stop the base-index wrap that may have previously jumped into invalid memory. ** Normal stream setup now checks the resolved vertex range in signed 64-bit, then converts to uint only after confirming it isn’t negative or past the 32-bit ceiling. This blocks the unsigned wrap that was hammering fullscreen alt-tab. Buffer overruns: Offsets/sizes now verified against real buffer bounds before locks, so we don’t overrun the managed vertex buffer. All conversions stay within x86 limits and survive if a draw ever asks for more than x86 process can handle.
1 parent 0b530dd commit 24e9e4f

File tree

3 files changed

+253
-60
lines changed

3 files changed

+253
-60
lines changed

Client/core/CAdditionalVertexStreamManager.cpp

Lines changed: 195 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,49 @@
1111

1212
#include "StdInc.h"
1313
#include "CAdditionalVertexStreamManager.h"
14+
#include <limits>
1415

1516
CAdditionalVertexStreamManager* CAdditionalVertexStreamManager::ms_Singleton = nullptr;
1617

1718
namespace
1819
{
1920
// Helper functions for this file only
2021

21-
// Convert size of PT stream to sizeof of N stream
22-
uint ConvertPTSize(uint SizePT) { return SizePT * 12 / 20; }
22+
uint ConvertPTOffset(uint OffsetPT)
23+
{
24+
return (OffsetPT / 20) * 12;
25+
}
26+
27+
// Convert size of PT stream to sizeof of N stream (rounded up to the next vertex)
28+
uint ConvertPTSize(uint SizePT)
29+
{
30+
uint64_t padded = static_cast<uint64_t>(SizePT) + 19ull;
31+
uint64_t result = (padded / 20ull) * 12ull;
32+
if (result > std::numeric_limits<uint>::max())
33+
return std::numeric_limits<uint>::max();
34+
return static_cast<uint>(result);
35+
}
36+
37+
struct STriKey
38+
{
39+
uint32_t v0;
40+
uint32_t v1;
41+
uint32_t v2;
42+
43+
bool operator<(const STriKey& rhs) const
44+
{
45+
if (v0 != rhs.v0)
46+
return v0 < rhs.v0;
47+
if (v1 != rhs.v1)
48+
return v1 < rhs.v1;
49+
return v2 < rhs.v2;
50+
}
51+
};
2352

24-
// Get 64 bit key for a triangle by using the ordered vertex indices
25-
long long getTriKey(WORD a, WORD b, WORD c)
53+
// Get ordered key for a triangle by using the sorted vertex indices
54+
STriKey GetTriKey(uint32_t a, uint32_t b, uint32_t c)
2655
{
27-
WORD tmp;
56+
uint32_t tmp;
2857
if (b < a)
2958
{
3059
tmp = b;
@@ -43,7 +72,8 @@ namespace
4372
b = a;
4473
a = tmp;
4574
}
46-
return (((long long)a) << 32) | (((long long)b) << 16) | ((long long)c);
75+
STriKey key = {a, b, c};
76+
return key;
4777
}
4878
} // namespace
4979

@@ -145,14 +175,14 @@ bool CAdditionalVertexStreamManager::MaybeSetAdditionalVertexStream(D3DPRIMITIVE
145175
state.args.primCount = primCount;
146176

147177
// Cache info about state streams etc
148-
UpdateCurrentStateInfo(state);
178+
if (!UpdateCurrentStateInfo(state))
179+
return false;
149180

150181
// For now, this only works if the original has 3 decl elements (0:D, 1:P, 1:T) and stream 1 has a stride of 20
151182
if (!CheckCanDoThis(state))
152183
return false;
153184

154-
SetAdditionalVertexStream(state);
155-
return true;
185+
return SetAdditionalVertexStream(state);
156186
}
157187

158188
///////////////////////////////////////////////////////////////
@@ -162,7 +192,7 @@ bool CAdditionalVertexStreamManager::MaybeSetAdditionalVertexStream(D3DPRIMITIVE
162192
//
163193
//
164194
///////////////////////////////////////////////////////////////
165-
void CAdditionalVertexStreamManager::SetAdditionalVertexStream(SCurrentStateInfo& state)
195+
bool CAdditionalVertexStreamManager::SetAdditionalVertexStream(SCurrentStateInfo& state)
166196
{
167197
HRESULT hr;
168198

@@ -171,40 +201,81 @@ void CAdditionalVertexStreamManager::SetAdditionalVertexStream(SCurrentStateInfo
171201
if (!pAdditionalInfo)
172202
pAdditionalInfo = CreateAdditionalStreamInfo(state);
173203

204+
if (!pAdditionalInfo)
205+
return false;
206+
174207
uint StridePT = 20;
175208
uint StrideN = 12;
176209

177-
// Calc area we are going to use
178-
uint viMinBased = state.args.MinVertexIndex + state.args.BaseVertexIndex;
179-
uint viMaxBased = state.args.MinVertexIndex + state.args.NumVertices + state.args.BaseVertexIndex;
210+
const INT baseVertexIndex = state.args.BaseVertexIndex;
180211

181-
uint ReadOffsetStart = viMinBased * StridePT + state.stream1.OffsetInBytes;
182-
uint ReadOffsetSize = (viMaxBased - viMinBased) * StridePT;
212+
const int64_t maxUint = static_cast<int64_t>(std::numeric_limits<uint>::max());
213+
const int64_t viMinBasedSigned = static_cast<int64_t>(state.args.MinVertexIndex) + static_cast<int64_t>(baseVertexIndex);
214+
if (viMinBasedSigned < 0 || viMinBasedSigned > maxUint)
215+
return false;
216+
217+
const int64_t viMaxBasedSigned = viMinBasedSigned + static_cast<int64_t>(state.args.NumVertices);
218+
if (viMaxBasedSigned < viMinBasedSigned || viMaxBasedSigned > maxUint)
219+
return false;
183220

184-
uint OffsetInBytesN = ConvertPTSize(state.stream1.OffsetInBytes);
185-
uint WriteOffsetStart = viMinBased * StrideN + OffsetInBytesN;
186-
uint WriteOffsetSize = (viMaxBased - viMinBased) * StrideN;
221+
uint viMinBased = static_cast<uint>(viMinBasedSigned);
222+
uint viMaxBased = static_cast<uint>(viMaxBasedSigned);
223+
uint vertexSpan = viMaxBased - viMinBased;
224+
225+
uint64_t readOffsetStart64 = static_cast<uint64_t>(viMinBased) * StridePT + state.stream1.OffsetInBytes;
226+
if (readOffsetStart64 > std::numeric_limits<uint>::max())
227+
return false;
228+
uint ReadOffsetStart = static_cast<uint>(readOffsetStart64);
187229

188-
assert(WriteOffsetStart == ConvertPTSize(ReadOffsetStart));
230+
uint64_t readOffsetSize64 = static_cast<uint64_t>(vertexSpan) * StridePT;
231+
if (readOffsetSize64 > std::numeric_limits<uint>::max())
232+
return false;
233+
uint ReadOffsetSize = static_cast<uint>(readOffsetSize64);
234+
235+
uint OffsetInBytesN = ConvertPTOffset(state.stream1.OffsetInBytes);
236+
uint64_t writeOffsetStart64 = static_cast<uint64_t>(viMinBased) * StrideN + OffsetInBytesN;
237+
if (writeOffsetStart64 > std::numeric_limits<uint>::max())
238+
return false;
239+
uint WriteOffsetStart = static_cast<uint>(writeOffsetStart64);
240+
241+
uint64_t writeOffsetSize64 = static_cast<uint64_t>(vertexSpan) * StrideN;
242+
if (writeOffsetSize64 > std::numeric_limits<uint>::max())
243+
return false;
244+
uint WriteOffsetSize = static_cast<uint>(writeOffsetSize64);
245+
246+
assert(WriteOffsetStart == ConvertPTOffset(ReadOffsetStart));
189247
assert(WriteOffsetSize == ConvertPTSize(ReadOffsetSize));
190248

191249
// See if area VB area needs updating
192250
if (!pAdditionalInfo->ConvertedRanges.IsRangeSet(WriteOffsetStart, WriteOffsetSize))
193251
{
194-
// Update VB area
195-
UpdateAdditionalStreamContent(state, pAdditionalInfo, ReadOffsetStart, ReadOffsetSize, WriteOffsetStart, WriteOffsetSize);
252+
if (!UpdateAdditionalStreamContent(state, pAdditionalInfo, ReadOffsetStart, ReadOffsetSize, WriteOffsetStart, WriteOffsetSize))
253+
return false;
254+
196255
pAdditionalInfo->ConvertedRanges.SetRange(WriteOffsetStart, WriteOffsetSize);
197256
}
198257

199-
// Save old declaration
200-
hr = m_pDevice->GetVertexDeclaration(&m_pOldVertexDeclaration);
258+
IDirect3DVertexDeclaration9* pPreviousDecl = nullptr;
259+
if (FAILED(m_pDevice->GetVertexDeclaration(&pPreviousDecl)))
260+
return false;
201261

202-
// Set declaration
203-
hr = g_pProxyDevice->SetVertexDeclaration(pAdditionalInfo->pVertexDeclaration);
262+
if (FAILED(g_pProxyDevice->SetVertexDeclaration(pAdditionalInfo->pVertexDeclaration)))
263+
{
264+
SAFE_RELEASE(pPreviousDecl);
265+
return false;
266+
}
204267

205-
// Set additional stream
206-
uint OffsetInBytes = ConvertPTSize(state.stream1.OffsetInBytes);
207-
hr = m_pDevice->SetStreamSource(2, pAdditionalInfo->pStreamData, OffsetInBytes, pAdditionalInfo->Stride);
268+
uint OffsetInBytes = ConvertPTOffset(state.stream1.OffsetInBytes);
269+
if (FAILED(m_pDevice->SetStreamSource(2, pAdditionalInfo->pStreamData, OffsetInBytes, pAdditionalInfo->Stride)))
270+
{
271+
g_pProxyDevice->SetVertexDeclaration(pPreviousDecl);
272+
SAFE_RELEASE(pPreviousDecl);
273+
return false;
274+
}
275+
276+
SAFE_RELEASE(m_pOldVertexDeclaration);
277+
m_pOldVertexDeclaration = pPreviousDecl;
278+
return true;
208279
}
209280

210281
///////////////////////////////////////////////////////////////
@@ -249,6 +320,38 @@ bool CAdditionalVertexStreamManager::UpdateAdditionalStreamContent(SCurrentState
249320
uint NumVerts = ReadSize / StridePT;
250321
assert(NumVerts == WriteSize / StrideN);
251322

323+
if (ReadSize == 0 || WriteSize == 0)
324+
return false;
325+
326+
if ((ReadSize % StridePT) != 0)
327+
return false;
328+
329+
if ((WriteSize % StrideN) != 0)
330+
return false;
331+
332+
if (WriteSize != NumVerts * StrideN)
333+
return false;
334+
335+
if (NumVerts == 0)
336+
return false;
337+
338+
const UINT bufferSizePT = state.decl.VertexBufferDesc1.Size;
339+
if (ReadOffsetStart > bufferSizePT)
340+
return false;
341+
342+
if (ReadSize > bufferSizePT - ReadOffsetStart)
343+
return false;
344+
345+
D3DVERTEXBUFFER_DESC destDesc;
346+
if (FAILED(pStreamDataN->GetDesc(&destDesc)))
347+
return false;
348+
349+
if (WriteOffsetStart > destDesc.Size)
350+
return false;
351+
352+
if (WriteSize > destDesc.Size - WriteOffsetStart)
353+
return false;
354+
252355
// Get the source vertex bytes
253356
std::vector<uchar> sourceArray;
254357
sourceArray.resize(ReadSize);
@@ -269,47 +372,73 @@ bool CAdditionalVertexStreamManager::UpdateAdditionalStreamContent(SCurrentState
269372
// Compute dest bytes
270373
{
271374
// Get index buffer
272-
if (FAILED(m_pDevice->GetIndices(&state.pIndexData)))
375+
if (FAILED(m_pDevice->GetIndices(&state.pIndexData)) || !state.pIndexData)
273376
return false;
274377

275378
// Get index buffer desc
276379
D3DINDEXBUFFER_DESC IndexBufferDesc;
277380
state.pIndexData->GetDesc(&IndexBufferDesc);
278381

382+
uint indexStride = 0;
383+
if (IndexBufferDesc.Format == D3DFMT_INDEX16)
384+
indexStride = sizeof(WORD);
385+
else if (IndexBufferDesc.Format == D3DFMT_INDEX32)
386+
indexStride = sizeof(DWORD);
387+
else
388+
return false;
389+
279390
uint numIndices = state.args.primCount + 2;
280391
uint step = 1;
281392
if (state.args.PrimitiveType == D3DPT_TRIANGLELIST)
282393
{
283394
numIndices = state.args.primCount * 3;
284395
step = 3;
285396
}
286-
assert(IndexBufferDesc.Size >= (numIndices + state.args.startIndex) * 2);
287397

288-
// Get index buffer data
289-
std::vector<uchar> indexArray;
290-
indexArray.resize(ReadSize);
291-
uchar* pIndexArrayBytes = &indexArray[0];
398+
size_t startByte = static_cast<size_t>(state.args.startIndex) * indexStride;
399+
size_t requiredBytes = static_cast<size_t>(numIndices) * indexStride;
400+
if (startByte > IndexBufferDesc.Size)
401+
return false;
402+
403+
if (requiredBytes > IndexBufferDesc.Size - startByte)
404+
return false;
405+
406+
std::vector<uint32_t> indices(numIndices);
292407
{
293408
void* pIndexBytes = nullptr;
294-
if (FAILED(state.pIndexData->Lock(state.args.startIndex * 2, numIndices * 2, &pIndexBytes, D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY)))
409+
if (FAILED(state.pIndexData->Lock(static_cast<UINT>(startByte), static_cast<UINT>(requiredBytes), &pIndexBytes,
410+
D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY)))
295411
return false;
296-
memcpy(pIndexArrayBytes, pIndexBytes, numIndices * 2);
412+
413+
if (indexStride == sizeof(WORD))
414+
{
415+
const WORD* pSrc = static_cast<const WORD*>(pIndexBytes);
416+
for (uint i = 0; i < numIndices; ++i)
417+
indices[i] = pSrc[i];
418+
}
419+
else
420+
{
421+
const DWORD* pSrc = static_cast<const DWORD*>(pIndexBytes);
422+
for (uint i = 0; i < numIndices; ++i)
423+
indices[i] = pSrc[i];
424+
}
425+
297426
state.pIndexData->Unlock();
298427
}
299428

300429
// Calc normals
301430
std::vector<CVector> NormalList;
302431
NormalList.insert(NormalList.end(), NumVerts, CVector());
303432

304-
std::map<long long, CVector> doneTrisMap;
433+
std::map<STriKey, CVector> doneTrisMap;
305434

306435
// For each triangle
307436
for (uint i = 0; i < numIndices - 2; i += step)
308437
{
309438
// Get triangle vertex indici
310-
WORD v0 = ((WORD*)pIndexArrayBytes)[i];
311-
WORD v1 = ((WORD*)pIndexArrayBytes)[i + 1];
312-
WORD v2 = ((WORD*)pIndexArrayBytes)[i + 2];
439+
uint32_t v0 = indices[i];
440+
uint32_t v1 = indices[i + 1];
441+
uint32_t v2 = indices[i + 2];
313442

314443
if (v0 >= NumVerts || v1 >= NumVerts || v2 >= NumVerts)
315444
continue; // vert index out of range
@@ -318,9 +447,9 @@ bool CAdditionalVertexStreamManager::UpdateAdditionalStreamContent(SCurrentState
318447
continue; // degenerate tri
319448

320449
// Get vertex positions from original stream
321-
CVector* pPos0 = (CVector*)(pSourceArrayBytes + v0 * 20);
322-
CVector* pPos1 = (CVector*)(pSourceArrayBytes + v1 * 20);
323-
CVector* pPos2 = (CVector*)(pSourceArrayBytes + v2 * 20);
450+
CVector* pPos0 = reinterpret_cast<CVector*>(pSourceArrayBytes + v0 * StridePT);
451+
CVector* pPos1 = reinterpret_cast<CVector*>(pSourceArrayBytes + v1 * StridePT);
452+
CVector* pPos2 = reinterpret_cast<CVector*>(pSourceArrayBytes + v2 * StridePT);
324453

325454
// Calculate the normal
326455
CVector Dir1 = *pPos2 - *pPos1;
@@ -335,7 +464,7 @@ bool CAdditionalVertexStreamManager::UpdateAdditionalStreamContent(SCurrentState
335464
Normal = -Normal;
336465

337466
// Try to improve results by ignoring duplicated triangles
338-
long long key = getTriKey(v0, v1, v2);
467+
STriKey key = GetTriKey(v0, v1, v2);
339468
if (CVector* pDoneTriPrevNormal = MapFind(doneTrisMap, key))
340469
{
341470
// Already done this tri - Keep prev tri if it has a better 'up' rating
@@ -365,7 +494,7 @@ bool CAdditionalVertexStreamManager::UpdateAdditionalStreamContent(SCurrentState
365494
Normal = CVector(0, 0, 1);
366495

367496
// Set
368-
CVector* pNormal = (CVector*)(pDestArrayBytes + i * 12);
497+
CVector* pNormal = reinterpret_cast<CVector*>(pDestArrayBytes + i * StrideN);
369498
*pNormal = Normal;
370499
}
371500
}
@@ -504,7 +633,7 @@ SAdditionalStreamInfo* CAdditionalVertexStreamManager::CreateAdditionalStreamInf
504633

505634
// Create new stream
506635
info.Stride = sizeof(float) * 3;
507-
UINT Size2 = ConvertPTSize(state.decl.VertexBufferDesc1.Size);
636+
UINT Size2 = ConvertPTSize(state.decl.VertexBufferDesc1.Size);
508637
if (FAILED(m_pDevice->CreateVertexBuffer(Size2, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &info.pStreamData, nullptr)))
509638
{
510639
SAFE_RELEASE(info.pVertexDeclaration);
@@ -549,6 +678,25 @@ void CAdditionalVertexStreamManager::OnVertexBufferRangeInvalidated(IDirect3DVer
549678
SAdditionalStreamInfo* pAdditionalInfo = GetAdditionalStreamInfo(pStreamData1);
550679
if (pAdditionalInfo)
551680
{
552-
pAdditionalInfo->ConvertedRanges.UnsetRange(Offset, Size);
681+
if (Size == 0)
682+
{
683+
pAdditionalInfo->ConvertedRanges = CRanges();
684+
return;
685+
}
686+
687+
uint convertedOffset = ConvertPTOffset(Offset);
688+
689+
uint verticesTouched = Size / 20;
690+
if ((Size % 20) != 0)
691+
++verticesTouched;
692+
693+
if (verticesTouched == 0)
694+
verticesTouched = 1;
695+
696+
uint64_t convertedSize64 = static_cast<uint64_t>(verticesTouched) * static_cast<uint64_t>(pAdditionalInfo->Stride);
697+
uint convertedSize = convertedSize64 > std::numeric_limits<uint>::max() ? std::numeric_limits<uint>::max()
698+
: static_cast<uint>(convertedSize64);
699+
700+
pAdditionalInfo->ConvertedRanges.UnsetRange(convertedOffset, convertedSize);
553701
}
554702
}

Client/core/CAdditionalVertexStreamManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class CAdditionalVertexStreamManager
8888
static void DestroySingleton();
8989

9090
protected:
91-
void SetAdditionalVertexStream(SCurrentStateInfo& renderState);
91+
bool SetAdditionalVertexStream(SCurrentStateInfo& renderState);
9292
bool UpdateCurrentStateInfo(SCurrentStateInfo& state);
9393
bool UpdateAdditionalStreamContent(SCurrentStateInfo& state, SAdditionalStreamInfo* pAdditionalStreamInfo, uint ReadOffsetStart, uint ReadSize,
9494
uint WriteOffsetStart, uint WriteSize);

0 commit comments

Comments
 (0)