1+ /**
2+ * @name Year field changed using an arithmetic operation without checking for leap year (AntiPattern 1)
3+ * @description A field that represents a year is being modified by an arithmetic operation, but no proper check for leap years can be detected afterwards.
4+ * @kind path-problem
5+ * @problem.severity warning
6+ * @id cpp/microsoft/public/leap-year/unchecked-after-arithmetic-year-modification-precise
7+ * @precision medium
8+ * @tags leap-year
9+ * correctness
10+ */
11+
12+ import cpp
13+ import LeapYear
14+
15+ /**
16+ * The set of expressions which are ignorable; either because they seem to not be part of a year mutation,
17+ * or because they seem to be a conversion pattern of mapping date scalars.
18+ */
19+ abstract class IgnorableOperationSource extends Expr { }
20+
21+ class IgnorableExprAssignRem extends IgnorableOperationSource instanceof AssignRemExpr { }
22+ class IgnorableExprRem extends IgnorableOperationSource instanceof RemExpr { }
23+ class IgnorableExprUnaryMinus extends IgnorableOperationSource instanceof UnaryMinusExpr { }
24+ /**
25+ * Ignore any operation that is nested inside a larger operation, assume the larger operation is the real source
26+ * */
27+ class IgnorableExprNestedExpr extends IgnorableOperationSource instanceof BinaryArithmeticOperation {
28+ IgnorableExprNestedExpr ( ) {
29+ exists ( BinaryArithmeticOperation parentOp |
30+ parentOp .getAChild + ( ) = this
31+ )
32+ }
33+ }
34+
35+ /**
36+ * Anything involving a sub expression with char literal 48, ignore as a likely string conversion
37+ */
38+ class IgnorableExpr48Mapping extends IgnorableOperationSource {
39+ IgnorableExpr48Mapping ( ) {
40+ exists ( SubExpr child | this .( Operation ) .getAChild * ( ) = child |
41+ child .getRightOperand ( ) .( Literal ) .getValue ( ) .toInt ( ) = 48
42+ or
43+ child .getRightOperand ( ) .( CharLiteral ) .getValue ( ) .toInt ( ) = 48
44+ )
45+ }
46+ }
47+
48+ /**
49+ * if doing any kind of operation involving multiplying 10, 100, 1000, etc., likely some kind of conversion
50+ * and ignorable
51+ */
52+ class IgnorableExpr10MulipleComponent extends IgnorableOperationSource {
53+ IgnorableExpr10MulipleComponent ( ) {
54+ this .( Operation ) .getAChild * ( ) .( MulExpr ) .getAnOperand ( ) .( Literal ) .getValue ( ) .toInt ( ) % 10 = 0
55+ or
56+ this .( AssignMulExpr ) .getRValue ( ) .( Literal ) .getValue ( ) .toInt ( ) % 10 = 0
57+ }
58+ }
59+
60+ /*
61+ * linux time conversions expect the year to start from 1900, so subtracting or
62+ * adding 1900 or anything involving 1900 as a generalization is probably
63+ * a conversion that is ignorable
64+ * */
65+ class IgnorableExprExpr1900Mapping extends IgnorableOperationSource instanceof Operation {
66+ IgnorableExprExpr1900Mapping ( ) {
67+ this .( Operation ) .getAnOperand ( ) .getAChild * ( ) .( Literal ) .getValue ( ) .toInt ( ) = 1900
68+ }
69+ }
70+
71+ class OperationSource extends Expr {
72+ OperationSource ( ) {
73+ (
74+ this instanceof BinaryArithmeticOperation
75+ or
76+ this instanceof UnaryArithmeticOperation
77+ or
78+ exists ( AssignArithmeticOperation a | a .getRValue ( ) = this )
79+ ) and
80+ not this instanceof IgnorableOperationSource
81+ }
82+ }
83+
84+ /**
85+ * An assignment to a year field, which will be a sink
86+ */
87+ abstract class YearFieldAssignment extends Expr { }
88+
89+ /**
90+ * An assignment to x ie `x = a`.
91+ */
92+ class YearFieldAssignmentAccess extends YearFieldAssignment {
93+ YearFieldAssignmentAccess ( ) {
94+ // TODO: why can't I make this just the L value? Doesn't seem to work
95+ exists ( Assignment a |
96+ a .getLValue ( ) instanceof YearFieldAccess and
97+ a .getRValue ( ) = this
98+ )
99+ }
100+ }
101+
102+ /**
103+ *
104+ */
105+ class YearFieldAssignmentUnary extends YearFieldAssignment instanceof UnaryArithmeticOperation {
106+ YearFieldAssignmentUnary ( ) {
107+ this .getOperand ( ) instanceof YearFieldAccess and
108+ }
109+ }
110+
111+ /**
112+ * A DataFlow configuration for identifying flows from some non trivial access or literal
113+ * to the Year field of a date object.
114+ */
115+ module OperationToYearAssignmentConfig implements DataFlow:: ConfigSig {
116+ predicate isSource ( DataFlow:: Node n ) { n .asExpr ( ) instanceof OperationSource }
117+
118+ predicate isSink ( DataFlow:: Node n ) { n .asExpr ( ) instanceof YearFieldAssignment }
119+
120+ predicate isBarrier ( DataFlow:: Node n ) {
121+ exists ( ArrayExpr arr | arr .getArrayOffset ( ) = n .asExpr ( ) )
122+ or
123+ n .asExpr ( ) .getUnspecifiedType ( ) instanceof PointerType
124+ or
125+ n .asExpr ( ) instanceof IgnorableOperationSource
126+ or
127+ exists ( ChecksForLeapYearFunctionCall fc | fc .getAnArgument ( ) = n .asExpr ( ) )
128+ }
129+ }
130+
131+ module OperationToYearAssignmentFlow = TaintTracking:: Global< OperationToYearAssignmentConfig > ;
132+ import OperationToYearAssignmentFlow:: PathGraph
133+
134+ from OperationToYearAssignmentFlow:: PathNode src , OperationToYearAssignmentFlow:: PathNode sink
135+ where OperationToYearAssignmentFlow:: flowPath ( src , sink )
136+ select sink , src , sink , "TEST"
0 commit comments