Skip to content

Commit f62b9b4

Browse files
damyanpCopilotCopilot
authored
[Validator] Add validation for createHandleFromBinding (#8205)
Previously, the validator didn't validate the parameters to createHandleFromBinding. This change adds validation that validates: * Bind parameter is constant — validates the bind struct parameter is a constant * Resource class is valid — validates resourceClass is one of CBuffer, Sampler, SRV, or UAV * Index range — validates constant indices fall within [rangeLowerBound, rangeUpperBound] Fixes #7019 --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: damyanp <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 054e0f5 commit f62b9b4

5 files changed

Lines changed: 208 additions & 0 deletions

File tree

docs/ReleaseNotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ line upon naming the release. Refer to previous for appropriate section names.
4141

4242
- Fixed non-deterministic DXIL/PDB output when compiling shaders with resource arrays, debug info, and SM 6.6+. [#8171](https://github.com/microsoft/DirectXShaderCompiler/issues/8171)
4343
- Fixed mesh shader semantics that were incorrectly case sensitive.
44+
- DXIL validation: added validation for `CreateHandleFromBinding`.
4445
- DXIL validation now rejects non-standard integer bit widths (e.g. `i25`) in instructions.
4546

4647
#### Other Changes

lib/DxilValidation/DxilValidationUtils.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "dxc/DXIL/DxilInstructions.h"
1616
#include "dxc/DXIL/DxilModule.h"
1717
#include "dxc/DXIL/DxilOperations.h"
18+
#include "dxc/DXIL/DxilResourceBinding.h"
1819
#include "dxc/DXIL/DxilUtil.h"
1920
#include "dxc/Support/Global.h"
2021

@@ -229,6 +230,52 @@ void ValidationContext::BuildResMap() {
229230
}
230231
const ShaderModel &SM = *DxilMod.GetShaderModel();
231232

233+
// Scan all createHandleFromBinding for validation.
234+
for (auto &it :
235+
hlslOP->GetOpFuncList(DXIL::OpCode::CreateHandleFromBinding)) {
236+
Function *F = it.second;
237+
if (!F)
238+
continue;
239+
for (User *U : F->users()) {
240+
CallInst *CI = cast<CallInst>(U);
241+
DxilInst_CreateHandleFromBinding Hdl(CI);
242+
243+
// Validate bind parameter is constant.
244+
Value *Bind = Hdl.get_bind();
245+
if (!isa<Constant>(Bind)) {
246+
EmitInstrError(CI, ValidationRule::InstrOpConstRange);
247+
continue;
248+
}
249+
250+
DxilResourceBinding B =
251+
resource_helper::loadBindingFromCreateHandleFromBinding(
252+
Hdl, hlslOP->GetHandleType(), SM);
253+
254+
// Validate resourceClass is valid.
255+
switch (static_cast<DXIL::ResourceClass>(B.resourceClass)) {
256+
case DXIL::ResourceClass::CBuffer:
257+
case DXIL::ResourceClass::Sampler:
258+
case DXIL::ResourceClass::SRV:
259+
case DXIL::ResourceClass::UAV:
260+
break;
261+
default:
262+
EmitInstrError(CI, ValidationRule::InstrOpConstRange);
263+
continue;
264+
}
265+
266+
// Validate constant index is within binding range.
267+
ConstantInt *CIndex = dyn_cast<ConstantInt>(Hdl.get_index());
268+
if (CIndex) {
269+
unsigned Index = CIndex->getLimitedValue();
270+
if (Index < B.rangeLowerBound || Index > B.rangeUpperBound) {
271+
// Index out of range.
272+
EmitInstrError(CI, ValidationRule::InstrOpConstRange);
273+
continue;
274+
}
275+
}
276+
}
277+
}
278+
232279
for (auto &it : hlslOP->GetOpFuncList(DXIL::OpCode::AnnotateHandle)) {
233280
Function *F = it.second;
234281
if (!F)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
; REQUIRES: dxil-1-8
2+
; RUN: not %dxv %s 2>&1 | FileCheck %s
3+
4+
; Verify that createHandleFromBinding rejects out-of-range indices
5+
; and invalid resource classes.
6+
7+
target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
8+
target triple = "dxil-ms-dx"
9+
10+
%dx.types.Handle = type { i8* }
11+
%dx.types.ResBind = type { i32, i32, i32, i8 }
12+
%dx.types.ResourceProperties = type { i32, i32 }
13+
%struct.RWByteAddressBuffer = type { i32 }
14+
15+
; --- Index below lower bound ---
16+
; CHECK-DAG: error: Constant values must be in-range for operation.
17+
; CHECK-DAG: note: at '%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 0, i1 false)' in block '#0' of function 'main'.
18+
19+
; --- Index above upper bound ---
20+
; CHECK-DAG: error: Constant values must be in-range for operation.
21+
; CHECK-DAG: note: at '%3 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 4, i1 false)' in block '#0' of function 'main'.
22+
23+
; --- Invalid resource class ---
24+
; CHECK-DAG: error: Constant values must be in-range for operation.
25+
; CHECK-DAG: note: at '%5 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 0, i32 0, i32 0, i8 5 }, i32 0, i1 false)' in block '#0' of function 'main'.
26+
27+
define void @main() {
28+
; Index 0 is below rangeLowerBound=1
29+
%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 0, i1 false)
30+
%2 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 4107, i32 0 })
31+
; Index 4 is above rangeUpperBound=3
32+
%3 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 4, i1 false)
33+
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 4107, i32 0 })
34+
; resourceClass=5 is invalid (valid: 0=SRV, 1=UAV, 2=CBuffer, 3=Sampler)
35+
%5 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 0, i32 0, i32 0, i8 5 }, i32 0, i1 false)
36+
%6 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %5, %dx.types.ResourceProperties { i32 4107, i32 0 })
37+
ret void
38+
}
39+
40+
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #0
41+
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #0
42+
43+
attributes #0 = { nounwind readnone }
44+
45+
!llvm.ident = !{!0}
46+
!dx.version = !{!1}
47+
!dx.valver = !{!1}
48+
!dx.shaderModel = !{!2}
49+
!dx.resources = !{!3}
50+
!dx.entryPoints = !{!6}
51+
52+
!0 = !{!"dxc(private) 1.8.0.15017 (main, 4e0f5364a-dirty)"}
53+
!1 = !{i32 1, i32 8}
54+
!2 = !{!"cs", i32 6, i32 8}
55+
!3 = !{null, !4, null, null}
56+
!4 = !{!5}
57+
!5 = !{i32 0, %struct.RWByteAddressBuffer* undef, !"", i32 0, i32 1, i32 3, i32 11, i1 false, i1 false, i1 false, null}
58+
!6 = !{void ()* @main, !"main", null, !3, !7}
59+
!7 = !{i32 0, i64 8589934608, i32 4, !8}
60+
!8 = !{i32 4, i32 1, i32 1}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
; REQUIRES: dxil-1-8
2+
; RUN: not %dxv %s 2>&1 | FileCheck %s
3+
4+
target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
5+
target triple = "dxil-ms-dx"
6+
7+
%dx.types.Handle = type { i8* }
8+
%dx.types.ResBind = type { i32, i32, i32, i8 }
9+
%dx.types.ResourceProperties = type { i32, i32 }
10+
%struct.RWByteAddressBuffer = type { i32 }
11+
12+
; CHECK: error: Constant values must be in-range for operation.
13+
; CHECK: note: at '%2 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind %1,
14+
15+
define void @main(i32 %lowerBound) {
16+
; Construct a non-constant bind struct using a function parameter
17+
%1 = insertvalue %dx.types.ResBind { i32 undef, i32 1, i32 0, i8 1 }, i32 %lowerBound, 0
18+
; Pass non-constant bind to createHandleFromBinding — should fail validation
19+
%2 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind %1, i32 0, i1 false) ; CreateHandleFromBinding(bind,index,nonUniformIndex)
20+
%3 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %2, %dx.types.ResourceProperties { i32 4107, i32 0 }) ; AnnotateHandle(res,props) resource: RWByteAddressBuffer
21+
ret void
22+
}
23+
24+
; Function Attrs: nounwind readnone
25+
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #0
26+
27+
; Function Attrs: nounwind readnone
28+
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #0
29+
30+
attributes #0 = { nounwind readnone }
31+
32+
!llvm.ident = !{!0}
33+
!dx.version = !{!1}
34+
!dx.valver = !{!1}
35+
!dx.shaderModel = !{!2}
36+
!dx.resources = !{!3}
37+
!dx.entryPoints = !{!6}
38+
39+
!0 = !{!"dxc(private) 1.8.0.15017 (main, 4e0f5364a-dirty)"}
40+
!1 = !{i32 1, i32 8}
41+
!2 = !{!"cs", i32 6, i32 8}
42+
!3 = !{null, !4, null, null}
43+
!4 = !{!5}
44+
!5 = !{i32 0, %struct.RWByteAddressBuffer* undef, !"", i32 0, i32 0, i32 1, i32 11, i1 false, i1 false, i1 false, null}
45+
!6 = !{void (i32)* @main, !"main", null, !3, !7}
46+
!7 = !{i32 0, i64 8589934608, i32 4, !8}
47+
!8 = !{i32 4, i32 1, i32 1}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
; REQUIRES: dxil-1-8
2+
; RUN: %dxv %s | FileCheck %s
3+
4+
; CHECK: Validation succeeded.
5+
6+
; Verify that valid createHandleFromBinding calls pass DXIL validation:
7+
; in-range constant indices, valid resource classes, and non-constant
8+
; indices on array resources.
9+
10+
target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
11+
target triple = "dxil-ms-dx"
12+
13+
%dx.types.Handle = type { i8* }
14+
%dx.types.ResBind = type { i32, i32, i32, i8 }
15+
%dx.types.ResourceProperties = type { i32, i32 }
16+
%struct.RWByteAddressBuffer = type { i32 }
17+
18+
define void @main() {
19+
; Valid: index 1 is within [1, 3], resourceClass=1 (UAV)
20+
%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 1, i1 false)
21+
%2 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 4107, i32 0 })
22+
; Valid: index 3 is within [1, 3] (upper bound)
23+
%3 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 3, i1 false)
24+
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 4107, i32 0 })
25+
; Valid: non-constant index on array resource (range [1, 3])
26+
%5 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96)
27+
%6 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 %5, i1 false)
28+
%7 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %6, %dx.types.ResourceProperties { i32 4107, i32 0 })
29+
ret void
30+
}
31+
32+
declare i32 @dx.op.flattenedThreadIdInGroup.i32(i32) #0
33+
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #0
34+
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #0
35+
36+
attributes #0 = { nounwind readnone }
37+
38+
!llvm.ident = !{!0}
39+
!dx.version = !{!1}
40+
!dx.valver = !{!1}
41+
!dx.shaderModel = !{!2}
42+
!dx.resources = !{!3}
43+
!dx.entryPoints = !{!6}
44+
45+
!0 = !{!"dxc(private) 1.8.0.15017 (main, 4e0f5364a-dirty)"}
46+
!1 = !{i32 1, i32 8}
47+
!2 = !{!"cs", i32 6, i32 8}
48+
!3 = !{null, !4, null, null}
49+
!4 = !{!5}
50+
!5 = !{i32 0, %struct.RWByteAddressBuffer* undef, !"", i32 0, i32 1, i32 3, i32 11, i1 false, i1 false, i1 false, null}
51+
!6 = !{void ()* @main, !"main", null, !3, !7}
52+
!7 = !{i32 0, i64 8589934608, i32 4, !8}
53+
!8 = !{i32 4, i32 1, i32 1}

0 commit comments

Comments
 (0)