|
6 | 6 | #include "app_utils.hpp" |
7 | 7 | #include "config.h" |
8 | 8 | #include "easyaccess.hpp" |
| 9 | +#include "enforce.hpp" |
9 | 10 | #include "exif.hpp" |
10 | 11 | #include "futils.hpp" |
11 | 12 | #include "i18n.h" // NLS support. |
12 | 13 | #include "image.hpp" |
13 | 14 | #include "iptc.hpp" |
14 | 15 | #include "preview.hpp" |
| 16 | +#include "safe_op.hpp" |
15 | 17 | #include "types.hpp" |
16 | 18 | #include "xmp_exiv2.hpp" |
17 | 19 |
|
@@ -1407,19 +1409,47 @@ int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, co |
1407 | 1409 | std::cerr << path << ": " << _("Failed to parse timestamp") << " `" << timeStr << "'\n"; |
1408 | 1410 | return 1; |
1409 | 1411 | } |
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)); |
1413 | 1443 | // Let's not create files with non-4-digit years, we can't read them. |
1414 | 1444 | if (tm.tm_year > 9999 - 1900 || tm.tm_year < 1000 - 1900) { |
1415 | 1445 | if (Params::instance().verbose_) |
1416 | 1446 | 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 << " " |
1418 | 1448 | << _("years") << "\n"; |
1419 | 1449 | return 1; |
1420 | 1450 | } |
1421 | 1451 | time_t time = mktime(&tm); |
1422 | | - time += adjustment_ + dayAdjustment_ * 86400; |
| 1452 | + time = Safe::add(time, Safe::add(adjustment, dayAdjustment * secondsInDay)); |
1423 | 1453 | timeStr = time2Str(time); |
1424 | 1454 | if (Params::instance().verbose_) { |
1425 | 1455 | std::cout << " " << _("to") << " " << timeStr << std::endl; |
@@ -1590,22 +1620,28 @@ int str2Tm(const std::string& timeStr, struct tm* tm) { |
1590 | 1620 | long tmp = 0; |
1591 | 1621 | if (!Util::strtol(timeStr.substr(0, 4).c_str(), tmp)) |
1592 | 1622 | 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); |
1594 | 1625 | if (!Util::strtol(timeStr.substr(5, 2).c_str(), tmp)) |
1595 | 1626 | 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); |
1597 | 1629 | if (!Util::strtol(timeStr.substr(8, 2).c_str(), tmp)) |
1598 | 1630 | 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); |
1600 | 1633 | if (!Util::strtol(timeStr.substr(11, 2).c_str(), tmp)) |
1601 | 1634 | 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); |
1603 | 1637 | if (!Util::strtol(timeStr.substr(14, 2).c_str(), tmp)) |
1604 | 1638 | 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); |
1606 | 1641 | if (!Util::strtol(timeStr.substr(17, 2).c_str(), tmp)) |
1607 | 1642 | 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); |
1609 | 1645 |
|
1610 | 1646 | // Conversions to set remaining fields of the tm structure |
1611 | 1647 | if (mktime(tm) == static_cast<time_t>(-1)) |
|
0 commit comments