Semmle 1.22
Skip to end of metadata
Go to start of metadata

Name: Unsafe array for days of the year

Description: An array of 365 items typically indicates one entry per day of the year, but without considering leap years, which would be 366 days. An access on a leap year could result in buffer overflow bugs.

ID: cpp/leap-year/unsafe-array-for-days-of-the-year

Kind: problem

Severity: warning

Precision: medium

Query: UnsafeArrayForDaysOfYear.ql
/**
 * @name Unsafe array for days of the year
 * @description An array of 365 items typically indicates one entry per day of the year, but without considering leap years, which would be 366 days.
 *              An access on a leap year could result in buffer overflow bugs.
 * @kind problem
 * @problem.severity warning
 * @id cpp/leap-year/unsafe-array-for-days-of-the-year
 * @precision medium
 * @tags security
 *       leap-year
 */

import cpp

class LeapYearUnsafeDaysOfTheYearArrayType extends ArrayType {
  LeapYearUnsafeDaysOfTheYearArrayType() { this.getArraySize() = 365 }
}

from Element element, string allocType
where
  exists(NewArrayExpr nae |
    element = nae and
    nae.getAllocatedType() instanceof LeapYearUnsafeDaysOfTheYearArrayType and
    allocType = "an array allocation"
  )
  or
  exists(Variable var |
    var = element and
    var.getType() instanceof LeapYearUnsafeDaysOfTheYearArrayType and
    allocType = "an array allocation"
  )
  or
  exists(ConstructorCall cc |
    element = cc and
    cc.getTarget().hasName("vector") and
    cc.getArgument(0).getValue().toInt() = 365 and
    allocType = "a std::vector allocation"
  )
select element,
  "There is " + allocType +
    " with a hard-coded set of 365 elements, which may indicate the number of days in a year without considering leap year scenarios."

The leap year rule for the Gregorian calendar, which has become the internationally accepted civil calendar, is: every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400.

A leap year bug occurs when software (in any language) is written without consideration of leap year logic, or with flawed logic to calculate leap years; which typically results in incorrect results.

The impact of these bugs may range from almost unnoticeable bugs such as an incorrect date, to severe bugs that affect reliability, availability or even the security of the affected system.

This query helps to detect when a developer allocates an array or other fixed-length data structure such as std::vector with 365 elements – one for each day of the year.

Since leap years have 366 days, there will be no allocated element on December 31st at the end of a leap year; which will lead to a buffer overflow on a leap year.

Recommendation

When allocating memory for storing elements for each day of the year, ensure that the correct number of elements are allocated.

It is also highly recommended to compile the code with array-bounds checking enabled whenever possible.

Example

In this example, we are allocating 365 integers, one for each day of the year. This code will fail on a leap year, when there are 366 days.

int items[365];
items[dayOfYear - 1] = x; // buffer overflow on December 31st of any leap year 

When using arrays, allocate the correct number of elements to match the year.

bool isLeapYear = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
int *items = new int[isLeapYear ? 366 : 365];
// ...
delete[] items;

References