Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/native/basic/native_basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ struct IpairsState {
// 辅助:比较 key 是否相等
static bool KeysEqual(CVar a, CVar b) {
if (a.type_ == b.type_) {
if (b.type_ == static_cast<int>(VarType::Int) || b.type_ == static_cast<int>(VarType::Bool)) return a.data_.i == b.data_.i;
// Bool only writes data_.b; comparing data_.i can see stale union bytes.
if (b.type_ == static_cast<int>(VarType::Bool)) return a.data_.b == b.data_.b;
if (b.type_ == static_cast<int>(VarType::Int)) return a.data_.i == b.data_.i;
if (b.type_ == static_cast<int>(VarType::Float)) return a.data_.f == b.data_.f;
if (b.type_ == static_cast<int>(VarType::StringId)) return a.data_.i == b.data_.i;
if (b.type_ == static_cast<int>(VarType::String)) {
Expand Down
10 changes: 9 additions & 1 deletion src/native/container/native_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <boost/container/list.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/container/vector.hpp>
#include <cmath>
#include <cstdint>
#include <format>
#include <iterator>
Expand Down Expand Up @@ -96,7 +97,14 @@ struct ContainerKey {
case Kind::Int:
return a.i < b.i;
case Kind::Float:
return a.f < b.f;
// NaN breaks strict weak ordering (a<b and b<a both false vs self).
// Sort all NaNs after non-NaN floats; NaNs compare equal to each other.
{
const bool a_nan = std::isnan(a.f);
const bool b_nan = std::isnan(b.f);
if (a_nan || b_nan) return !a_nan && b_nan;
return a.f < b.f;
}
case Kind::String:
return a.s < b.s;
}
Expand Down
5 changes: 4 additions & 1 deletion src/native/crypto/crypto_cipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ std::vector<uint8_t> Rc4(const uint8_t *key, size_t key_len, const uint8_t *data
}
}
int final_len = 0;
EVP_EncryptFinal_ex(ctx, out.data() + outlen, &final_len);
if (EVP_EncryptFinal_ex(ctx, out.data() + outlen, &final_len) != 1) {
EVP_CIPHER_CTX_free(ctx);
ThrowFakeluaException("rc4: EVP_EncryptFinal_ex failed");
}

EVP_CIPHER_CTX_free(ctx);
return out;
Expand Down
14 changes: 11 additions & 3 deletions src/native/http/native_http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <stdexcept>
#if defined(_WIN32)
#define strcasecmp _stricmp
Expand Down Expand Up @@ -206,8 +207,13 @@ static bool TryParseHttpMessage(std::string &buf, bool request, HttpRequestData
std::string cl = HeaderGet(hdrs, "Content-Length");
size_t body_off = hdr_end + 4;
if (!cl.empty()) {
size_t n = static_cast<size_t>(std::strtoull(cl.c_str(), nullptr, 10));
if (buf.size() < body_off + n) return false;
// Reject non-decimal / overflow so body_off + n cannot wrap.
char *end = nullptr;
unsigned long long parsed = std::strtoull(cl.c_str(), &end, 10);
if (end == cl.c_str() || *end != '\0') return false;
if (parsed > std::numeric_limits<size_t>::max() - body_off) return false;
size_t n = static_cast<size_t>(parsed);
if (buf.size() < body_off || buf.size() - body_off < n) return false;
if (body) *body = buf.substr(body_off, n);
if (req) req->body = buf.substr(body_off, n);
buf.erase(0, body_off + n);
Expand Down Expand Up @@ -995,9 +1001,11 @@ static CVar HttpServerFn(State *s, CVar *args, int n) {
auto *srv = new HttpServer(s);
srv->SetNativeObject(nat);
try {
// Listen takes ownership of tls_ctx immediately (stores in ssl_ctx_).
// On failure, ~HttpServer/Close frees it — do not SSL_CTX_free here.
srv->Listen(ip, port, backlog, timeout_ms, tls_ctx);
tls_ctx = nullptr;
} catch (const std::exception &e) {
if (tls_ctx) SSL_CTX_free(tls_ctx);
delete srv;
s->GetNativeObjectManager().DestroyGroup(gid);
ThrowFakeluaException(std::string("http.server: ") + e.what());
Expand Down
9 changes: 7 additions & 2 deletions src/native/ini/native_ini.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,13 @@ static CVar IniEncode(State *s, CVar *args, int n) {
// INI has no nested tables; encode array as comma-separated
auto arr_kvs = table::TableHelper::CollectKVPairs(skv.val);
std::sort(arr_kvs.begin(), arr_kvs.end(), [](const table::TableKV &a, const table::TableKV &b) {
if (a.key.type_ == static_cast<int>(VarType::Int) && b.key.type_ == static_cast<int>(VarType::Int)) return a.key.data_.i < b.key.data_.i;
return false;
const bool a_int = a.key.type_ == static_cast<int>(VarType::Int);
const bool b_int = b.key.type_ == static_cast<int>(VarType::Int);
// Integer keys first (array part), then others by string form —
// keeps a total order for mixed Int/String section values.
if (a_int && b_int) return a.key.data_.i < b.key.data_.i;
if (a_int != b_int) return a_int;
return inter::FakeluaToNativeString(nullptr, a.key) < inter::FakeluaToNativeString(nullptr, b.key);
});
for (size_t i = 0; i < arr_kvs.size(); i++) {
if (i > 0) out += ", ";
Expand Down
15 changes: 11 additions & 4 deletions src/native/json/native_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

#include <algorithm>
#include <boost/json.hpp>
#include <limits>
#include <string>
#include <string_view>
#include <unordered_set>

namespace fakelua::json {
Expand Down Expand Up @@ -46,15 +48,20 @@ static CVar JsonValueToLua(State *s, const bj::value &v) {
return inter::NativeToFakeluaLonglong(s, v.get_int64());
}
if (v.is_uint64()) {
// Lua numbers are signed; but we can still represent up to 2^63-1 as positive.
// For values > 2^63-1, we could convert to double, but keep simple.
return inter::NativeToFakeluaLonglong(s, static_cast<int64_t>(v.get_uint64()));
const uint64_t u = v.get_uint64();
// Keep exact integers in int64 range; larger values become double
// (lossy above 2^53) instead of wrapping to a negative int64.
if (u <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
return inter::NativeToFakeluaLonglong(s, static_cast<int64_t>(u));
}
return inter::NativeToFakeluaDouble(s, static_cast<double>(u));
}
if (v.is_double()) {
return inter::NativeToFakeluaDouble(s, v.get_double());
}
if (v.is_string()) {
return inter::NativeToFakeluaString(s, v.get_string().c_str());
const auto &js = v.get_string();
return inter::NativeToFakeluaStringView(s, std::string_view(js.data(), js.size()));
}
if (v.is_array()) {
CVar tbl = table::TableHelper::CreateTable(s);
Expand Down
27 changes: 19 additions & 8 deletions src/native/net/net_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,25 +305,36 @@ bool TryParsePacket(CircularBuffer &buf, const NetConfig &cfg, const char *&out_
case FramerType::LineDelimiter: {
if (buf.Empty()) return false;
size_t total = buf.Size();
if (total > static_cast<size_t>(cfg.max_packet_len)) {
// 超过 max_packet_len 仍未见到换行符,视为恶意/异常连接
out_error = true;
return false;
// Only scan up to max_packet_len+1: a complete short line may sit at
// the front while later bytes push total past max_packet_len.
size_t scan = total;
if (cfg.max_packet_len > 0) {
scan = std::min(total, static_cast<size_t>(cfg.max_packet_len) + 1);
}
if (parse_tmp.size() < total) parse_tmp.resize(total);
buf.Peek(parse_tmp.data(), total);
if (parse_tmp.size() < scan) parse_tmp.resize(scan);
buf.Peek(parse_tmp.data(), scan);

// 查找 '\n'
size_t line_end = 0;
bool found = false;
for (size_t i = 0; i < total; ++i) {
for (size_t i = 0; i < scan; ++i) {
if (parse_tmp[i] == '\n') {
line_end = i;
found = true;
break;
}
}
if (!found) return false;
if (!found) {
// No newline in the first max_packet_len(+1) bytes → oversize line
if (cfg.max_packet_len > 0 && total > static_cast<size_t>(cfg.max_packet_len)) {
out_error = true;
}
return false;
}
if (cfg.max_packet_len > 0 && line_end > static_cast<size_t>(cfg.max_packet_len)) {
out_error = true;
return false;
}

// 消费包含 '\n' 在内的所有字节
buf.Read(parse_tmp.data(), line_end + 1);
Expand Down
21 changes: 21 additions & 0 deletions test/lua/json/test_json_edge.lua
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,24 @@ function test_encode_large_int_key()
if not string.find(s, "9999999") then return 0 end
return 1
end

-- uint64 > INT64_MAX must not wrap to a negative int64
function test_decode_uint64_overflow()
local v = json.decode("9223372036854775808")
if type(v) ~= "number" then return 0 end
if v < 0 then return 0 end
-- Exact int64 can't hold 2^63; expect double approximation
if v < 9.223372036854e18 then return 0 end
return 1
end

-- JSON strings may contain embedded NUL via \u0000
function test_decode_embedded_nul()
local v = json.decode('"a\\u0000b"')
if type(v) ~= "string" then return 0 end
if #v ~= 3 then return 0 end
if string.byte(v, 1) ~= 97 then return 0 end
if string.byte(v, 2) ~= 0 then return 0 end
if string.byte(v, 3) ~= 98 then return 0 end
return 1
end
22 changes: 22 additions & 0 deletions test/test_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,3 +682,25 @@ TEST(test_json, encode_large_int_key) {
EXPECT_EQ(ret, 1);
FakeluaDeleteState(s);
}

TEST(test_json, decode_uint64_overflow) {
State *s = FakeluaNewState();
ASSERT_NE(s, nullptr);
CompileConfig config;
CompileFile(s, "./json/test_json_edge.lua", config);
int64_t ret = 0;
CallAll(s, "JsonTest.test_decode_uint64_overflow", ret);
EXPECT_EQ(ret, 1);
FakeluaDeleteState(s);
}

TEST(test_json, decode_embedded_nul) {
State *s = FakeluaNewState();
ASSERT_NE(s, nullptr);
CompileConfig config;
CompileFile(s, "./json/test_json_edge.lua", config);
int64_t ret = 0;
CallAll(s, "JsonTest.test_decode_embedded_nul", ret);
EXPECT_EQ(ret, 1);
FakeluaDeleteState(s);
}
Loading