fix(db2): 修复第六轮审计定位的 4 个问题并更新审计文档

- ND-1(P2): start() 引入 CleanupGuard RAII 守卫,rpc.start() 异常路径
  下清理线程被优雅回收,消除 std::terminate 硬崩溃
- ND-4(P3): where/limit/offset 类型不符立即抛错,不再静默忽略
- ND-5(P3): _bind_value 对超 INT64_MAX 的 uint64 抛错,防截断溢出
- ND-6(P3): in 运算符非数组值抛错,仅空数组保留恒假语义
- 记录 2 项已知限制 ND-2/ND-3(无结果集上限、无 WHERE 全表更新/删除)

审计文档更新至 26/26(100%) 修复、30 项安全机制;clang++/g++ 双编译验证通过
This commit is contained in:
qing committed 2026-08-07 21:42:06 +08:00
1 parent 3ace5931e6
commit edbb7ef1c6
2 files changed
+104 -19

No files matched your search

+32 -10
View File
@@ -22,6 +22,7 @@
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
@@ -341,6 +342,18 @@ public:
_cleanup_running = true;
_cleanup_thread = std::thread(&DB2Server::_cleanup_loop, this);
// RAII 作用域守卫:无论 rpc.start() 正常返回还是抛出(socket/bind/listen 失败、
// 事件循环异常),都停止并 join 清理线程。否则异常路径下 _cleanup_thread 保持
// joinable,栈展开时 std::thread 析构会触发 std::terminate() 硬崩溃,掩盖真实错误。
struct CleanupGuard {
DB2Server& s;
~CleanupGuard() {
s._cleanup_running = false;
s._cleanup_cv.notify_all();
if (s._cleanup_thread.joinable()) s._cleanup_thread.join();
}
} guard{*this};
// 启动 RPC 服务
MsgServerConfig srv_cfg;
srv_cfg.workers = _cfg.workers;
@@ -356,11 +369,6 @@ public:
});
rpc.start();
// rpc.start() 返回后停止清理线程
_cleanup_running = false;
_cleanup_cv.notify_all();
if (_cleanup_thread.joinable()) _cleanup_thread.join();
}
private:
@@ -470,7 +478,10 @@ private:
} else if (jv.is_int64()) {
sqlite3_bind_int64(stmt, idx, jv.as_int64());
} else if (jv.is_uint64()) {
sqlite3_bind_int64(stmt, idx, static_cast<sqlite3_int64>(jv.as_uint64()));
std::uint64_t u = jv.as_uint64();
if (u > static_cast<std::uint64_t>(INT64_MAX))
throw std::runtime_error("uint64 value exceeds signed 64-bit integer range");
sqlite3_bind_int64(stmt, idx, static_cast<sqlite3_int64>(u));
} else if (jv.is_double()) {
sqlite3_bind_double(stmt, idx, jv.as_double());
} else if (jv.is_bool()) {
@@ -545,7 +556,9 @@ private:
parts.push_back(col + " LIKE ?");
wc.params.push_back(_coerce_value(ci, op_val));
} else if (op == "in") {
if (!op_val.is_array() || op_val.as_array().empty()) {
if (!op_val.is_array())
throw std::runtime_error("'in' operator requires an array value");
if (op_val.as_array().empty()) {
parts.push_back("0");
continue;
}
@@ -611,6 +624,11 @@ private:
std::vector<ColInfo> cols = _get_schema(table);
// 'where' 存在但非对象时立即报错,不静默忽略(与 action/columns 校验一致)
if (auto wit = req.find("where"); wit != req.end() && !wit->value().is_object()) {
throw std::runtime_error("'where' must be an object");
}
if (action == "select") return _do_select(req, table, cols);
if (action == "insert") return _do_insert(req, table, cols);
if (action == "update") return _do_update(req, table, cols);
@@ -673,14 +691,18 @@ private:
if (!order.empty()) sql += " ORDER BY " + order;
}
// limit / offset
if (auto it = req.find("limit"); it != req.end() && it->value().is_int64()) {
// limit / offset(类型不符立即报错,不静默忽略,与 action 校验一致)
if (auto it = req.find("limit"); it != req.end()) {
if (!it->value().is_int64())
throw std::runtime_error("limit must be an integer");
std::int64_t limit = it->value().as_int64();
if (limit < 0)
throw std::runtime_error("limit must be non-negative");
sql += " LIMIT " + std::to_string(limit);
}
if (auto it = req.find("offset"); it != req.end() && it->value().is_int64()) {
if (auto it = req.find("offset"); it != req.end()) {
if (!it->value().is_int64())
throw std::runtime_error("offset must be an integer");
std::int64_t offset = it->value().as_int64();
if (offset < 0)
throw std::runtime_error("offset must be non-negative");