Skip to content

Commit c027f3b

Browse files
authored
Merge pull request #4324 from tamasvajk/feature/unsigned-sign-analysis
Handle unsigned types in sign analysis (C# and Java)
2 parents 36450a8 + 2bbaa4e commit c027f3b

File tree

9 files changed

+122
-41
lines changed

9 files changed

+122
-41
lines changed

csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -245,24 +245,30 @@ Sign ssaDefSign(SsaVariable v) {
245245
/** Gets a possible sign for `e`. */
246246
cached
247247
Sign exprSign(Expr e) {
248-
result = certainExprSign(e)
249-
or
250-
not exists(certainExprSign(e)) and
251-
(
252-
unknownSign(e)
248+
exists(Sign s |
249+
s = certainExprSign(e)
253250
or
254-
exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e))
255-
or
256-
e =
257-
any(VarAccess access |
258-
not exists(SsaVariable v | getARead(v) = access) and
259-
(
260-
result = fieldSign(getField(access.(FieldAccess))) or
261-
not access instanceof FieldAccess
251+
not exists(certainExprSign(e)) and
252+
(
253+
unknownSign(e)
254+
or
255+
exists(SsaVariable v | getARead(v) = e | s = ssaVariableSign(v, e))
256+
or
257+
e =
258+
any(VarAccess access |
259+
not exists(SsaVariable v | getARead(v) = access) and
260+
(
261+
s = fieldSign(getField(access.(FieldAccess))) or
262+
not access instanceof FieldAccess
263+
)
262264
)
263-
)
264-
or
265-
result = specificSubExprSign(e)
265+
or
266+
s = specificSubExprSign(e)
267+
)
268+
|
269+
if e.getType() instanceof UnsignedNumericType and s = TNeg()
270+
then result = TPos()
271+
else result = s
266272
)
267273
}
268274

csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,29 @@ private module Impl {
7171

7272
predicate positiveExpression(Expr e) { e instanceof SizeofExpr }
7373

74-
class NumericOrCharType extends Type {
75-
NumericOrCharType() {
76-
this instanceof CharType or
77-
this instanceof IntegralType or
78-
this instanceof FloatingPointType or
79-
this instanceof DecimalType or
80-
this instanceof Enum or
81-
this instanceof PointerType // should be similar to unsigned integers
74+
abstract class NumericOrCharType extends Type { }
75+
76+
class UnsignedNumericType extends NumericOrCharType {
77+
UnsignedNumericType() {
78+
this instanceof CharType
79+
or
80+
this instanceof UnsignedIntegralType
81+
or
82+
this instanceof PointerType
83+
or
84+
this instanceof Enum and this.(Enum).getUnderlyingType() instanceof UnsignedIntegralType
85+
}
86+
}
87+
88+
class SignedNumericType extends NumericOrCharType {
89+
SignedNumericType() {
90+
this instanceof SignedIntegralType
91+
or
92+
this instanceof FloatingPointType
93+
or
94+
this instanceof DecimalType
95+
or
96+
this instanceof Enum and this.(Enum).getUnderlyingType() instanceof SignedIntegralType
8297
}
8398
}
8499

@@ -125,6 +140,7 @@ private module Impl {
125140
e.getType() instanceof NumericOrCharType and
126141
not e = getARead(_) and
127142
not e instanceof FieldAccess and
143+
not e instanceof TypeAccess and
128144
// The expression types that are listed here are the ones handled in `specificSubExprSign`.
129145
// Keep them in sync.
130146
not e instanceof AssignExpr and

csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import semmle.code.csharp.dataflow.SignAnalysis
44
from Expr e
55
where
66
not exists(exprSign(e)) and
7+
not e instanceof TypeAccess and
78
(
89
e.getType() instanceof CharType or
910
e.getType() instanceof IntegralType or

csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ void EnumOp(MyEnum x, MyEnum y)
437437

438438
unsafe void PointerCast(byte* src, byte* dst)
439439
{
440-
var x = (int)(src-dst);
440+
var x = (int)(src - dst);
441441
if (x < 0)
442442
{
443443
System.Console.WriteLine(x); // strictly negative
@@ -450,6 +450,20 @@ unsafe void PointerCast(byte* src, byte* dst)
450450
System.Console.WriteLine((int)to);
451451
}
452452
}
453+
454+
uint Unsigned() { return 1; }
455+
void UnsignedCheck(int i)
456+
{
457+
long l = Unsigned();
458+
if (l != 0)
459+
{
460+
System.Console.WriteLine(l); // strictly positive
461+
}
462+
463+
uint x = (uint)i;
464+
x++;
465+
System.Console.WriteLine(x); // strictly positive
466+
}
453467
}
454468

455469
// semmle-extractor-options: /r:System.Linq.dll

csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
| SignAnalysis.cs:108:13:108:21 | Decimal de = ... | strictlyPositive |
9191
| SignAnalysis.cs:108:18:108:21 | 4.2 | strictlyPositive |
9292
| SignAnalysis.cs:109:34:109:35 | access to local variable de | strictlyPositive |
93+
| SignAnalysis.cs:110:13:110:13 | access to local variable c | positive |
9394
| SignAnalysis.cs:110:13:110:19 | Char c = ... | strictlyPositive |
9495
| SignAnalysis.cs:110:17:110:19 | a | strictlyPositive |
9596
| SignAnalysis.cs:111:34:111:34 | access to local variable c | strictlyPositive |
@@ -162,6 +163,8 @@
162163
| SignAnalysis.cs:306:21:306:22 | -... | strictlyNegative |
163164
| SignAnalysis.cs:306:22:306:22 | 1 | strictlyPositive |
164165
| SignAnalysis.cs:309:38:309:38 | access to local variable x | strictlyNegative |
166+
| SignAnalysis.cs:315:13:315:15 | access to local variable min | positive |
167+
| SignAnalysis.cs:316:13:316:15 | access to local variable max | positive |
165168
| SignAnalysis.cs:316:13:316:31 | Char max = ... | strictlyPositive |
166169
| SignAnalysis.cs:316:19:316:31 | access to constant MaxValue | strictlyPositive |
167170
| SignAnalysis.cs:317:13:317:23 | Int32 c = ... | strictlyPositive |
@@ -193,7 +196,13 @@
193196
| SignAnalysis.cs:342:38:342:38 | access to local variable x | negative |
194197
| SignAnalysis.cs:348:62:348:62 | 5 | strictlyPositive |
195198
| SignAnalysis.cs:351:38:351:38 | access to local variable i | strictlyNegative |
199+
| SignAnalysis.cs:357:13:357:13 | access to parameter i | positive |
200+
| SignAnalysis.cs:359:38:359:38 | access to parameter i | strictlyPositive |
196201
| SignAnalysis.cs:371:38:371:38 | access to local variable y | strictlyNegative |
202+
| SignAnalysis.cs:377:16:377:17 | access to local variable dp | positive |
203+
| SignAnalysis.cs:377:16:377:22 | Single* dp = ... | positive |
204+
| SignAnalysis.cs:377:21:377:22 | &... | positive |
205+
| SignAnalysis.cs:378:18:378:19 | access to local variable dp | positive |
197206
| SignAnalysis.cs:381:38:381:38 | access to local variable x | strictlyNegative |
198207
| SignAnalysis.cs:385:50:385:99 | access to constant Explicit | strictlyPositive |
199208
| SignAnalysis.cs:385:109:385:110 | 15 | strictlyPositive |
@@ -215,5 +224,25 @@
215224
| SignAnalysis.cs:428:19:428:24 | ... = ... | strictlyPositive |
216225
| SignAnalysis.cs:428:23:428:24 | 12 | strictlyPositive |
217226
| SignAnalysis.cs:434:38:434:38 | access to local variable i | strictlyNegative |
227+
| SignAnalysis.cs:440:23:440:25 | access to parameter src | positive |
228+
| SignAnalysis.cs:440:29:440:31 | access to parameter dst | positive |
218229
| SignAnalysis.cs:443:38:443:38 | access to local variable x | strictlyNegative |
219230
| SignAnalysis.cs:446:31:446:32 | 10 | strictlyPositive |
231+
| SignAnalysis.cs:448:22:448:23 | access to local variable to | positive |
232+
| SignAnalysis.cs:448:22:448:29 | Byte* to = ... | positive |
233+
| SignAnalysis.cs:448:27:448:29 | (...) ... | positive |
234+
| SignAnalysis.cs:450:38:450:44 | (...) ... | positive |
235+
| SignAnalysis.cs:450:43:450:44 | access to local variable to | positive |
236+
| SignAnalysis.cs:454:30:454:30 | 1 | strictlyPositive |
237+
| SignAnalysis.cs:454:30:454:30 | (...) ... | strictlyPositive |
238+
| SignAnalysis.cs:457:14:457:27 | Int64 l = ... | positive |
239+
| SignAnalysis.cs:457:18:457:27 | (...) ... | positive |
240+
| SignAnalysis.cs:457:18:457:27 | call to method Unsigned | positive |
241+
| SignAnalysis.cs:458:13:458:13 | access to local variable l | positive |
242+
| SignAnalysis.cs:460:38:460:38 | access to local variable l | strictlyPositive |
243+
| SignAnalysis.cs:463:14:463:14 | access to local variable x | positive |
244+
| SignAnalysis.cs:463:14:463:24 | UInt32 x = ... | positive |
245+
| SignAnalysis.cs:463:18:463:24 | (...) ... | positive |
246+
| SignAnalysis.cs:464:9:464:9 | access to local variable x | positive |
247+
| SignAnalysis.cs:464:9:464:11 | ...++ | positive |
248+
| SignAnalysis.cs:465:34:465:34 | access to local variable x | strictlyPositive |

java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -245,24 +245,30 @@ Sign ssaDefSign(SsaVariable v) {
245245
/** Gets a possible sign for `e`. */
246246
cached
247247
Sign exprSign(Expr e) {
248-
result = certainExprSign(e)
249-
or
250-
not exists(certainExprSign(e)) and
251-
(
252-
unknownSign(e)
248+
exists(Sign s |
249+
s = certainExprSign(e)
253250
or
254-
exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e))
255-
or
256-
e =
257-
any(VarAccess access |
258-
not exists(SsaVariable v | getARead(v) = access) and
259-
(
260-
result = fieldSign(getField(access.(FieldAccess))) or
261-
not access instanceof FieldAccess
251+
not exists(certainExprSign(e)) and
252+
(
253+
unknownSign(e)
254+
or
255+
exists(SsaVariable v | getARead(v) = e | s = ssaVariableSign(v, e))
256+
or
257+
e =
258+
any(VarAccess access |
259+
not exists(SsaVariable v | getARead(v) = access) and
260+
(
261+
s = fieldSign(getField(access.(FieldAccess))) or
262+
not access instanceof FieldAccess
263+
)
262264
)
263-
)
264-
or
265-
result = specificSubExprSign(e)
265+
or
266+
s = specificSubExprSign(e)
267+
)
268+
|
269+
if e.getType() instanceof UnsignedNumericType and s = TNeg()
270+
then result = TPos()
271+
else result = s
266272
)
267273
}
268274

java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ private module Impl {
5353
private import SignAnalysisCommon
5454
private import SsaReadPositionCommon
5555

56+
class UnsignedNumericType = CharacterType;
57+
5658
float getNonIntegerValue(Expr e) {
5759
result = e.(LongLiteral).getValue().toFloat() or
5860
result = e.(FloatingPointLiteral).getValue().toFloat() or

java/ql/test/library-tests/dataflow/sign-analysis/A.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,9 @@ int f1(int x, int y) {
99

1010
return 0;
1111
}
12+
13+
void unsigned(int x) {
14+
char c = (char)x;
15+
System.out.println(c); // positive
16+
}
1217
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
| A.java:4:14:4:14 | x | strictlyNegative |
22
| A.java:6:9:6:9 | x | positive |
33
| A.java:7:14:7:14 | y | strictlyPositive |
4+
| A.java:14:14:14:20 | (...)... | positive |
5+
| A.java:15:24:15:24 | c | positive |

0 commit comments

Comments
 (0)