summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorCopilot <198982749+Copilot@users.noreply.github.com>2025-07-17 20:58:02 -0700
committerGitHub <noreply@github.com>2025-07-18 03:58:02 +0000
commit85edfb178cd243134f4bb3d35ad71f154d76c81c (patch)
tree68f5340c8796f6c2d5ff343cabf3d96c7d387ac5 /source/slang
parent447d73f8c2245d061b0e84890fb994a77816a736 (diff)
Add bounds checking for out-of-bounds array access with constant indices (#7814)
* Initial plan * Implement out-of-bounds array access checking Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Add tests and format code for array bounds checking Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Move checkForOutOfBoundAccess to separate file and refactor using InstPassBase Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Optimize out-of-bounds checker to use single IR traversal Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Fix DiagnosticSink forward declaration from struct to class Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Update 0-array-1 test to use runtime indices to avoid bounds checking diagnostic Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Use SV_DispatchThreadID for truly runtime array access in 0-array-1 test Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com>
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/slang-check-out-of-bound-access.cpp89
-rw-r--r--source/slang/slang-check-out-of-bound-access.h12
-rw-r--r--source/slang/slang-diagnostic-defs.h5
-rw-r--r--source/slang/slang-emit.cpp2
4 files changed, 108 insertions, 0 deletions
diff --git a/source/slang/slang-check-out-of-bound-access.cpp b/source/slang/slang-check-out-of-bound-access.cpp
new file mode 100644
index 000000000..51adc2b4c
--- /dev/null
+++ b/source/slang/slang-check-out-of-bound-access.cpp
@@ -0,0 +1,89 @@
+// slang-check-out-of-bound-access.cpp
+#include "slang-check-out-of-bound-access.h"
+
+#include "slang-ir-inst-pass-base.h"
+#include "slang-ir-insts.h"
+#include "slang-ir.h"
+
+namespace Slang
+{
+
+struct OutOfBoundAccessChecker : public InstPassBase
+{
+ DiagnosticSink* sink;
+
+ OutOfBoundAccessChecker(IRModule* inModule, DiagnosticSink* inSink)
+ : InstPassBase(inModule), sink(inSink)
+ {
+ }
+
+
+ void checkArrayAccess(IRInst* inst, IRInst* base, IRInst* index)
+ {
+ // Check if index is a constant integer
+ auto indexLit = as<IRIntLit>(index);
+ if (!indexLit)
+ return; // Skip non-constant indices
+
+ // Get the base type
+ auto baseType = base->getDataType();
+
+ // Handle pointer-to-array case (for GetElementPtr)
+ if (auto ptrType = as<IRPtrTypeBase>(baseType))
+ {
+ baseType = ptrType->getValueType();
+ }
+
+ // Check if base is an array type
+ auto arrayType = as<IRArrayTypeBase>(baseType);
+ if (!arrayType)
+ return; // Skip non-array types
+
+ // Check if array size is a constant
+ auto arraySizeInst = arrayType->getElementCount();
+ auto arraySizeLit = as<IRIntLit>(arraySizeInst);
+ if (!arraySizeLit)
+ return; // Skip arrays with non-constant size
+
+ // Get the actual values
+ IRIntegerValue indexValue = indexLit->getValue();
+ IRIntegerValue arraySizeValue = arraySizeLit->getValue();
+
+ // Check bounds: index should be >= 0 and < arraySize
+ if (indexValue < 0 || indexValue >= arraySizeValue)
+ {
+ sink->diagnose(inst, Diagnostics::arrayIndexOutOfBounds, indexValue, arraySizeValue);
+ }
+ }
+
+ void processModule()
+ {
+ processAllInsts(
+ [&](IRInst* inst)
+ {
+ switch (inst->getOp())
+ {
+ case kIROp_GetElement:
+ case kIROp_GetElementPtr:
+ {
+ if (inst->getOperandCount() < 2)
+ return;
+
+ auto base = inst->getOperand(0);
+ auto index = inst->getOperand(1);
+
+ checkArrayAccess(inst, base, index);
+ }
+ break;
+ }
+ });
+ }
+};
+
+void checkForOutOfBoundAccess(IRModule* module, DiagnosticSink* sink)
+{
+ OutOfBoundAccessChecker checker(module, sink);
+ checker.processModule();
+}
+
+} // namespace Slang \ No newline at end of file
diff --git a/source/slang/slang-check-out-of-bound-access.h b/source/slang/slang-check-out-of-bound-access.h
new file mode 100644
index 000000000..17f4e8c3d
--- /dev/null
+++ b/source/slang/slang-check-out-of-bound-access.h
@@ -0,0 +1,12 @@
+// slang-check-out-of-bound-access.h
+#pragma once
+
+#include "slang-ir.h"
+
+namespace Slang
+{
+struct IRModule;
+class DiagnosticSink;
+
+void checkForOutOfBoundAccess(IRModule* module, DiagnosticSink* sink);
+} // namespace Slang \ No newline at end of file
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 3d584679e..0ce5d9f47 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -659,6 +659,11 @@ DIAGNOSTIC(
"Cannot convert array of size $0 to array of size $1 as this would truncate data")
DIAGNOSTIC(30025, Error, invalidArraySize, "array size must be non-negative.")
DIAGNOSTIC(
+ 30029,
+ Error,
+ arrayIndexOutOfBounds,
+ "array index '$0' is out of bounds for array of size '$1'.")
+DIAGNOSTIC(
30026,
Error,
returnInComponentMustComeLast,
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index 1657a2980..f40679bd9 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -9,6 +9,7 @@
#include "../core/slang-performance-profiler.h"
#include "../core/slang-type-text-util.h"
#include "../core/slang-writer.h"
+#include "slang-check-out-of-bound-access.h"
#include "slang-emit-c-like.h"
#include "slang-emit-cpp.h"
#include "slang-emit-cuda.h"
@@ -1109,6 +1110,7 @@ Result linkAndOptimizeIR(
{
checkForRecursiveTypes(irModule, sink);
checkForRecursiveFunctions(codeGenContext->getTargetReq(), irModule, sink);
+ checkForOutOfBoundAccess(irModule, sink);
if (requiredLoweringPassSet.missingReturn)
checkForMissingReturns(irModule, sink, target, false);