Skip to content

Commit e6cec37

Browse files
committed
Add tests for UB in CI, correct some more float->int UB
1 parent fc29a28 commit e6cec37

File tree

7 files changed

+47
-10
lines changed

7 files changed

+47
-10
lines changed

Analysis/src/BuiltinDefinitions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,10 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes, SolverMode mode)
12821282
return arena->addType(TableType{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
12831283
}
12841284

1285+
// ServerLua: Suppress UBSan for out-of-range select() index (bounds-checked afterward)
1286+
#if defined(__clang__) || defined(__GNUC__)
1287+
__attribute__((no_sanitize("float-cast-overflow")))
1288+
#endif
12851289
std::optional<WithPredicate<TypePackId>> MagicSelect::handleOldSolver(
12861290
TypeChecker& typechecker,
12871291
const ScopePtr& scope,

Ast/src/Parser.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3130,6 +3130,10 @@ AstExpr* Parser::parseAssertionExpr()
31303130
return expr;
31313131
}
31323132

3133+
// ServerLua: Suppress UBSan for intentional precision-loss detection cast
3134+
#if defined(__clang__) || defined(__GNUC__)
3135+
__attribute__((no_sanitize("float-cast-overflow")))
3136+
#endif
31333137
static ConstantNumberParseResult parseInteger(double& result, const char* data, int base)
31343138
{
31353139
LUAU_ASSERT(base == 2 || base == 16);

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ ifeq ($(config),coverage)
120120
endif
121121

122122
ifeq ($(config),sanitize)
123-
CXXFLAGS+=-fsanitize=address -O1 -DLUAU_ENABLE_ASAN=1
124-
LDFLAGS+=-fsanitize=address
123+
CXXFLAGS+=-fsanitize=address,undefined -fno-sanitize=vptr -O1 -DLUAU_ENABLE_ASAN=1
124+
LDFLAGS+=-fsanitize=address,undefined
125125
endif
126126

127127
ifeq ($(config),analyze)

VM/src/cjson/lua_cjson.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,8 +697,8 @@ static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json)
697697
/* table, key, value */
698698
if (lua_type(l, -2) == LUA_TNUMBER &&
699699
(k = lua_tonumber(l, -2))) {
700-
/* Integer >= 1 ? */
701-
if (floor(k) == k && k >= 1) {
700+
/* Integer >= 1 and in int range? (floor(inf)==inf, so check upper bound) */
701+
if (floor(k) == k && k >= 1 && k <= INT_MAX) {
702702
if (k > max)
703703
max = k;
704704
items++;

VM/src/lapi.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,9 +431,9 @@ int lua_tointegerx(lua_State* L, int idx, int* isnum)
431431
}
432432
else if (tonumber(o, &n))
433433
{
434-
int res;
434+
// ServerLua: Use safe conversion for cross-platform consistency
435435
double num = nvalue(o);
436-
luai_num2int(res, num);
436+
int res = luai_num2int_safe(num);
437437
if (isnum)
438438
*isnum = 1;
439439
return res;

VM/src/lnumutils.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,28 @@ inline float luai_lerpf(float a, float b, float t)
6464
return (t == 1.0) ? b : a + (b - a) * t;
6565
}
6666

67+
// ServerLua: Safe float-to-int with x86 semantics
68+
inline int luai_num2int_safe(double d)
69+
{
70+
constexpr double kMaxInt = 2147483648.0; // 2^31
71+
if (d >= -kMaxInt && d < kMaxInt)
72+
return (int)d;
73+
return (int)(-kMaxInt);
74+
}
75+
76+
// ServerLua: Suppress UBSan float-cast-overflow in ASAN builds
77+
#ifdef LUAU_ENABLE_ASAN
78+
#if defined(__clang__) || defined(__GNUC__)
79+
__attribute__((no_sanitize("float-cast-overflow")))
80+
#endif
81+
inline int luai_num2int_impl(double d)
82+
{
83+
return (int)d;
84+
}
85+
#define luai_num2int(i, d) ((i) = luai_num2int_impl(d))
86+
#else
6787
#define luai_num2int(i, d) ((i) = (int)(d))
88+
#endif
6889

6990
// On MSVC in 32-bit, double to unsigned cast compiles into a call to __dtoui3, so we invoke x87->int64 conversion path manually
7091
#if defined(_MSC_VER) && defined(_M_IX86)

VM/src/lvmexecute.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -707,8 +707,10 @@ static void luau_execute(lua_State* L)
707707
{
708708
LuaTable* h = hvalue(rb);
709709

710+
// ServerLua: Use luai_num2int for UBSan suppression in ASAN builds
710711
double indexd = nvalue(rc);
711-
int index = int(indexd);
712+
int index;
713+
luai_num2int(index, indexd);
712714

713715
// index has to be an exact integer and in-bounds for the array portion
714716
if (LUAU_LIKELY(unsigned(index) - 1 < unsigned(h->sizearray) && !h->metatable && double(index) == indexd))
@@ -748,8 +750,10 @@ static void luau_execute(lua_State* L)
748750
{
749751
LuaTable* h = hvalue(rb);
750752

753+
// ServerLua: Use luai_num2int for UBSan suppression in ASAN builds
751754
double indexd = nvalue(rc);
752-
int index = int(indexd);
755+
int index;
756+
luai_num2int(index, indexd);
753757

754758
// index has to be an exact integer and in-bounds for the array portion
755759
if (LUAU_LIKELY(unsigned(index) - 1 < unsigned(h->sizearray) && !h->metatable && !h->readonly && double(index) == indexd))
@@ -1856,7 +1860,9 @@ static void luau_execute(lua_State* L)
18561860
{
18571861
// ServerLua: division by -1 is weird, and
18581862
// previously special-cased to prevent division errors.
1859-
setintvalue(ra, -1 * intvalue(rb));
1863+
// Note: -INT32_MIN overflows, so handle that case explicitly.
1864+
int32_t val = intvalue(rb);
1865+
setintvalue(ra, val == INT32_MIN ? INT32_MIN : -val);
18601866
}
18611867
else
18621868
{
@@ -2148,7 +2154,9 @@ static void luau_execute(lua_State* L)
21482154
{
21492155
// ServerLua: division by -1 is weird, and
21502156
// previously special-cased to prevent division errors.
2151-
setintvalue(ra, -1 * intvalue(rb));
2157+
// Note: -INT32_MIN overflows, so handle that case explicitly.
2158+
int32_t val = intvalue(rb);
2159+
setintvalue(ra, val == INT32_MIN ? INT32_MIN : -val);
21522160
}
21532161
else
21542162
{

0 commit comments

Comments
 (0)