开启-ftrapv编译时boost::posix_time::from_time_t传大值崩溃问题
问题说明
调用boost::posix_time::ptime from_time_t(time_t t)函数时,传入UINT_MAX这类极大值会出现异常:
- 编译开启
-ftrapv选项时,程序因有符号整数溢出直接崩溃 - 不开启
-ftrapv选项时,函数偶现返回错误结果,返回的时间值接近1970年1月1日00:00 - 诉求为保留
-ftrapv编译选项,确认该现象属于Boost库bug,还是from_time_t函数存在明确入参限制
复现代码如下:
#include <boost/date_time/posix_time/posix_time.hpp> #include <climits> #include <type_traits> int main() { long int lmax{LONG_MAX}; unsigned int umax{UINT_MAX}; std::cout<<"Start = "<<lmax<<std::endl; std::cout<<"std::is_same_v<time_t, long int> = " <<std::is_same<time_t, long int>::value<<std::endl; try { std::cout <<boost::posix_time::from_time_t(umax)<<std::endl; std::cout <<boost::posix_time::from_time_t(lmax)<<std::endl; } catch(const std::exception& e) { std::cout<<"exception e = "<<e.what()<<std::endl; } std::cout<<"Finish"<<std::endl; }
结论
该现象不属于Boost库bug,from_time_t函数对入参存在明确取值限制,传入超出范围的参数属于未定义行为,具体原因如下:
time_t是C/C++标准规定的有符号算术类型,绝大多数编译环境下为32位或64位有符号整数,用于表示从1970-01-01 00:00:00 UTC起算的秒数。直接传入UINT_MAX(32位无符号整数最大值,数值为4294967295)时,会先发生无符号整数到有符号time_t的隐式类型转换:- 若环境中
time_t为32位有符号类型,UINT_MAX转换后的值为-1,对应时间为1969-12-31 23:59:59,和观测到的“返回值接近1970年时间原点”的现象完全吻合 - 若环境中
time_t为64位有符号类型,UINT_MAX转换后数值为4294967295,本身对应2106年的时间点,在64位time_t的表示范围内,但Boost内部做时间分量换算时,部分中间计算逻辑会用32位整数承接结果,此时会触发有符号整数溢出,这就是开启-ftrapv时程序直接崩溃的根因
- 若环境中
-ftrapv是GCC/Clang的编译选项,作用是对有符号整数溢出这类标准定义的未定义行为主动触发陷阱终止程序,它捕获到的溢出本质是入参超出了接口合法取值范围,并非Boost实现存在逻辑错误- Boost官方对
from_time_t的入参有明确约束:传入值必须是time_t类型可合法表示、且对应有效POSIX时间点的值,所有超出time_t取值范围、无法映射到有效时间的传参,都属于未定义行为,库本身不做额外的边界校验兜底。
规避方案
- 调用
from_time_t前主动增加入参边界校验,禁止传入超出time_t合法取值范围的参数:校验入参值必须落在[std::numeric_limits<time_t>::min(), std::numeric_limits<time_t>::max()]区间内 - 禁止直接将无符号整数类型的极值作为入参传入,传参前先做显式类型转换和范围判断
- 不要依赖未定义行为下的函数返回结果,未开启
-ftrapv时返回接近时间原点的错误值,本质是整数溢出后的回绕结果,不具备跨环境可移植性。
内容的提问来源于stack exchange,提问作者AlexBG
相关产品推荐
相关产品推荐

