8.8 KiB
libsqlite3 模块安全审计报告
审计日期: 2026-08-06
审计范围: libsqlite3/inc/us/Sqlite3.hpp、Makefile、_set.sh、README.md
代码版本: main 分支
修复进度
| 优先级 | 总数 | 已修复 | 未修复 |
|---|---|---|---|
| P0 | 3 | 2 | 1 |
| P1 | 4 | 1 | 3 |
| P2 | 5 | 2 | 3 |
| 合计 | 12 | 5 | 7 |
模块现状
libsqlite3 当前实际是一个单头文件 C++ RAII 封装库,只提供 qing::Sqlite3(数据库连接)和 qing::Sqlite3_stmt(预编译语句)。
- 整个模块没有任何单元测试或示例
Makefile已经完全注释掉编译规则,只保留install/uninstall_set.sh只调用make install,不编译任何产物- 全项目唯一引用方是
gateway/src/main.cpp(#include <us/Sqlite3.hpp>),但对应的使用代码已被注释掉 db2直接使用原生sqlite3.hAPI,没有使用本封装
结论:该模块目前处于事实上的废弃/未使用状态,但头文件仍被 _inst.sh 安装到系统目录,存在被误用的风险。
P0 — 严重问题
P0-1: 未定义拷贝/移动语义,存在 double free / use-after-free
-
位置:
Sqlite3.hppL7-60(Sqlite3_stmt)、L62-97(Sqlite3) -
当前状态: ✅ 已修复(2026-08-06)
-
修复: 为
Sqlite3和Sqlite3_stmt显式= delete拷贝构造和拷贝赋值,并实现noexcept移动构造/移动赋值,移动后把源对象指针置nullptr。析构函数先判空再释放,彻底消除 double free 和 use-after-free 风险。修复前代码问题:
Sqlite3_stmt stmt(std::string& sql) { // L91 return Sqlite3_stmt(db, sql.c_str()); // L92 }return依赖 NRVO/copy elision。若编译器未成功省略,会调用拷贝构造,触发 double free。即使现代编译器通常能省略,代码仍不是良构且安全的。同理,
Sqlite3类也持有sqlite3* db原始指针,未禁用拷贝/移动,也存在 double free 风险。
P0-2: Sqlite3::~Sqlite3() 忽略 sqlite3_close 返回值
- 位置:
Sqlite3.hppL74-L76 - 当前状态: ✅ 已修复(2026-08-06)
- 修复:
~Sqlite3()改用sqlite3_close_v2(db),它会在所有 prepared statements 释放后才真正关闭数据库,避免SQLITE_BUSY导致的数据库未关闭问题。同时析构前判空,防止重复关闭。
P0-3: Sqlite3::exec 使用字符串 SQL,无参数化绑定接口
-
位置:
Sqlite3.hppL78-L89 -
当前状态: ❌ 未修复
-
详情:
bool exec(const std::string& sql)接收完整 SQL 字符串。虽然函数本身不拼接,但调用者容易把外部输入拼进 SQL,且该函数没有提供参数化绑定版本,迫使调用者自行处理转义或只能用于硬编码 SQL。 -
修复建议: 至少提供文档警告“禁止拼接用户输入”;更推荐提供参数化版本或强制仅用于常量 SQL。
P1 — 高危问题
P1-1: stmt() 参数应为 const std::string&
-
位置:
Sqlite3.hppL91 -
当前状态: ✅ 已修复(2026-08-06)
-
修复:
stmt()参数改为const std::string& sql,现在可以传入字符串字面量和临时对象:auto s = db.stmt("SELECT 1"); // 现在可以编译
P1-2: bind_int 只支持 32 位 int
-
位置:
Sqlite3.hppL21-L27 -
当前状态: ❌ 未修复
-
详情:
bind_int调用sqlite3_bind_int,只能绑定 32 位整数。SQLite 的INTEGER可以存 64 位,如果用户要绑定int64_t,会静默截断。 -
修复建议: 增加
bind_int64(int idx, int64_t data)使用sqlite3_bind_int64。
P1-3: 缺少 bind_null、bind_double、bind_blob 等接口
-
位置:
Sqlite3.hppL21-L35 -
当前状态: ❌ 未修复
-
详情:
当前只提供
bind_int和bind_text,缺少bind_null、bind_blob、bind_double、bind_int64。功能不完整,无法满足通用数据库操作。 -
修复建议: 补充上述接口。
P1-4: 异常信息丢失 SQLite 错误详情
-
位置:
Sqlite3.hppL9-L15、L21-L35、L65-L70 -
当前状态: ❌ 未修复
-
详情:
所有异常都使用固定字符串,例如:
throw std::runtime_error("Failed to prepare sql stmt");没有把
sqlite3_errmsg(db)或错误码带入异常。调用者无法区分“语法错误”“无此表”“只读数据库”等不同错误。 -
修复建议: 将
sqlite3_errmsg(db)和错误码拼入异常信息。
P2 — 中危问题
P2-1: Sqlite3_stmt 成员 stmt 未显式初始化
- 位置:
Sqlite3.hppL58-L59 - 当前状态: ✅ 已修复(2026-08-06)
- 修复:
Sqlite3和Sqlite3_stmt的成员指针均显式初始化为nullptr,配合移动语义中的置空操作,避免野指针和重复释放。
P2-2: read_text 返回指针生命周期未文档化
-
位置:
Sqlite3.hppL54-L56 -
当前状态: ❌ 未修复
-
详情:
read_text返回const unsigned char*,生命周期依赖于 statement。statement finalize 后指针失效,头文件中没有任何文档说明。 -
修复建议: 在注释中明确说明返回指针的生命周期;或提供返回
std::string的安全版本。
P2-3: 缺少类型安全读取接口
-
位置:
Sqlite3.hppL49-L56 -
当前状态: ❌ 未修复
-
详情:
只有
read_int和read_text,缺少read_int64、read_double、read_blob、column_type、column_count等常用接口。 -
修复建议: 补充上述接口。
P2-4: exec 中 errMsg 未初始化且存在危险注释
- 位置:
Sqlite3.hppL78-L85 - 当前状态: ✅ 已修复(2026-08-06)
- 修复:
errMsg显式初始化为nullptr,并删除危险注释//sqlite3_close(db),避免未来被误恢复导致 SQL 执行错误时关闭数据库连接。
P2-5: Makefile install 目标文案错误
-
位置:
libsqlite3/MakefileL27 -
当前状态: ❌ 未修复
-
详情:
install: @echo "install UniServe_libth..."文案是
libth,明显是从libth/Makefile复制过来的,应改为UniServe_libsqlite3。 -
修复建议: 修正 echo 文本。
P2-6: Makefile 与 _set.sh 完全无构建/测试步骤
-
位置:
libsqlite3/Makefile、libsqlite3/_set.sh -
当前状态: ❌ 未修复
-
详情:
- 全部编译规则被注释
_set.sh只执行make install- 头文件库不编译
.so尚可理解,但连编译时语法检查都没有
-
修复建议: 至少保留一个
check或test目标,编译一个最小测试文件验证头文件能正常包含和实例化。
已有安全机制(正确实现)
- RAII 思路正确 — 用析构函数自动
sqlite3_finalize/sqlite3_close SQLITE_TRANSIENT使用正确 —bind_text传-1和SQLITE_TRANSIENT,让 SQLite 复制数据,避免悬垂指针step()返回值语义清晰 — 返回bool表示是否还有数据行- 异常路径不吞异常 — 构造/绑定失败直接抛异常,不返回错误码让调用者忽略
- 头文件仅暴露最小接口 — 没有暴露原始
sqlite3*/sqlite3_stmt*,封装完整
修复路线图
立即(24小时)
- P0-1: 为
Sqlite3和Sqlite3_stmt显式 delete 拷贝、实现移动构造/赋值 - P0-2:
~Sqlite3()改用sqlite3_close_v2或检查返回值 - P0-3: 为
exec增加文档警告,限制仅用于硬编码 SQL
短期(72小时)
- P1-1:
stmt()参数改为const std::string& - P1-2: 增加
bind_int64 - P1-3: 补充
bind_null、bind_double、bind_blob - P1-4: 异常信息携带
sqlite3_errmsg和错误码
中期
- P2-1: 成员指针初始化为
nullptr - P2-2:
read_text生命周期文档化,或提供std::string版本 - P2-3: 补充读取接口(int64/double/blob/column_type/column_count)
- P2-4: 初始化
errMsg并删除危险注释 - P2-5: 修正
Makefileecho 文案 - P2-6: 增加最小编译检查目标
审计方法
- 通读
libsqlite3/inc/us/Sqlite3.hpp全部 98 行实现 - 审查
libsqlite3/Makefile、_set.sh、README.md的安装与构建流程 - 使用
grep全项目查找Sqlite3.hpp、Sqlite3、Sqlite3_stmt、libus_sqlite3的引用情况 - 对照 C++ 资源管理规则检查拷贝/移动/析构语义
- 对照 SQLite3 C API 文档检查 API 使用正确性(绑定、关闭、finalize)
- 评估接口完整性(参数化绑定、类型绑定、读取接口)