Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect integral underflow, NaN and inf values in the new parser #2114

Merged
81 changes: 45 additions & 36 deletions Shared/mods/deathmatch/logic/lua/CLuaFunctionParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,19 +352,41 @@ struct CLuaFunctionParserBase
if constexpr (std::is_same_v<T, dummy_type>)
return dummy_type{};
// primitive types are directly popped
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> || std::is_integral_v<T>)
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
return lua::PopPrimitive<T>(L, index);
// floats/doubles may not be NaN
else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>)
else if constexpr (std::is_same_v<T, bool>)
return lua::PopPrimitive<T>(L, index);
else if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) // bool is an integral type, so must pop it before ^^
{
T value = lua::PopPrimitive<T>(L, index);
if (std::isnan(value))
{
const auto number = lua::PopPrimitive<lua_Number>(L, index);

const auto SetError = [&](const char* expected, const char* got) {
// Subtract one from the index, as the call to lua::PopPrimitive above increments the index, even if the
// underlying element is of a wrong type
SetBadArgumentError(L, "number", index - 1, "NaN");
SetBadArgumentError(L, expected, index - 1, got);
};

if (std::isnan(number))
{
SetError("number", "NaN");
return static_cast<T>(number);
}

if (std::isinf(number)) {
SetError("number", "inf");
return static_cast<T>(number);
}
return value;

// NOTE/TODO: Use C++20 `std::in_range` here instead
// For now this doesn't do all the safety checks, but this should be "good enough" [until we switch to C++20]
if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>)
{
SetError("positive number", "negative");

return static_cast<T>(static_cast<int64_t>(number));
}

return static_cast<T>(number);
}
else if constexpr (std::is_enum_v<T>)
{
Expand Down Expand Up @@ -488,10 +510,7 @@ struct CLuaFunctionParserBase
{
if (lua_isnumber(L, index))
{
CVector2D vec;
vec.fX = lua::PopPrimitive<float>(L, index);
vec.fY = lua::PopPrimitive<float>(L, index);
return vec;
return { PopUnsafe<float>(L, index), PopUnsafe<float>(L, index) };
}
else
{
Expand Down Expand Up @@ -520,11 +539,7 @@ struct CLuaFunctionParserBase
{
if (lua_isnumber(L, index))
{
CVector vec;
vec.fX = lua::PopPrimitive<float>(L, index);
vec.fY = lua::PopPrimitive<float>(L, index);
vec.fZ = lua::PopPrimitive<float>(L, index);
return vec;
return { PopUnsafe<float>(L, index), PopUnsafe<float>(L, index), PopUnsafe<float>(L, index) };
}
else
{
Expand All @@ -551,12 +566,8 @@ struct CLuaFunctionParserBase
{
if (lua_isnumber(L, index))
{
CVector4D vec;
vec.fX = lua::PopPrimitive<float>(L, index);
vec.fY = lua::PopPrimitive<float>(L, index);
vec.fZ = lua::PopPrimitive<float>(L, index);
vec.fW = lua::PopPrimitive<float>(L, index);
return vec;
return { PopUnsafe<float>(L, index), PopUnsafe<float>(L, index),
PopUnsafe<float>(L, index), PopUnsafe<float>(L, index) };
}
else
{
Expand All @@ -583,19 +594,17 @@ struct CLuaFunctionParserBase
{
if (lua_isnumber(L, index))
{
const auto ReadVector = [&] {
return CVector(PopUnsafe<float>(L, index), PopUnsafe<float>(L, index), PopUnsafe<float>(L, index));
};

CMatrix matrix;
matrix.vRight.fX = lua::PopPrimitive<float>(L, index);
matrix.vRight.fY = lua::PopPrimitive<float>(L, index);
matrix.vRight.fZ = lua::PopPrimitive<float>(L, index);
matrix.vFront.fX = lua::PopPrimitive<float>(L, index);
matrix.vFront.fY = lua::PopPrimitive<float>(L, index);
matrix.vFront.fZ = lua::PopPrimitive<float>(L, index);
matrix.vUp.fX = lua::PopPrimitive<float>(L, index);
matrix.vUp.fY = lua::PopPrimitive<float>(L, index);
matrix.vUp.fZ = lua::PopPrimitive<float>(L, index);
matrix.vPos.fX = lua::PopPrimitive<float>(L, index);
matrix.vPos.fY = lua::PopPrimitive<float>(L, index);
matrix.vPos.fZ = lua::PopPrimitive<float>(L, index);

matrix.vRight = ReadVector();
matrix.vFront = ReadVector();
matrix.vUp = ReadVector();
matrix.vPos = ReadVector();

return matrix;
}
else
Expand Down Expand Up @@ -635,7 +644,7 @@ struct CLuaFunctionParserBase
return static_cast<T>(result);
}
else if constexpr (std::is_same_v<T, SColor>)
return static_cast<unsigned long>(lua::PopPrimitive<int64_t>(L, index));
return static_cast<unsigned long>(static_cast<int64_t>(lua::PopPrimitive<lua_Number>(L, index)));
else if constexpr (std::is_same_v<T, CLuaArgument>)
{
CLuaArgument argument;
Expand Down
68 changes: 8 additions & 60 deletions Shared/mods/deathmatch/logic/lua/LuaBasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,66 +29,6 @@ namespace lua
return outValue;
}

template <>
int PopPrimitive<int>(lua_State* L, int& index)
{
return static_cast<int>(lua_tonumber(L, index++));
}

template <>
unsigned int PopPrimitive<unsigned int>(lua_State* L, int& index)
{
return static_cast<unsigned int>(lua_tonumber(L, index++));
}

template <>
short PopPrimitive<short>(lua_State* L, int& index)
{
return static_cast<short>(lua_tonumber(L, index++));
}

template <>
unsigned short PopPrimitive<unsigned short>(lua_State* L, int& index)
{
return static_cast<unsigned short>(lua_tonumber(L, index++));
}

template <>
char PopPrimitive<char>(lua_State* L, int& index)
{
return static_cast<char>(lua_tonumber(L, index++));
}

template <>
unsigned char PopPrimitive<unsigned char>(lua_State* L, int& index)
{
return static_cast<unsigned char>(lua_tonumber(L, index++));
}

template <>
int64_t PopPrimitive<int64_t>(lua_State* L, int& index)
{
return static_cast<int64_t>(lua_tonumber(L, index++));
}

template <>
uint64_t PopPrimitive<uint64_t>(lua_State* L, int& index)
{
return static_cast<uint64_t>(lua_tonumber(L, index++));
}

template <>
float PopPrimitive<float>(lua_State* L, int& index)
{
return static_cast<float>(lua_tonumber(L, index++));
}

template <>
double PopPrimitive<double>(lua_State* L, int& index)
{
return static_cast<double>(lua_tonumber(L, index++));
}

template <>
bool PopPrimitive<bool>(lua_State* L, int& index)
{
Expand All @@ -100,4 +40,12 @@ namespace lua
{
return lua_touserdata(L, index++);
}

template<>
lua_Number PopPrimitive<lua_Number>(lua_State* L, int& index)
{
return lua_tonumber(L, index++);
}

// Int types aren't Poppable directly, because they need to be checked for under/overflow, NaN, inf
} // namespace lua