Skip to content

Commit 4db6bd3

Browse files
authored
Improved bounds checking for C++/CUDA (shader-slang#2263)
* #include an absolute path didn't work - because paths were taken to always be relative. * Use TerminatedUnownedStringSlice for literals in output C++. * Remove Escape/Unescape functions used in slang-token-reader.cpp Add target type of 'host-cpp' etc to map to the target types. * Fix some corner cases around string encoding. * Added unit test for string escaping. Fixed some assorted escaping bugs. * Updated test output. * Added decode test. * Stop using hex output, to get around 'greedy' aspect. Use octal instead. * Added HostHostCallable Small changes to use ArtifactDesc/Info instead of large switches. * Fix C++ emit to handle arbitrary function export. * Add options handling for callable without an output being specified. * Can compile with COM interface. Added example using com interface. * Use the IR Ptr type instead of hack in C++ emit for interfaces. * Fix issue with outputting the COM call when ptr is used. * Fix crash issue on compilation failure. * Add support for __global. * Added `ActualGlobalRate` Added special handling around globals and COM interfaces. Tested out in cpu-com-example. * Fix typo in NodeBase. * Support for accessing globals by name working. * Bounds checking for C++ Improved bounds checks for CUDA. * Check that actual global initialization is working. * Fix typo. * Refactor the com replacement such that it doesn't need a cache or do anything special with GlobalVar. * Fix typo in CUDA prelude. * Remove context. Only create replacement if needed. * Split out COM host-callable into a unit-test. * host-callable com testing on C++and llvm. * Comment around the COM ptr replacement. * WIP Zero bound test. * Disable com test on vs 32 bit. Fix C++ prelude * Disable 32 bit targets testing com host-callable. * For now disable zero index test. * Enable bounds checking for CPU/CUDA. * Small fixes. Disable CUDA zero index bound fix. * Add test result for bound check. * Work around for index wrapping issue. * Added Fixed array test. * Only enable prelude asserts via SLANG_PRELUDE_ENABLE_ASSERT (unless defined by the user)
1 parent 1146920 commit 4db6bd3

9 files changed

+242
-109
lines changed

prelude/slang-cpp-prelude.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ Any platforms not detected by the above logic are now now explicitly zeroed out.
194194
# endif
195195
#endif /* SLANG_PLATFORM */
196196

197-
198197
/* Shorthands for "families" of compilers/platforms */
199198
#define SLANG_GCC_FAMILY (SLANG_CLANG || SLANG_SNC || SLANG_GHS || SLANG_GCC)
200199
#define SLANG_WINDOWS_FAMILY (SLANG_WINRT || SLANG_WIN32 || SLANG_WIN64)
@@ -249,8 +248,12 @@ convention for interface methods.
249248
# define SLANG_MCALL SLANG_STDCALL
250249
#endif
251250

251+
#ifndef SLANG_FORCE_INLINE
252+
# define SLANG_FORCE_INLINE inline
253+
#endif
252254

253-
255+
// TODO(JS): Should these be in slang-cpp-types.h?
256+
// They are more likely to clash with slang.h
254257

255258
struct SlangUUID
256259
{
@@ -271,6 +274,8 @@ struct ISlangUnknown
271274

272275
#endif // SLANG_H
273276

277+
// Includes
278+
274279
#include "slang-cpp-types.h"
275280
#include "slang-cpp-scalar-intrinsics.h"
276281

prelude/slang-cpp-types.h

+70-37
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,48 @@
22
#define SLANG_PRELUDE_CPP_TYPES_H
33

44
#ifndef SLANG_PRELUDE_ASSERT
5-
# ifdef _DEBUG
5+
# ifdef SLANG_PRELUDE_ENABLE_ASSERT
66
# define SLANG_PRELUDE_ASSERT(VALUE) assert(VALUE)
77
# else
88
# define SLANG_PRELUDE_ASSERT(VALUE)
99
# endif
1010
#endif
1111

12-
#ifndef SLANG_FORCE_INLINE
13-
# define SLANG_FORCE_INLINE inline
12+
// Since we are using unsigned arithmatic care is need in this comparison.
13+
// It is *assumed* that sizeInBytes >= elemSize. Which means (sizeInBytes >= elemSize) >= 0
14+
// Which means only a single test is needed
15+
16+
// Asserts for bounds checking.
17+
// It is assumed index/count are unsigned types.
18+
#define SLANG_BOUND_ASSERT(index, count) SLANG_PRELUDE_ASSERT(index < count);
19+
#define SLANG_BOUND_ASSERT_BYTE_ADDRESS(index, elemSize, sizeInBytes) SLANG_PRELUDE_ASSERT(index <= (sizeInBytes - elemSize) && (index & 3) == 0);
20+
21+
// Macros to zero index if an access is out of range
22+
#define SLANG_BOUND_ZERO_INDEX(index, count) index = (index < count) ? index : 0;
23+
#define SLANG_BOUND_ZERO_INDEX_BYTE_ADDRESS(index, elemSize, sizeInBytes) index = (index <= (sizeInBytes - elemSize)) ? index : 0;
24+
25+
// The 'FIX' macro define how the index is fixed. The default is to do nothing. If SLANG_ENABLE_BOUND_ZERO_INDEX
26+
// the fix macro will zero the index, if out of range
27+
#ifdef SLANG_ENABLE_BOUND_ZERO_INDEX
28+
# define SLANG_BOUND_FIX(index, count) SLANG_BOUND_ZERO_INDEX(index, count)
29+
# define SLANG_BOUND_FIX_BYTE_ADDRESS(index, elemSize, sizeInBytes) SLANG_BOUND_ZERO_INDEX_BYTE_ADDRESS(index, elemSize, sizeInBytes)
30+
# define SLANG_BOUND_FIX_FIXED_ARRAY(index, count) SLANG_BOUND_ZERO_INDEX(index, count)
31+
#else
32+
# define SLANG_BOUND_FIX(index, count)
33+
# define SLANG_BOUND_FIX_BYTE_ADDRESS(index, elemSize, sizeInBytes)
34+
# define SLANG_BOUND_FIX_FIXED_ARRAY(index, count)
35+
#endif
36+
37+
#ifndef SLANG_BOUND_CHECK
38+
# define SLANG_BOUND_CHECK(index, count) SLANG_BOUND_ASSERT(index, count) SLANG_BOUND_FIX(index, count)
39+
#endif
40+
41+
#ifndef SLANG_BOUND_CHECK_BYTE_ADDRESS
42+
# define SLANG_BOUND_CHECK_BYTE_ADDRESS(index, elemSize, sizeInBytes) SLANG_BOUND_ASSERT_BYTE_ADDRESS(index, elemSize, sizeInBytes) SLANG_BOUND_FIX_BYTE_ADDRESS(index, elemSize, sizeInBytes)
43+
#endif
44+
45+
#ifndef SLANG_BOUND_CHECK_FIXED_ARRAY
46+
# define SLANG_BOUND_CHECK_FIXED_ARRAY(index, count) SLANG_BOUND_ASSERT(index, count) SLANG_BOUND_FIX_FIXED_ARRAY(index, count)
1447
#endif
1548

1649
#ifdef SLANG_PRELUDE_NAMESPACE
@@ -25,8 +58,8 @@ struct TypeInfo
2558
template <typename T, size_t SIZE>
2659
struct FixedArray
2760
{
28-
const T& operator[](size_t index) const { SLANG_PRELUDE_ASSERT(index < SIZE); return m_data[index]; }
29-
T& operator[](size_t index) { SLANG_PRELUDE_ASSERT(index < SIZE); return m_data[index]; }
61+
const T& operator[](size_t index) const { SLANG_BOUND_CHECK_FIXED_ARRAY(index, SIZE); return m_data[index]; }
62+
T& operator[](size_t index) { SLANG_BOUND_CHECK_FIXED_ARRAY(index, SIZE); return m_data[index]; }
3063

3164
T m_data[SIZE];
3265
};
@@ -36,8 +69,8 @@ struct FixedArray
3669
template <typename T>
3770
struct Array
3871
{
39-
const T& operator[](size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
40-
T& operator[](size_t index) { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
72+
const T& operator[](size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
73+
T& operator[](size_t index) { SLANG_BOUND_CHECK(index, count); return data[index]; }
4174

4275
T* data;
4376
size_t count;
@@ -126,8 +159,8 @@ typedef size_t NonUniformResourceIndex;
126159
template <typename T>
127160
struct RWStructuredBuffer
128161
{
129-
SLANG_FORCE_INLINE T& operator[](size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
130-
const T& Load(size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
162+
SLANG_FORCE_INLINE T& operator[](size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
163+
const T& Load(size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
131164
void GetDimensions(uint32_t* outNumStructs, uint32_t* outStride) { *outNumStructs = uint32_t(count); *outStride = uint32_t(sizeof(T)); }
132165

133166
T* data;
@@ -137,8 +170,8 @@ struct RWStructuredBuffer
137170
template <typename T>
138171
struct StructuredBuffer
139172
{
140-
SLANG_FORCE_INLINE const T& operator[](size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
141-
const T& Load(size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
173+
SLANG_FORCE_INLINE const T& operator[](size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
174+
const T& Load(size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
142175
void GetDimensions(uint32_t* outNumStructs, uint32_t* outStride) { *outNumStructs = uint32_t(count); *outStride = uint32_t(sizeof(T)); }
143176

144177
T* data;
@@ -149,8 +182,8 @@ struct StructuredBuffer
149182
template <typename T>
150183
struct RWBuffer
151184
{
152-
SLANG_FORCE_INLINE T& operator[](size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
153-
const T& Load(size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
185+
SLANG_FORCE_INLINE T& operator[](size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
186+
const T& Load(size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
154187
void GetDimensions(uint32_t* outCount) { *outCount = uint32_t(count); }
155188

156189
T* data;
@@ -160,8 +193,8 @@ struct RWBuffer
160193
template <typename T>
161194
struct Buffer
162195
{
163-
SLANG_FORCE_INLINE const T& operator[](size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
164-
const T& Load(size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
196+
SLANG_FORCE_INLINE const T& operator[](size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
197+
const T& Load(size_t index) const { SLANG_BOUND_CHECK(index, count); return data[index]; }
165198
void GetDimensions(uint32_t* outCount) { *outCount = uint32_t(count); }
166199

167200
T* data;
@@ -174,32 +207,32 @@ struct ByteAddressBuffer
174207
void GetDimensions(uint32_t* outDim) const { *outDim = uint32_t(sizeInBytes); }
175208
uint32_t Load(size_t index) const
176209
{
177-
SLANG_PRELUDE_ASSERT(index + 4 <= sizeInBytes && (index & 3) == 0);
210+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 4, sizeInBytes);
178211
return data[index >> 2];
179212
}
180213
uint2 Load2(size_t index) const
181214
{
182-
SLANG_PRELUDE_ASSERT(index + 8 <= sizeInBytes && (index & 3) == 0);
215+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 8, sizeInBytes);
183216
const size_t dataIdx = index >> 2;
184217
return uint2{data[dataIdx], data[dataIdx + 1]};
185218
}
186219
uint3 Load3(size_t index) const
187220
{
188-
SLANG_PRELUDE_ASSERT(index + 12 <= sizeInBytes && (index & 3) == 0);
221+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 12, sizeInBytes);
189222
const size_t dataIdx = index >> 2;
190223
return uint3{data[dataIdx], data[dataIdx + 1], data[dataIdx + 2]};
191224
}
192225
uint4 Load4(size_t index) const
193226
{
194-
SLANG_PRELUDE_ASSERT(index + 16 <= sizeInBytes && (index & 3) == 0);
227+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 16, sizeInBytes);
195228
const size_t dataIdx = index >> 2;
196229
return uint4{data[dataIdx], data[dataIdx + 1], data[dataIdx + 2], data[dataIdx + 3]};
197230
}
198231
template<typename T>
199-
T Load(size_t offset) const
232+
T Load(size_t index) const
200233
{
201-
SLANG_PRELUDE_ASSERT(offset + sizeof(T) <= sizeInBytes && (offset & (alignof(T)-1)) == 0);
202-
return *(T const*)((char*)data + offset);
234+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, sizeof(T), sizeInBytes);
235+
return *(const T*)(((const char*)data) + index);
203236
}
204237

205238
const uint32_t* data;
@@ -215,68 +248,68 @@ struct RWByteAddressBuffer
215248

216249
uint32_t Load(size_t index) const
217250
{
218-
SLANG_PRELUDE_ASSERT(index + 4 <= sizeInBytes && (index & 3) == 0);
251+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 4, sizeInBytes);
219252
return data[index >> 2];
220253
}
221254
uint2 Load2(size_t index) const
222255
{
223-
SLANG_PRELUDE_ASSERT(index + 8 <= sizeInBytes && (index & 3) == 0);
256+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 8, sizeInBytes);
224257
const size_t dataIdx = index >> 2;
225258
return uint2{data[dataIdx], data[dataIdx + 1]};
226259
}
227260
uint3 Load3(size_t index) const
228261
{
229-
SLANG_PRELUDE_ASSERT(index + 12 <= sizeInBytes && (index & 3) == 0);
262+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 12, sizeInBytes);
230263
const size_t dataIdx = index >> 2;
231264
return uint3{data[dataIdx], data[dataIdx + 1], data[dataIdx + 2]};
232265
}
233266
uint4 Load4(size_t index) const
234267
{
235-
SLANG_PRELUDE_ASSERT(index + 16 <= sizeInBytes && (index & 3) == 0);
268+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 16, sizeInBytes);
236269
const size_t dataIdx = index >> 2;
237270
return uint4{data[dataIdx], data[dataIdx + 1], data[dataIdx + 2], data[dataIdx + 3]};
238271
}
239272
template<typename T>
240-
T Load(size_t offset) const
273+
T Load(size_t index) const
241274
{
242-
SLANG_PRELUDE_ASSERT(offset + sizeof(T) <= sizeInBytes && (offset & (alignof(T)-1)) == 0);
243-
return *(T const*)((char*)data + offset);
275+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, sizeof(T), sizeInBytes);
276+
return *(const T*)(((const char*)data) + index);
244277
}
245278

246279
void Store(size_t index, uint32_t v) const
247280
{
248-
SLANG_PRELUDE_ASSERT(index + 4 <= sizeInBytes && (index & 3) == 0);
281+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 4, sizeInBytes);
249282
data[index >> 2] = v;
250283
}
251284
void Store2(size_t index, uint2 v) const
252285
{
253-
SLANG_PRELUDE_ASSERT(index + 8 <= sizeInBytes && (index & 3) == 0);
286+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 8, sizeInBytes);
254287
const size_t dataIdx = index >> 2;
255288
data[dataIdx + 0] = v.x;
256289
data[dataIdx + 1] = v.y;
257290
}
258291
void Store3(size_t index, uint3 v) const
259-
{
260-
SLANG_PRELUDE_ASSERT(index + 12 <= sizeInBytes && (index & 3) == 0);
292+
{
293+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 12, sizeInBytes);
261294
const size_t dataIdx = index >> 2;
262295
data[dataIdx + 0] = v.x;
263296
data[dataIdx + 1] = v.y;
264297
data[dataIdx + 2] = v.z;
265298
}
266299
void Store4(size_t index, uint4 v) const
267300
{
268-
SLANG_PRELUDE_ASSERT(index + 16 <= sizeInBytes && (index & 3) == 0);
301+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, 16, sizeInBytes);
269302
const size_t dataIdx = index >> 2;
270303
data[dataIdx + 0] = v.x;
271304
data[dataIdx + 1] = v.y;
272305
data[dataIdx + 2] = v.z;
273306
data[dataIdx + 3] = v.w;
274307
}
275308
template<typename T>
276-
void Store(size_t offset, T const& value) const
309+
void Store(size_t index, T const& value) const
277310
{
278-
SLANG_PRELUDE_ASSERT(offset + sizeof(T) <= sizeInBytes && (offset & (alignof(T)-1)) == 0);
279-
*(T*)((char*)data + offset) = value;
311+
SLANG_BOUND_CHECK_BYTE_ADDRESS(index, sizeof(T), sizeInBytes);
312+
*(T*)(((char*)data) + index) = value;
280313
}
281314

282315
uint32_t* data;

0 commit comments

Comments
 (0)