diff --git a/src/prometheus.lua b/src/prometheus.lua index d5b27366..7b15453a 100644 --- a/src/prometheus.lua +++ b/src/prometheus.lua @@ -39,6 +39,17 @@ end) then end end +if not math.log10 then + local ln10 = math.log(10); + math.log10 = function(x) + return math.log(x) / ln10; + end +end + +if not loadstring then + loadstring = load +end + -- newproxy polyfill _G.newproxy = _G.newproxy or function(arg) if arg then diff --git a/src/prometheus/steps/NumbersToExpressions.lua b/src/prometheus/steps/NumbersToExpressions.lua index 6d4b3838..78c5bf7d 100644 --- a/src/prometheus/steps/NumbersToExpressions.lua +++ b/src/prometheus/steps/NumbersToExpressions.lua @@ -14,6 +14,8 @@ local visitast = require("prometheus.visitast") local util = require("prometheus.util") local logger = require("logger") local AstKind = Ast.AstKind +local SAFE_INT_LIMIT = jit and math.huge or 2^53 +local MAX_VAL = jit and math.huge or 2^54 local NumbersToExpressions = Step:extend() NumbersToExpressions.Description = "This Step Converts number Literals to Expressions" @@ -37,6 +39,9 @@ NumbersToExpressions.SettingsDescriptor = { NumberRepresentationMutation = { type = "boolean", default = false, + + -- NOTE: This alias is a legacy misspelling preservation. + -- Please don't remove it. aliases = { "NumberRepresentationMutaton" }, }, @@ -92,7 +97,9 @@ function NumbersToExpressions:init(_) end, function(val, depth) -- Modulo + if val > MAX_VAL then return false end local lhs, rhs = generateModuloExpression(val) + if lhs > SAFE_INT_LIMIT or rhs > SAFE_INT_LIMIT then return false end if tonumber(tostring(lhs)) % tonumber(tostring(rhs)) ~= val then return false end @@ -153,7 +160,30 @@ function NumbersToExpressions:CreateNumberExpression(val, depth) local exp = math.floor(math.log10(math.abs(val))) local mantissa = val / (10 ^ exp) - return Ast.NumberExpression(string.format("%.15ge%d", mantissa, exp)) + + -- LuaJIT has full double precision support. Meaning we don't need aggressive safety harnesses for LuaJIT. + if jit then + return Ast.NumberExpression(string.format("%.15ge%d", mantissa, exp)) + end + + local formatStr = string.format("%ge%+d", mantissa, exp) + local concatStr = mantissa .. "e" .. (exp >= 0 and "+" or "") .. exp + + local formatVal = loadstring("return " .. formatStr) + local concatVal = loadstring("return " .. concatStr) + + local formatResult = formatVal and formatVal() + local concatResult = concatVal and concatVal() + + local formatErr = formatResult and math.abs(formatResult - val) or math.huge + local concatErr = concatResult and math.abs(concatResult - val) or math.huge + + if formatErr > 0 and concatErr > 0 then + return Ast.NumberExpression(val) + end + + local useFormat = formatErr <= concatErr + return Ast.NumberExpression(useFormat and formatStr or concatStr) end if format == "normal" then @@ -185,4 +215,4 @@ function NumbersToExpressions:apply(ast) end) end -return NumbersToExpressions +return NumbersToExpressions \ No newline at end of file diff --git a/tests/strings-integrity.lua b/tests/strings-integrity.lua new file mode 100644 index 00000000..fb507700 --- /dev/null +++ b/tests/strings-integrity.lua @@ -0,0 +1,26 @@ +--============================================================ +-- Strings Integrity Test Suite +-- Target: NumbersToExpressions Step +-- Author: SpinnySpiwal +-- Purpose: Prevent any further quality regressions from NumbersToExpressions. +-- There was no actual bugs per se, but the bug was the use of Vanilla Lua instead of LuaJIT. +-- This test ensures that the obfuscator stays client-agnostic & doesn't require a specific Lua version. +--============================================================ +local integrity = {} +for i = 0, 255 do integrity[i+1]=i end +assert(#integrity == 256, "Failed to create integrity array! Reason: array length is not 256!") +local array = {} + +local function chararray(str) + for i = 1, #str do + array[i] = string.byte(str:sub(i, i)) + end + return array +end + +local cArray = chararray("\0\1\2\3\4\5\6\7\8\9\10\11\12\13\14\15\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\32\33\34\35\36\37\38\39\40\41\42\43\44\45\46\47\48\49\50\51\52\53\54\55\56\57\58\59\60\61\62\63\64\65\66\67\68\69\70\71\72\73\74\75\76\77\78\79\80\81\82\83\84\85\86\87\88\89\90\91\92\93\94\95\96\97\98\99\100\101\102\103\104\105\106\107\108\109\110\111\112\113\114\115\116\117\118\119\120\121\122\123\124\125\126\127\128\129\130\131\132\133\134\135\136\137\138\139\140\141\142\143\144\145\146\147\148\149\150\151\152\153\154\155\156\157\158\159\160\161\162\163\164\165\166\167\168\169\170\171\172\173\174\175\176\177\178\179\180\181\182\183\184\185\186\187\188\189\190\191\192\193\194\195\196\197\198\199\200\201\202\203\204\205\206\207\208\209\210\211\212\213\214\215\216\217\218\219\220\221\222\223\224\225\226\227\228\229\230\231\232\233\234\235\236\237\238\239\240\241\242\243\244\245\246\247\248\249\250\251\252\253\254\255") +for i = 1, #cArray do + if cArray[i] ~= integrity[i] then + error("Integrity check failed!") + end +end \ No newline at end of file