Skip to content

Commit

Permalink
Prevent stack overflow in use-stable-resource-identifiers linter (#10001
Browse files Browse the repository at this point in the history
)

* Bail walking symbol values when a cycle is detected

* Update cyclic symbol checker not to emit cyclic error message on references that don't participate in the cycle itself.
  • Loading branch information
jeskew authored Mar 6, 2023
1 parent 87663da commit 3ba6e06
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 11 deletions.
25 changes: 22 additions & 3 deletions src/Bicep.Core.IntegrationTests/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Cryptography;
using Bicep.Core.CodeAction;
using Bicep.Core.Diagnostics;
using Bicep.Core.Resources;
using Bicep.Core.TypeSystem;
Expand Down Expand Up @@ -1894,7 +1893,7 @@ public void Test_Issue2494()
{
("BCP080", DiagnosticLevel.Error, "The expression is involved in a cycle (\"nameCopy\" -> \"name\")."),
("BCP080", DiagnosticLevel.Error, "The expression is involved in a cycle (\"name\" -> \"nameCopy\")."),
("BCP080", DiagnosticLevel.Error, "The expression is involved in a cycle (\"name\" -> \"nameCopy\").")
("BCP062", DiagnosticLevel.Error, "The referenced declaration with name \"name\" is not valid.")
});
}

Expand Down Expand Up @@ -4346,8 +4345,28 @@ param CertificateSubjects certificateMapping[]

result.Should().GenerateATemplate();

var evaluated = TemplateEvaluator.Evaluate(result.Template, parameters);
var evaluated = TemplateEvaluator.Evaluate(result.Template, parameters);
evaluated.Should().HaveValueAtPath("$.outputs['vaultId'].value", "/subscriptions/mySub/resourceGroups/myRg/providers/Microsoft.KeyVault/vaults/myKv");
}

// https://github.com/Azure/bicep/issues/9978
[TestMethod]
public void Test_Issue9978()
{
var result = CompilationHelper.Compile(@"
param foo string = guid(foo)
#disable-next-line no-unused-existing-resources
resource asdf 'Microsoft.Storage/storageAccounts@2022-09-01' existing = {
name: foo
}
");

result.Should().HaveDiagnostics(new[]
{
("BCP079", DiagnosticLevel.Error, "This expression is referencing its own declaration, which is not allowed."),
("BCP062", DiagnosticLevel.Error, "The referenced declaration with name \"foo\" is not valid."),
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ var two = one.f
// #completionTest(17) -> empty
var twotwo = one.
//@[04:10) [no-unused-vars (Warning)] Variable "twotwo" is declared but never used. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-unused-vars)) |twotwo|
//@[13:16) [BCP080 (Error)] The expression is involved in a cycle ("one" -> "two"). (CodeDescription: none) |one|
//@[13:16) [BCP062 (Error)] The referenced declaration with name "one" is not valid. (CodeDescription: none) |one|
//@[17:17) [BCP020 (Error)] Expected a function or property name at this location. (CodeDescription: none) ||

// resource completion cycles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ param idValue1 string
"[2:26] The expression is involved in a cycle (\"v3\" -> \"v2\" -> \"v1\").",
"[3:26] The expression is involved in a cycle (\"v1\" -> \"v3\" -> \"v2\").",
"[4:26] The expression is involved in a cycle (\"v2\" -> \"v1\" -> \"v3\").",
"[9:25] The expression is involved in a cycle (\"v3\" -> \"v2\" -> \"v1\").",
"[9:25] The referenced declaration with name \"v3\" is not valid.",
},
DisplayName = "resolved variable cycle should not hang")]
[DataRow(@"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
{
switch (model.GetSymbolInfo(syntax))
{
case Symbol symbol when pathSegments.Contains(symbol):
// Symbol cycles are reported on elsewhere. As far as this visitor is concerned, a cycle does not introduce nondeterminism.
break;
case ParameterSymbol @parameter:
if (@parameter.DeclaringParameter.Modifier is ParameterDefaultValueSyntax defaultValueSyntax)
{
Expand All @@ -83,12 +86,6 @@ public override void VisitVariableAccessSyntax(VariableAccessSyntax syntax)
}
break;
case VariableSymbol @variable:
// Variable cycles are reported on elsewhere. As far as this visitor is concerned, a cycle does not introduce nondeterminism.
if (pathSegments.Contains(@variable))
{
return;
}

pathSegments.AddLast(@variable);
@variable.DeclaringVariable.Value.Accept(this);
pathSegments.RemoveLast();
Expand Down
7 changes: 7 additions & 0 deletions src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ private void AssignTypeWithDiagnostics(SyntaxBase syntax, Func<IDiagnosticWriter
if (this.binder.TryGetCycle(declaredSymbol) is { } cycle)
{
// there's a cycle. stop visiting now or we never will!

if (!cycle.Any(symbol => this.binder.IsDescendant(syntax, symbol.DeclaringSyntax)))
{
// the supplied syntax is not part of the cycle, just a reference to the cyclic symbol.
return ErrorType.Create(DiagnosticBuilder.ForPosition(syntax).ReferencedSymbolHasErrors(declaredSymbol.Name));
}

if (cycle.Length == 1)
{
return ErrorType.Create(DiagnosticBuilder.ForPosition(syntax).CyclicExpressionSelfReference());
Expand Down

0 comments on commit 3ba6e06

Please sign in to comment.