Buffer overflow when attempting to `strcpy` a c_str into a smart pointer possibly due to huge size of c_str

Viewed 54

I'm getting a buffer overflow in this code, specifically the strcpy line, when I compile a release version of my program using G++.

Is there a better way to do what I'm trying to do here?

int main()
{
    /*
     * Calculates the date from a week number.
     * @param week_num: the week number to calculate the date for.
     * @return an integer denoting date, in YYYYMMDD format.
     */

    struct tm tm{};
    std::string week_num {"2020-47-1"};  // This value is what's being passed into the function when it crashes
    const std::string week_num_tmp {week_num + "-1"};
    volatile size_t test {std::strlen(week_num_tmp.c_str())};  //debugging variable - 93824994280448
    auto week_char {std::make_unique<char>(week_num_tmp.length() + 1)};  
    strcpy(week_char.get(), week_num_tmp.c_str());  // Think the error is because length of c_str far greater than size of week_char?
    strptime(week_char.get(), "%Y-%W-%w", &tm);

    std::string year {std::to_string(tm.tm_year +1900)};
    std::string month {std::to_string(tm.tm_mon + 1)};
    std::string day {std::to_string(tm.tm_mday)};

    month = (month.length() == 1) ? '0' + month : month; // Zero pad if single digit month
    day = (day.length() == 1) ? '0' + day : day;  // Zero pad if single digit day

    const std::string date_str = year + month + day;
    return std::stoi(date_str);
}
2 Answers

You appear to be starting with a date in the format like this 2022-47-1 where 2022 is the 4-digit year, 47 is the week within the year, and 1 is the day within the week.

To convert to year, month, day format you can use std::get_time to convert from a string to a std::tm and then std::put_time to convert from a std::tm to a string. Both these conversions are io manipulators, meaning the must be used by way of a stream of some sort, such as std::stringstream.

Your input format appears to be %Y-%U-%w, and your output format appears to be %Y-%m-%d (YYYYMMDD)

#include <ctime>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

int main() {
  struct std::tm tm;
  std::stringstream ssin("2020-47-1"), ssout;
  ssin >> std::get_time(&tm, "%Y-%U-%w");
  ssout << std::put_time(&tm, "%Y-%m-%d");
  std::cout << ssout.str();
}

This converts 2020-47-1 to 2020-11-23

Live example on godbolt

Fixed by making this change:

//auto week_char {std::make_unique<char>(strlen(week_num_tmp.c_str()) + 1)};
auto week_char = std::make_unique<char[]>(week_num_tmp.length() + 1);
Related