Skip to content

Commit ddbae1e

Browse files
Merge pull request #2244 from kevinbackhouse/adjustDateTime
More bounds checking in Adjust::adjustDateTime
2 parents 6e56ec1 + 2e0ab1a commit ddbae1e

File tree

1 file changed

+47
-11
lines changed

1 file changed

+47
-11
lines changed

app/actions.cpp

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
#include "app_utils.hpp"
77
#include "config.h"
88
#include "easyaccess.hpp"
9+
#include "enforce.hpp"
910
#include "exif.hpp"
1011
#include "futils.hpp"
1112
#include "i18n.h" // NLS support.
1213
#include "image.hpp"
1314
#include "iptc.hpp"
1415
#include "preview.hpp"
16+
#include "safe_op.hpp"
1517
#include "types.hpp"
1618
#include "xmp_exiv2.hpp"
1719

@@ -1407,19 +1409,47 @@ int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, co
14071409
std::cerr << path << ": " << _("Failed to parse timestamp") << " `" << timeStr << "'\n";
14081410
return 1;
14091411
}
1410-
const long monOverflow = (tm.tm_mon + monthAdjustment_) / 12;
1411-
tm.tm_mon = (tm.tm_mon + monthAdjustment_) % 12;
1412-
tm.tm_year += yearAdjustment_ + monOverflow;
1412+
1413+
// bounds checking for yearAdjustment_
1414+
enforce<std::overflow_error>(yearAdjustment_ >= std::numeric_limits<decltype(tm.tm_year)>::min(),
1415+
"year adjustment too low");
1416+
enforce<std::overflow_error>(yearAdjustment_ <= std::numeric_limits<decltype(tm.tm_year)>::max(),
1417+
"year adjustment too high");
1418+
const auto yearAdjustment = static_cast<decltype(tm.tm_year)>(yearAdjustment_);
1419+
1420+
// bounds checking for monthAdjustment_
1421+
enforce<std::overflow_error>(monthAdjustment_ >= std::numeric_limits<decltype(tm.tm_mon)>::min(),
1422+
"month adjustment too low");
1423+
enforce<std::overflow_error>(monthAdjustment_ <= std::numeric_limits<decltype(tm.tm_mon)>::max(),
1424+
"month adjustment too high");
1425+
const auto monthAdjustment = static_cast<decltype(tm.tm_mon)>(monthAdjustment_);
1426+
1427+
// bounds checking for dayAdjustment_
1428+
static constexpr time_t secondsInDay = 24 * 60 * 60;
1429+
enforce<std::overflow_error>(dayAdjustment_ >= std::numeric_limits<time_t>::min() / secondsInDay,
1430+
"day adjustment too low");
1431+
enforce<std::overflow_error>(dayAdjustment_ <= std::numeric_limits<time_t>::max() / secondsInDay,
1432+
"day adjustment too high");
1433+
const auto dayAdjustment = static_cast<time_t>(dayAdjustment_);
1434+
1435+
// bounds checking for adjustment_
1436+
enforce<std::overflow_error>(adjustment_ >= std::numeric_limits<time_t>::min(), "seconds adjustment too low");
1437+
enforce<std::overflow_error>(adjustment_ <= std::numeric_limits<time_t>::max(), "seconds adjustment too high");
1438+
const auto adjustment = static_cast<time_t>(adjustment_);
1439+
1440+
const auto monOverflow = Safe::add(tm.tm_mon, monthAdjustment) / 12;
1441+
tm.tm_mon = Safe::add(tm.tm_mon, monthAdjustment) % 12;
1442+
tm.tm_year = Safe::add(tm.tm_year, Safe::add(yearAdjustment, monOverflow));
14131443
// Let's not create files with non-4-digit years, we can't read them.
14141444
if (tm.tm_year > 9999 - 1900 || tm.tm_year < 1000 - 1900) {
14151445
if (Params::instance().verbose_)
14161446
std::cout << std::endl;
1417-
std::cerr << path << ": " << _("Can't adjust timestamp by") << " " << yearAdjustment_ + monOverflow << " "
1447+
std::cerr << path << ": " << _("Can't adjust timestamp by") << " " << yearAdjustment + monOverflow << " "
14181448
<< _("years") << "\n";
14191449
return 1;
14201450
}
14211451
time_t time = mktime(&tm);
1422-
time += adjustment_ + dayAdjustment_ * 86400;
1452+
time = Safe::add(time, Safe::add(adjustment, dayAdjustment * secondsInDay));
14231453
timeStr = time2Str(time);
14241454
if (Params::instance().verbose_) {
14251455
std::cout << " " << _("to") << " " << timeStr << std::endl;
@@ -1590,22 +1620,28 @@ int str2Tm(const std::string& timeStr, struct tm* tm) {
15901620
long tmp = 0;
15911621
if (!Util::strtol(timeStr.substr(0, 4).c_str(), tmp))
15921622
return 5;
1593-
tm->tm_year = tmp - 1900;
1623+
// tmp is a 4-digit number so this cast cannot overflow
1624+
tm->tm_year = static_cast<decltype(tm->tm_year)>(tmp - 1900);
15941625
if (!Util::strtol(timeStr.substr(5, 2).c_str(), tmp))
15951626
return 6;
1596-
tm->tm_mon = tmp - 1;
1627+
// tmp is a 2-digit number so this cast cannot overflow
1628+
tm->tm_mon = static_cast<decltype(tm->tm_mon)>(tmp - 1);
15971629
if (!Util::strtol(timeStr.substr(8, 2).c_str(), tmp))
15981630
return 7;
1599-
tm->tm_mday = tmp;
1631+
// tmp is a 2-digit number so this cast cannot overflow
1632+
tm->tm_mday = static_cast<decltype(tm->tm_mday)>(tmp);
16001633
if (!Util::strtol(timeStr.substr(11, 2).c_str(), tmp))
16011634
return 8;
1602-
tm->tm_hour = tmp;
1635+
// tmp is a 2-digit number so this cast cannot overflow
1636+
tm->tm_hour = static_cast<decltype(tm->tm_hour)>(tmp);
16031637
if (!Util::strtol(timeStr.substr(14, 2).c_str(), tmp))
16041638
return 9;
1605-
tm->tm_min = tmp;
1639+
// tmp is a 2-digit number so this cast cannot overflow
1640+
tm->tm_min = static_cast<decltype(tm->tm_min)>(tmp);
16061641
if (!Util::strtol(timeStr.substr(17, 2).c_str(), tmp))
16071642
return 10;
1608-
tm->tm_sec = tmp;
1643+
// tmp is a 2-digit number so this cast cannot overflow
1644+
tm->tm_sec = static_cast<decltype(tm->tm_sec)>(tmp);
16091645

16101646
// Conversions to set remaining fields of the tm structure
16111647
if (mktime(tm) == static_cast<time_t>(-1))

0 commit comments

Comments
 (0)