Skip to content

Commit 2f1a850

Browse files
committed
Check if there is a guard checking for a month that isnt a february value
1 parent 9b04b42 commit 2f1a850

2 files changed

Lines changed: 93 additions & 1 deletion

File tree

cpp/ql/src/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModificationPrecise.ql

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import cpp
1313
import LeapYear
14+
import semmle.code.cpp.controlflow.IRGuards
1415

1516
/**
1617
* The set of expressions which are ignorable; either because they seem to not be part of a year mutation,
@@ -360,10 +361,42 @@ predicate isUsedInFeb29Check(YearFieldAccess fa) {
360361
)
361362
}
362363

364+
class MonthEqualityCheck extends EqualityOperation{
365+
MonthEqualityCheck(){
366+
this.getAnOperand() instanceof MonthFieldAccess
367+
}
368+
369+
Expr getExprCompared(){
370+
exists(Expr e |
371+
e = this.getAnOperand() and
372+
not e instanceof MonthFieldAccess and
373+
result = e
374+
)
375+
}
376+
377+
MonthFieldAccess getMonthFieldAccess(){
378+
result = this.getAnOperand()
379+
}
380+
}
381+
382+
class MonthEqualityCheckGuard extends GuardCondition instanceof MonthEqualityCheck{
383+
MonthEqualityCheckGuard(){ any() }
384+
}
385+
386+
bindingset[e]
387+
pragma[inline_late]
388+
predicate isControlledByMonthEqualityCheckNonFebruary(Expr e){
389+
exists(MonthEqualityCheckGuard monthGuard |
390+
monthGuard.controls(e.getBasicBlock(), true) and
391+
not monthGuard.(MonthEqualityCheck).getExprCompared().getValueText() = "2"
392+
)
393+
}
394+
363395
import OperationToYearAssignmentFlow::PathGraph
364396

365397
from OperationToYearAssignmentFlow::PathNode src, OperationToYearAssignmentFlow::PathNode sink
366398
where
367399
OperationToYearAssignmentFlow::flowPath(src, sink) and
368-
not isYearModifiedWithCheck(sink.getNode().asExpr().(YearFieldAssignment).getYearFieldAccess())
400+
not isYearModifiedWithCheck(sink.getNode().asExpr().(YearFieldAssignment).getYearFieldAccess()) and
401+
not isControlledByMonthEqualityCheckNonFebruary(sink.getNode().asExpr())
369402
select sink, src, sink, "TEST"

cpp/ql/test/query-tests/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification/test.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,30 @@ struct logtime {
7373
long usec;
7474
};
7575

76+
/*
77+
* Data structure representing a broken-down timestamp.
78+
*
79+
* CAUTION: the IANA timezone library (src/timezone/) follows the POSIX
80+
* convention that tm_mon counts from 0 and tm_year is relative to 1900.
81+
* However, Postgres' datetime functions generally treat tm_mon as counting
82+
* from 1 and tm_year as relative to 1 BC. Be sure to make the appropriate
83+
* adjustments when moving from one code domain to the other.
84+
*/
85+
struct pg_tm
86+
{
87+
int tm_sec;
88+
int tm_min;
89+
int tm_hour;
90+
int tm_mday;
91+
int tm_mon; /* see above */
92+
int tm_year; /* see above */
93+
int tm_wday;
94+
int tm_yday;
95+
int tm_isdst;
96+
long int tm_gmtoff;
97+
const char *tm_zone;
98+
};
99+
76100
BOOL
77101
SystemTimeToFileTime(
78102
const SYSTEMTIME* lpSystemTime,
@@ -1088,4 +1112,39 @@ void fn_year_set_through_out_arg(){
10881112
// GetSystemTime(&st);
10891113
// Bad, year incremented without check
10901114
increment_arg_by_pointer(&st.wYear);
1115+
}
1116+
1117+
1118+
/* void
1119+
GetEpochTime(struct pg_tm *tm)
1120+
{
1121+
struct pg_tm *t0;
1122+
pg_time_t epoch = 0;
1123+
1124+
t0 = pg_gmtime(&epoch);
1125+
1126+
tm->tm_year = t0->tm_year;
1127+
tm->tm_mon = t0->tm_mon;
1128+
tm->tm_mday = t0->tm_mday;
1129+
tm->tm_hour = t0->tm_hour;
1130+
tm->tm_min = t0->tm_min;
1131+
tm->tm_sec = t0->tm_sec;
1132+
1133+
tm->tm_year += 1900;
1134+
tm->tm_mon++;
1135+
} */
1136+
1137+
void
1138+
fp_guarded_by_month(struct pg_tm *tm){
1139+
int woy = 52;
1140+
int MONTHS_PER_YEAR = 12;
1141+
/*
1142+
* If it is week 52/53 and the month is January, then the
1143+
* week must belong to the previous year. Also, some
1144+
* December dates belong to the next year.
1145+
*/
1146+
if (woy >= 52 && tm->tm_mon == 1)
1147+
--tm->tm_year; // Negative Test Case
1148+
if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
1149+
++tm->tm_year; // Negative Test Case
10911150
}

0 commit comments

Comments
 (0)