diff options
| author | Copilot <198982749+Copilot@users.noreply.github.com> | 2025-07-17 20:58:02 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-18 03:58:02 +0000 |
| commit | 85edfb178cd243134f4bb3d35ad71f154d76c81c (patch) | |
| tree | 68f5340c8796f6c2d5ff343cabf3d96c7d387ac5 /source/slang | |
| parent | 447d73f8c2245d061b0e84890fb994a77816a736 (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.cpp | 89 | ||||
| -rw-r--r-- | source/slang/slang-check-out-of-bound-access.h | 12 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 2 |
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); |
