From c848f16d9d08d70c254ff474f4db5f75df3935b0 Mon Sep 17 00:00:00 2001 From: Victoria Lacroix Date: Sat, 27 Dec 2025 20:28:32 -0500 Subject: [PATCH 1/4] Underflow when converting signed to unsigned --- LuaGObject/marshal.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/LuaGObject/marshal.c b/LuaGObject/marshal.c index e6eb194c..ad7db18f 100644 --- a/LuaGObject/marshal.c +++ b/LuaGObject/marshal.c @@ -21,6 +21,9 @@ static lua_Number check_integer(lua_State *L, int narg, lua_Number val_min, lua_Number val_max) { lua_Number val = luaL_checknumber(L, narg); + /* If the value is negative and the target type is unsigned, return it so it underflows. */ + if (val_min == 0 && val < 0) + return val; if (val < val_min || val > val_max) { lua_pushfstring(L, "%f is out of <%f, %f>", val, val_min, val_max); luaL_argerror(L, narg, lua_tostring(L, -1)); @@ -32,6 +35,9 @@ static lua_Integer check_integer(lua_State *L, int narg, lua_Integer val_min, lua_Integer val_max) { lua_Integer val = luaL_checkint(L, narg); + /* If the value is negative and the target type is unsigned, return it so it underflows. */ + if (val_min == 0 && val < 0) + return val; if (val < val_min || val > val_max) { lua_pushfstring(L, "%I is out of <%I, %I>", val, val_min, val_max); luaL_argerror(L, narg, lua_tostring(L, -1)); From 0125924815c136784b9a2a655d668b29df63275f Mon Sep 17 00:00:00 2001 From: Victoria Lacroix Date: Mon, 29 Dec 2025 10:52:34 -0500 Subject: [PATCH 2/4] Update unit tests to account for integer underflows --- tests/gireg.lua | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/gireg.lua b/tests/gireg.lua index daf66d9c..c80a6ded 100644 --- a/tests/gireg.lua +++ b/tests/gireg.lua @@ -56,9 +56,9 @@ function gireg.type_uint8() if not nativeIntegers then checkv(R.test_uint8(1.1), 1, 'number') end + checkv(R.test_uint8(-1), 0xff, 'number') checkv(R.test_uint8(0xff), 0xff, 'number') check(not pcall(R.test_uint8, 0x100)) - check(not pcall(R.test_uint8, -1)) check(not pcall(R.test_uint8)) check(not pcall(R.test_uint8, nil)) check(not pcall(R.test_uint8, 'string')) @@ -95,9 +95,9 @@ function gireg.type_uint16() if not nativeIntegers then checkv(R.test_uint16(1.1), 1, 'number') end + checkv(R.test_uint16(-1), 0xffff, 'number') checkv(R.test_uint16(0xffff), 0xffff, 'number') check(not pcall(R.test_uint16, 0x10000)) - check(not pcall(R.test_uint16, -1)) check(not pcall(R.test_uint16)) check(not pcall(R.test_uint16, nil)) check(not pcall(R.test_uint16, 'string')) @@ -134,9 +134,9 @@ function gireg.type_uint32() if not nativeIntegers then checkv(R.test_uint32(1.1), 1, 'number') end + checkv(R.test_uint32(-1), 0xffffffff, 'number') checkv(R.test_uint32(0xffffffff), 0xffffffff, 'number') check(not pcall(R.test_uint32, 0x100000000)) - check(not pcall(R.test_uint32, -1)) check(not pcall(R.test_uint32)) check(not pcall(R.test_uint32, nil)) check(not pcall(R.test_uint32, 'string')) @@ -181,7 +181,9 @@ function gireg.type_uint64() if not nativeIntegers then checkv(R.test_uint64(1.1), 1, 'number') end - check(not pcall(R.test_uint64, -1)) + if nativeIntegers then + checkv(R.test_uint64(-1), 0xffffffffffffffff, 'number') + end check(not pcall(R.test_uint64)) check(not pcall(R.test_uint64, nil)) check(not pcall(R.test_uint64, 'string')) @@ -189,8 +191,8 @@ function gireg.type_uint64() check(not pcall(R.test_uint64, {})) check(not pcall(R.test_uint64, function() end)) + checkv(R.test_uint64(0xffffffffffffffff), 0xffffffffffffffff, 'number') -- See comment above about lossy conversions. - --checkv(R.test_uint64(0xffffffffffffffff), 0xffffffffffffffff, 'number') --check(not pcall(R.test_uint64, 0x10000000000000000)) end @@ -212,7 +214,6 @@ function gireg.type_ushort() if not nativeIntegers then checkv(R.test_ushort(1.1), 1, 'number') end - check(not pcall(R.test_ushort, -1)) end function gireg.type_int() @@ -233,7 +234,6 @@ function gireg.type_uint() if not nativeIntegers then checkv(R.test_uint(1.1), 1, 'number') end - check(not pcall(R.test_uint, -1)) end function gireg.type_ssize() @@ -254,11 +254,9 @@ function gireg.type_size() if not nativeIntegers then checkv(R.test_size(1.1), 1, 'number') end - check(not pcall(R.test_size, -1)) end --- Helper, checks that given value has requested type and value, with some --- tolerance because of low precision of gfloat type. +-- Helper, checks that given value has requested type and value, with some tolerance because of low precision of gfloat type. local function checkvf(val, exp, tolerance) check(type(val) == 'number', string.format("got type `%s', expected `number'", type(val)), 2) From 056d1376c0fb81da141232773093f46754306914 Mon Sep 17 00:00:00 2001 From: Victoria Lacroix Date: Mon, 29 Dec 2025 10:54:31 -0500 Subject: [PATCH 3/4] Add more underflow test cases --- tests/gireg.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/gireg.lua b/tests/gireg.lua index c80a6ded..98f464b8 100644 --- a/tests/gireg.lua +++ b/tests/gireg.lua @@ -57,6 +57,7 @@ function gireg.type_uint8() checkv(R.test_uint8(1.1), 1, 'number') end checkv(R.test_uint8(-1), 0xff, 'number') + checkv(R.test_uint8(-2), 0xfe, 'number') checkv(R.test_uint8(0xff), 0xff, 'number') check(not pcall(R.test_uint8, 0x100)) check(not pcall(R.test_uint8)) @@ -96,6 +97,7 @@ function gireg.type_uint16() checkv(R.test_uint16(1.1), 1, 'number') end checkv(R.test_uint16(-1), 0xffff, 'number') + checkv(R.test_uint16(-3), 0xfffd, 'number') checkv(R.test_uint16(0xffff), 0xffff, 'number') check(not pcall(R.test_uint16, 0x10000)) check(not pcall(R.test_uint16)) @@ -135,6 +137,7 @@ function gireg.type_uint32() checkv(R.test_uint32(1.1), 1, 'number') end checkv(R.test_uint32(-1), 0xffffffff, 'number') + checkv(R.test_uint32(-4), 0xfffffffc, 'number') checkv(R.test_uint32(0xffffffff), 0xffffffff, 'number') check(not pcall(R.test_uint32, 0x100000000)) check(not pcall(R.test_uint32)) @@ -183,6 +186,7 @@ function gireg.type_uint64() end if nativeIntegers then checkv(R.test_uint64(-1), 0xffffffffffffffff, 'number') + checkv(R.test_uint64(-5), 0xfffffffffffffffb, 'number') end check(not pcall(R.test_uint64)) check(not pcall(R.test_uint64, nil)) From ec5720837a7ce9a5ace3fa3789e493b3e80ff947 Mon Sep 17 00:00:00 2001 From: Victoria Lacroix Date: Mon, 29 Dec 2025 10:57:36 -0500 Subject: [PATCH 4/4] Skip a 64-bit integer test case < 5.3 --- tests/gireg.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/gireg.lua b/tests/gireg.lua index 98f464b8..ad431bd3 100644 --- a/tests/gireg.lua +++ b/tests/gireg.lua @@ -195,7 +195,10 @@ function gireg.type_uint64() check(not pcall(R.test_uint64, {})) check(not pcall(R.test_uint64, function() end)) - checkv(R.test_uint64(0xffffffffffffffff), 0xffffffffffffffff, 'number') + -- With integer underflows, this actually works. + if nativeIntegers then + checkv(R.test_uint64(0xffffffffffffffff), 0xffffffffffffffff, 'number') + end -- See comment above about lossy conversions. --check(not pcall(R.test_uint64, 0x10000000000000000)) end