Skip to content

Commit 7670683

Browse files
Merge pull request #86 from lifebeyondfife/overflow
fix(bounds): protect against integer overflow
2 parents 674f8d8 + 483056c commit 7670683

3 files changed

Lines changed: 43 additions & 28 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@
33
## Tools
44
- Use MCP tools for GitHub operations (issues, PRs, etc.).
55

6+
## Coding Conventions
7+
- Always use explicit visibility identifiers (`private`, `public`, `internal`, `protected`) for all members
8+
69
## Deploy
710
- To publish to nuget, update the Version XML value in `Integer/Csp.csproj` with the appropriate SEMVER increment

Csp/Csp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PackageDescription>An Open Source .Net Constraint Programming Solver</PackageDescription>
1313
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1414
<RepositoryUrl>https://github.com/lifebeyondfife/Decider</RepositoryUrl>
15-
<Version>2.1.1</Version>
15+
<Version>2.1.2</Version>
1616
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
1717
<Deterministic>true</Deterministic>
1818
</PropertyGroup>

Csp/Integer/ExpressionInteger.cs

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,61 @@
44
This file is part of Decider.
55
*/
66
using System;
7+
using System.Linq;
78

89
using Decider.Csp.BaseTypes;
910

1011
namespace Decider.Csp.Integer
1112
{
1213
public class ExpressionInteger : Expression<int>
1314
{
15+
private static int ClampToInt(long value) =>
16+
(int) Math.Max(int.MinValue, Math.Min(int.MaxValue, value));
17+
1418
public static ExpressionInteger operator +(ExpressionInteger left, ExpressionInteger right)
1519
{
1620
return new ExpressionInteger(left, right)
1721
{
18-
evaluate = (l, r) => l.Value + r.Value,
22+
evaluate = (l, r) => ClampToInt((long)l.Value + r.Value),
1923
evaluateBounds = (l, r) =>
2024
{
2125
var leftBounds = l.GetUpdatedBounds();
2226
var rightBounds = r.GetUpdatedBounds();
2327

2428
return new Bounds<int>
2529
(
26-
(int) Math.Max(int.MinValue, Math.Min(int.MaxValue, (long)leftBounds.LowerBound + rightBounds.LowerBound)),
27-
(int) Math.Min(int.MaxValue, Math.Max(int.MinValue, (long)leftBounds.UpperBound + rightBounds.UpperBound))
30+
ClampToInt((long)leftBounds.LowerBound + rightBounds.LowerBound),
31+
ClampToInt((long)leftBounds.UpperBound + rightBounds.UpperBound)
2832
);
2933
},
3034
propagator = (first, second, enforce) =>
3135
{
3236
var result = ConstraintOperationResult.Undecided;
33-
if (first.Bounds.LowerBound < enforce.LowerBound - second.Bounds.UpperBound)
37+
var newBound = ClampToInt((long)enforce.LowerBound - second.Bounds.UpperBound);
38+
if (first.Bounds.LowerBound < newBound)
3439
{
35-
first.Bounds.LowerBound = enforce.LowerBound - second.Bounds.UpperBound;
40+
first.Bounds.LowerBound = newBound;
3641
result = ConstraintOperationResult.Propagated;
3742
}
3843

39-
if (first.Bounds.UpperBound > enforce.UpperBound - second.Bounds.LowerBound)
44+
newBound = ClampToInt((long)enforce.UpperBound - second.Bounds.LowerBound);
45+
if (first.Bounds.UpperBound > newBound)
4046
{
41-
first.Bounds.UpperBound = enforce.UpperBound - second.Bounds.LowerBound;
47+
first.Bounds.UpperBound = newBound;
4248
result = ConstraintOperationResult.Propagated;
4349
}
4450

45-
if (second.Bounds.LowerBound < enforce.LowerBound - first.Bounds.UpperBound)
51+
newBound = ClampToInt((long)enforce.LowerBound - first.Bounds.UpperBound);
52+
if (second.Bounds.LowerBound < newBound)
4653
{
47-
second.Bounds.LowerBound = enforce.LowerBound - first.Bounds.UpperBound;
54+
second.Bounds.LowerBound = newBound;
4855
result = ConstraintOperationResult.Propagated;
4956
}
5057

51-
if (second.Bounds.UpperBound > enforce.UpperBound - first.Bounds.LowerBound)
58+
newBound = ClampToInt((long)enforce.UpperBound - first.Bounds.LowerBound);
59+
if (second.Bounds.UpperBound > newBound)
5260
{
53-
second.Bounds.UpperBound = enforce.UpperBound - first.Bounds.LowerBound;
61+
second.Bounds.UpperBound = newBound;
5462
result = ConstraintOperationResult.Propagated;
5563
}
5664

@@ -66,42 +74,42 @@ public class ExpressionInteger : Expression<int>
6674
{
6775
var expression = new ExpressionInteger(left, right)
6876
{
69-
evaluate = (l, r) => l.Value - r.Value,
77+
evaluate = (l, r) => ClampToInt((long)l.Value - r.Value),
7078
evaluateBounds = (l, r) =>
7179
{
7280
var leftBounds = l.GetUpdatedBounds();
7381
var rightBounds = r.GetUpdatedBounds();
7482

7583
return new Bounds<int>
7684
(
77-
(int) Math.Max(int.MinValue, Math.Min(int.MaxValue, (long)leftBounds.LowerBound - rightBounds.UpperBound)),
78-
(int) Math.Min(int.MaxValue, Math.Max(int.MinValue, (long)leftBounds.UpperBound - rightBounds.LowerBound))
85+
ClampToInt((long)leftBounds.LowerBound - rightBounds.UpperBound),
86+
ClampToInt((long)leftBounds.UpperBound - rightBounds.LowerBound)
7987
);
8088
},
8189
propagator = (first, second, enforce) =>
8290
{
8391
var result = ConstraintOperationResult.Undecided;
8492
if (first.Bounds.LowerBound < enforce.LowerBound + second.Bounds.LowerBound)
8593
{
86-
first.Bounds.LowerBound = enforce.LowerBound + second.Bounds.LowerBound;
94+
first.Bounds.LowerBound = ClampToInt((long)enforce.LowerBound + second.Bounds.LowerBound);
8795
result = ConstraintOperationResult.Propagated;
8896
}
8997

9098
if (first.Bounds.UpperBound > enforce.UpperBound + second.Bounds.UpperBound)
9199
{
92-
first.Bounds.UpperBound = enforce.UpperBound + second.Bounds.UpperBound;
100+
first.Bounds.UpperBound = ClampToInt((long)enforce.UpperBound + second.Bounds.UpperBound);
93101
result = ConstraintOperationResult.Propagated;
94102
}
95103

96104
if (second.Bounds.LowerBound < first.Bounds.LowerBound - enforce.UpperBound)
97105
{
98-
second.Bounds.LowerBound = first.Bounds.LowerBound - enforce.UpperBound;
106+
second.Bounds.LowerBound = ClampToInt((long)first.Bounds.LowerBound - enforce.UpperBound);
99107
result = ConstraintOperationResult.Propagated;
100108
}
101109

102110
if (second.Bounds.UpperBound > first.Bounds.UpperBound - enforce.LowerBound)
103111
{
104-
second.Bounds.UpperBound = first.Bounds.UpperBound - enforce.LowerBound;
112+
second.Bounds.UpperBound = ClampToInt((long)first.Bounds.UpperBound - enforce.LowerBound);
105113
result = ConstraintOperationResult.Propagated;
106114
}
107115

@@ -134,7 +142,7 @@ public class ExpressionInteger : Expression<int>
134142
{
135143
return new ExpressionInteger(left, right)
136144
{
137-
evaluate = (l, r) => l.Value / r.Value,
145+
evaluate = (l, r) => (l.Value == int.MinValue && r.Value == -1) ? int.MaxValue : l.Value / r.Value,
138146
evaluateBounds = (l, r) =>
139147
{
140148
var leftBounds = l.GetUpdatedBounds();
@@ -151,13 +159,13 @@ public class ExpressionInteger : Expression<int>
151159
var result = ConstraintOperationResult.Undecided;
152160
if (first.Bounds.LowerBound < second.Bounds.LowerBound * enforce.LowerBound)
153161
{
154-
first.Bounds.LowerBound = second.Bounds.LowerBound * enforce.LowerBound;
162+
first.Bounds.LowerBound = ClampToInt((long)second.Bounds.LowerBound * enforce.LowerBound);
155163
result = ConstraintOperationResult.Propagated;
156164
}
157165

158166
if (first.Bounds.UpperBound > second.Bounds.UpperBound * enforce.UpperBound)
159167
{
160-
first.Bounds.UpperBound = second.Bounds.UpperBound * enforce.UpperBound;
168+
first.Bounds.UpperBound = ClampToInt((long)second.Bounds.UpperBound * enforce.UpperBound);
161169
result = ConstraintOperationResult.Propagated;
162170
}
163171

@@ -191,17 +199,21 @@ public class ExpressionInteger : Expression<int>
191199
{
192200
return new ExpressionInteger(left, right)
193201
{
194-
evaluate = (l, r) => l.Value * r.Value,
202+
evaluate = (l, r) => ClampToInt((long)l.Value * r.Value),
195203
evaluateBounds = (l, r) =>
196204
{
197205
var leftBounds = l.GetUpdatedBounds();
198206
var rightBounds = r.GetUpdatedBounds();
199207

200-
return new Bounds<int>
201-
(
202-
(leftBounds.LowerBound == 0 || rightBounds.LowerBound == 0) ? 0 : ((leftBounds.LowerBound < 0) != (rightBounds.LowerBound < 0) && Math.Abs((long)leftBounds.LowerBound) > int.MaxValue / Math.Abs((long)rightBounds.LowerBound)) ? int.MinValue : leftBounds.LowerBound * rightBounds.LowerBound,
203-
(leftBounds.UpperBound > 0 && rightBounds.UpperBound > 0 && leftBounds.UpperBound > int.MaxValue / rightBounds.UpperBound) ? int.MaxValue : leftBounds.UpperBound * rightBounds.UpperBound
204-
);
208+
var products = new[]
209+
{
210+
(long)leftBounds.LowerBound * rightBounds.LowerBound,
211+
(long)leftBounds.LowerBound * rightBounds.UpperBound,
212+
(long)leftBounds.UpperBound * rightBounds.LowerBound,
213+
(long)leftBounds.UpperBound * rightBounds.UpperBound
214+
};
215+
216+
return new Bounds<int>(ClampToInt(products.Min()), ClampToInt(products.Max()));
205217
},
206218
propagator = (first, second, enforce) =>
207219
{

0 commit comments

Comments
 (0)