fix(libmsg): 修复安全审计发现的三个漏洞

- CWE-362: _remove_client() 关闭 fd 前获取 per-connection send_lock,
  防止 broadcast/reply 向 OS 复用的 fd 发送数据到错误客户端
- CWE-400: workers>0 模式下消息接收移至 worker 线程,
  事件循环不再阻塞 pmsg_recv,防 Slowloris DoS
- CWE-209: 扩展 _sanitize_error 路径正则为 /\S+,
  覆盖含空格/括号/@/~ 等特殊字符的内部路径,防信息泄露
This commit is contained in:
qing committed 2026-08-06 17:16:33 +00:00
1 parent 61a0e4cf7a
commit e92ef05ed0
2 files changed
+45 -19

No files matched your search

+43 -16
View File
@@ -249,6 +249,7 @@ private:
int fd; int fd;
std::string addr; std::string addr;
std::string message; std::string message;
bool needs_recv = false; // true: worker 负责 pmsg_recv;false: 消息已就绪
}; };
void _worker_loop() { void _worker_loop() {
@@ -268,6 +269,19 @@ private:
} }
if (!has_task) continue; if (!has_task) continue;
// workers > 0 模式下,消息接收在 worker 线程中完成(防事件循环阻塞 CWE-400)
if (task.needs_recv) {
try {
task.message = pmsg_recv(task.fd, _config.recv_timeout);
} catch (const MsgException&) {
std::lock_guard<std::mutex> flk(_failed_mtx);
_failed.insert(task.fd);
std::lock_guard<std::mutex> blk(_busy_mtx);
_busy.erase(task.fd);
continue;
}
}
// 执行回调 // 执行回调
bool failed = false; bool failed = false;
try { try {
@@ -418,17 +432,15 @@ private:
addr = it->second; addr = it->second;
} }
std::string message;
try {
// deadline 模式:header+payload 共享总超时,防 Slowloris
message = pmsg_recv(fd, _config.recv_timeout);
} catch (const MsgException&) {
_remove_client(fd);
return;
}
if (_config.workers == 0) { if (_config.workers == 0) {
// 同步模式:在主线程执行回调 // 同步模式:在主线程接收并执行回调
std::string message;
try {
message = pmsg_recv(fd, _config.recv_timeout);
} catch (const MsgException&) {
_remove_client(fd);
return;
}
try { try {
_on_message(fd, addr, std::move(message)); _on_message(fd, addr, std::move(message));
} catch (const MsgConnectionException&) { } catch (const MsgConnectionException&) {
@@ -441,7 +453,8 @@ private:
return; return;
} }
// 线程池模式:标记 busy,提交任务 // 线程池模式:标记 busy,提交任务(由 worker 负责 recv + callback,
// 事件循环线程不阻塞接收,防 Slowloris DoS CWE-400)
{ {
std::lock_guard<std::mutex> blk(_busy_mtx); std::lock_guard<std::mutex> blk(_busy_mtx);
_busy.insert(fd); _busy.insert(fd);
@@ -455,7 +468,7 @@ private:
_remove_client(fd); _remove_client(fd);
return; return;
} }
_pool_queue.push(Task{fd, std::move(addr), std::move(message)}); _pool_queue.push(Task{fd, std::move(addr), {}, true});
} }
_pool_cv.notify_one(); _pool_cv.notify_one();
} }
@@ -476,10 +489,24 @@ private:
} }
void _remove_client(int fd) { void _remove_client(int fd) {
std::lock_guard<std::mutex> clk(_clients_mtx); std::shared_ptr<std::mutex> send_lock;
if (_clients.erase(fd) == 0) return; // 已被移除,防止 double-close {
_send_locks.erase(fd); std::lock_guard<std::mutex> clk(_clients_mtx);
::close(fd); if (_clients.erase(fd) == 0) return; // 已被移除,防止 double-close
auto it = _send_locks.find(fd);
if (it != _send_locks.end()) {
send_lock = it->second;
_send_locks.erase(it);
}
}
// 持有 send_lock 后再关闭 fd:防止 broadcast/reply 在 fd 关闭后向 OS
// 复用的新连接发送数据(CWE-362)。
if (send_lock) {
std::lock_guard<std::mutex> slk(*send_lock);
::close(fd);
} else {
::close(fd);
}
} }
void _close_all_clients() { void _close_all_clients() {
+2 -3
View File
@@ -179,9 +179,8 @@ private:
static std::string _sanitize_error(const std::string& msg) { static std::string _sanitize_error(const std::string& msg) {
if (msg.empty()) return msg; if (msg.empty()) return msg;
try { try {
// 匹配以 / 开头的绝对路径段,替换为 <redacted> // 匹配以 / 开头的绝对路径(含特殊字符如空格、括号、@、~ 等),替换为 <redacted>
// 不返回函数堆栈、SQL、文件路径等内部细节给客户端 static const std::regex path_re(R"(/\S+(?:/\S*)*)");
static const std::regex path_re(R"(/[A-Za-z0-9_.\-]+(?:/[A-Za-z0-9_.\-]+)*)");
return std::regex_replace(msg, path_re, "<redacted>"); return std::regex_replace(msg, path_re, "<redacted>");
} catch (...) { } catch (...) {
return msg; // 正则异常时返回原文 return msg; // 正则异常时返回原文