<HTML><BODY><div class="cl-sq0u197qde"><div><br>Hi, Sergey! Thanks for the patch!<br> </div><div>Please, see my comment.</div><div> </div><div><div>--<br>Best regards,</div><div>Evgeniy Temirgaleev</div></div><div> </div><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><span>From: Sergey Bronnikov <<a href="mailto:estetus@gmail.com">estetus@gmail.com</a>><br>To: tarantool-patches@dev.tarantool.org, Sergey Kaplun <<a href="mailto:skaplun@tarantool.org">skaplun@tarantool.org</a>>,<a href="mailto:e.temirgaleev@tarantool.org">e.temirgaleev@tarantool.org</a><br>Date: Friday, June 5, 2026 6:19 PM +03:00</span><br> <div><div id=""><div class="cl-851jltyjq5"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div id="style_17806727590583898164_mr_css_attr"><div id="style_17806727590583898164_BODY_mr_css_attr">From: Mike Pall <mike><br><br>Reported by Sergey Bronnikov.<br><br>(cherry picked from commit 86d414f5cae062b06998ec66b0696a47d4f6a0f0)<br><br>The function `lj_cf_os_time()` calculates `tm_mon` and `tm_year`<br>values using `get_field()` and when the helper function returns<br>a negative value the resulted values may be negative as well. This<br>is an undefined behaviour (signed integer overflow). The patch fixes<br>that by adding a cast for the resulted value to returned by<br>`get_field()`.<br><br>Sergey Bronnikov:<br>* added the description and the test for the problem<br><br>Part of tarantool/tarantool#12480<br>---<br><br>Branch: <a href="https://github.com/tarantool/luajit/tree/ligurio/lj-1454-ub-os-time">https://github.com/tarantool/luajit/tree/ligurio/lj-1454-ub-os-time</a><br><br>Related issues:<br><br>* <a href="https://github.com/tarantool/tarantool/issues/12480">https://github.com/tarantool/tarantool/issues/12480</a><br>* <a href="https://github.com/LuaJIT/LuaJIT/issues/1454">https://github.com/LuaJIT/LuaJIT/issues/1454</a><br><br>src/lib_os.c | 4 ++--<br>test/tarantool-tests/lj-1454-os-time.test.lua | 19 +++++++++++++++++++<br>2 files changed, 21 insertions(+), 2 deletions(-)<br>create mode 100644 test/tarantool-tests/lj-1454-os-time.test.lua<br><br>diff --git a/src/lib_os.c b/src/lib_os.c<br>index ffbc3fdc..0feb0d47 100644<br>--- a/src/lib_os.c<br>+++ b/src/lib_os.c<br>@@ -239,8 +239,8 @@ LJLIB_CF(os_time)<br>ts.tm_min = getfield(L, "min", 0);<br>ts.tm_hour = getfield(L, "hour", 12);<br>ts.tm_mday = getfield(L, "day", -1);<br>- ts.tm_mon = getfield(L, "month", -1) - 1;<br>- ts.tm_year = getfield(L, "year", -1) - 1900;<br>+ ts.tm_mon = (int)((unsigned int)getfield(L, "month", -1) - 1u);<br>+ ts.tm_year = (int)((unsigned int)getfield(L, "year", -1) - 1900u);</div></div></div></div></div></div></blockquote></div></div><div>We just suppress signed integer-overflow warning implicitly by casting (and get an unsigned integer-overflow warning instead — try `luajit -e "os.time({ day = 0, month = 0, year = 0 })"` with clang and -fsanitize=integer).</div><div><div><div> </div><div>The ignored warning is meaningful in a case, when we get a valid input for mktime() and it returns a valid output for it args, but that result is invalid for args of os.time() due to integer overflow.</div><div> </div><div>I think, the range checks for Lua os.time() args are needed to provide <span style="white-space:pre-wrap">predictable error response. At least, a check for year arg to prevent the invalid result of os.time().</span></div><div> </div><div>Where are some example calls below (Ubuntu 24, amd64) with some debug printing.</div><div> </div><div><strong>Not patched version — warning + invalid result</strong></div><div><div><div>/src/src/luajit -e "print(os.time({ day = 1, month = 1, year = -2^31 }),'\n')"</div><div>lib_os.c:244:42: runtime error: signed integer overflow: -2147483648 - 1900 cannot be represented in type 'int'</div><div>lj_cf_os_time: (1) <- ts.tm_mday=1 ts.tm_mon=0 ts.tm_year=2147481748</div><div>lj_cf_os_time: (2) -> ts.tm_mday=1 ts.tm_mon=0 ts.tm_year=2147481748 t=67767976233576000 errno=0</div><div>6.7767976233576e+16</div></div></div></div></div><div> </div><div><strong>Patched version — invalid result only</strong></div><div><div><div>/src/src/luajit -e "print(os.time({ day = 1, month = 1, year = -2^31 }),'\n')"</div><div>lj_cf_os_time: (1) <- getfield(month)=1 getfield(year)=-2147483648 (u)getfield(month)=0 (u)getfield(year)=2147481748 ts.tm_mday=1 ts.tm_mon=0 ts.tm_year=2147481748</div><div>lj_cf_os_time: (2) -> ts.tm_mday=1 ts.tm_mon=0 ts.tm_year=2147481748 t=67767976233576000 errno=0</div><div>6.7767976233576e+16</div></div></div><div> </div><div>Some more calls for example: the mktime() may ignore invalid tm_mday, tm_mon values of struct tm and update them in a «no error» case.</div><div> </div><div><strong>Not patched version</strong></div><div> </div><div><div><div>/src/src/luajit -e "print(os.time({ day = -2^31, month = -2^31, year = -2^31+1900 }),'\n')"</div><div>lib_os.c:243:42: runtime error: signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'</div><div>lj_cf_os_time: (1) ts.tm_mday=-2147483648 ts.tm_mon=2147483647 ts.tm_year=-2147483648</div><div>lj_cf_os_time: (2) ts.tm_mday=20 ts.tm_mon=0 ts.tm_year=-1974406288 t=-62306246666232000 errno=0</div><div>-6.2306246666232e+16</div><div> </div><div>/src/src/luajit -e "print(os.time({ day = -2^31, month = -2^31, year = -2^31+1899 }),'\n')"</div><div>lib_os.c:243:42: runtime error: signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'</div><div>lib_os.c:244:42: runtime error: signed integer overflow: -2147481749 - 1900 cannot be represented in type 'int'</div><div>lj_cf_os_time: (1) ts.tm_mday=-2147483648 ts.tm_mon=2147483647 ts.tm_year=2147483647</div><div>lj_cf_os_time: (2) ts.tm_mday=-2147483648 ts.tm_mon=2147483647 ts.tm_year=2147483647 t=-1 errno=75</div><div>nil</div></div></div><div> </div><div><strong>Patched version</strong></div><div><div><div> </div><div>/src/src/luajit -e "print(os.time({ day = -2^31, month = -2^31, year = -2^31+1900 }),'\n')"</div><div>lj_cf_os_time: (1) <- getfield(month)=-2147483648 getfield(year)=-2147481748 (u)getfield(month)=2147483647 (u)getfield(year)=2147483648 ts.tm_mday=-2147483648 ts.tm_mon=2147483647 ts.tm_year=-2147483648</div><div>lj_cf_os_time: (2) -> ts.tm_mday=20 ts.tm_mon=0 ts.tm_year=-1974406288 t=-62306246666232000 errno=0</div><div>-6.2306246666232e+16</div><div> </div><div>/src/src/luajit -e "print(os.time({ day = -2^31, month = -2^31, year = -2^31+1899 }),'\n')"</div><div>lj_cf_os_time: (1) <- getfield(month)=-2147483648 getfield(year)=-2147481749 (u)getfield(month)=2147483647 (u)getfield(year)=2147483647 ts.tm_mday=-2147483648 ts.tm_mon=2147483647 ts.tm_year=2147483647</div><div>lj_cf_os_time: (2) -> ts.tm_mday=-2147483648 ts.tm_mon=2147483647 ts.tm_year=2147483647 t=-1 errno=75</div><div>nil</div><div> </div></div></div><div class="cl-sq0u197qde"><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><div><div><div class="cl-851jltyjq5"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div><div>ts.tm_isdst = getboolfield(L, "isdst");<br>t = mktime(&ts);<br>}<br>diff --git a/test/tarantool-tests/lj-1454-os-time.test.lua b/test/tarantool-tests/lj-1454-os-time.test.lua<br>new file mode 100644<br>index 00000000..2a48750c<br>--- /dev/null<br>+++ b/test/tarantool-tests/lj-1454-os-time.test.lua<br>@@ -0,0 +1,19 @@<br>+local tap = require('tap')<br>+<br>+-- The test file to demonstrate UBSan warning for `os.time()` with<br>+-- a huge indices value for month and/or year.<br>+-- See also: <a href="https://github.com/LuaJIT/LuaJIT/issues/1454">https://github.com/LuaJIT/LuaJIT/issues/1454</a>.<br>+local test = tap.test('lj-1454-os-time')<br>+<br>+test:plan(1)<br>+<br>+local INT_MIN = -2 ^ 31<br>+<br>+local cur_time = os.time({<br>+ day = 1,<br>+ month = INT_MIN,<br>+ year = INT_MIN,<br>+})<br>+test:is(cur_time, nil, 'os.time() with INT_MIN')<br>+<br>+test:done(true)<br>--<br>2.43.0</div></div></div></div></div></div></blockquote></div></div><div><div> </div></div></BODY></HTML>