十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ZeroTierOne 内置 hiredis 1.0.2 版本演进解析:从 CVE-2021-32765 安全修复到 RESP3、SSL 与分配器注入

ZeroTierOne 内置 hiredis 1.0.2 版本演进解析:从 CVE-2021-32765 安全修复到 RESP3、SSL 与分配器注入 ZeroTierOne 内置 hiredis 1.0.2 版本演进解析从 CVE-2021-32765 安全修复到 RESP3、SSL 与分配器注入【免费下载链接】ZeroTierOneA Smart Ethernet Switch for Earth项目地址: https://gitcode.com/GitHub_Trending/ze/ZeroTierOne导读本文以 ZeroTierOne 仓库中随附的 hiredis 1.0.2 版本ext/hiredis-1.0.2/CHANGELOG.md为核心梳理该 C 语言 Redis 客户端从 0.10 到 1.0.2 的关键版本演进脉络重点解读 1.0.0 稳定版引入的 RESP3、SSL/TLS 连接、运行时分配器注入、独立连接/命令超时等新特性以及 1.0.1/1.0.2 针对 CVE-2021-32765 的安全修复与 SONAME 回退事件。文章结合仓库内 hiredis 源码ext/hiredis-1.0.2/hiredis.h、适配器ext/hiredis-1.0.2/adapters/、示例ext/hiredis-1.0.2/examples/以及 ZeroTierOne 中央控制器对 Redis 的实际使用方式nonfree/controller/帮助读者理解 hiredis 的 API 变迁、升级注意事项以及它如何在 ZeroTierOne 的控制器集群场景中发挥作用。一、hiredis 是什么为什么 ZeroTierOne 会随附它hiredis 是一款极简风格的 C 语言 Redis 客户端库它只提供对 Redis 协议的最小支持却通过 printf 风格的命令格式化 API 提供了远比其代码量所暗示的更高层的使用体验。它不针对每条 Redis 命令提供显式绑定而是把构造命令 解析回复两件事做到极致并附带一套与 I/O 层完全解耦的流式回复解析器可以复用于各类高层语言绑定的高效回复解析。ZeroTierOne 在ext/目录下随附了多个版本的 hiredis 及其上层 C 封装 redis-plus-plusext/hiredis-0.14.1/1.0.0 之前的 0.14.1 版本ext/hiredis-1.0.2/本文章核心版本包含完整的 hiredis 1.0.2 源码树ext/redis-plus-plus-1.1.1/ 与 ext/redis-plus-plus-1.3.3/基于 hiredis 的 C 封装。按照 ext/README.md 的说明ext/子目录存放的是在系统不提供这些库的平台上被编译进二进制文件的捆绑第三方库以及预编译二进制。也就是说hiredis 在这里扮演的是 ZeroTierOne 中央控制器Central Controller在本地系统缺少该库时可直接编译链接的兜底依赖。ZeroTierOne 的中央控制器在实现基于 Redis 的流Stream通知、发布订阅与状态写入时实际使用的是 redis-plus-plus 这一 C 封装其底层正是 hiredis例如 nonfree/controller/RedisListener.cpp 中通过sw::redis::Redis/sw::redis::RedisCluster的xread、xdel消费 Redis Stream并通过RedisConfig见 nonfree/controller/Redis.hpp中的hostname、port、password、clusterMode配置连接。因此hiredis 的版本选择直接影响 ZeroTierOne 控制器在 Ubuntu 22.04、CentOS 8 等目标平台上能否稳定、安全地对接 Redis本文对 1.0.2 版本变更的解读对理解该依赖选型具有直接参考价值。二、版本时间线与 1.0.2 的定位结合 ext/hiredis-1.0.2/CHANGELOG.md 可以还原 hiredis 从 0.10 到 1.0.2 的完整演进时间线版本发布日期定位与关键内容1.0.22021-10-07修复 CVE-2021-32765同时把 SONAME 回退到正确的1.0.01.0.12021-10-04安全修复版修复 CVE-2021-32765但错误地提升了 SONAME官方明确建议改用 1.0.21.0.02020-08-03首个稳定版引入 RESP3、SSL 连接、分配器注入、独立超时、更好的 Windows 支持1.0.0-rc12020-07-29与 1.0.0 之间无代码差异直接参见 1.0.0 变更说明0.14.12020-03-13安全补丁版加入安全分配包装CVE-2020-71050.14.02018-09-25redisReply.len改为size_t、长度溢出防护、移除旧宏别名、DEBUG更名DEBUG_FLAGS0.13.x2015修复异步回复处理内存泄漏、增加多种事件适配器、引入 Windows 兼容层0.12.x2015KeepAlive、libuv 适配器、IPv6、redisConnectFd()/redisFreeKeepFd()0.11.x—最大 multi-bulk 深度提升到 7、读缓冲从 2k 提升到 16k、改用 poll(2)0.10.x—Makefile 重构、修复若干内存泄漏需要特别注意的是 1.0.1 与 1.0.2 之间的关系1.0.1 是安全修复版本应修复 CVE-2021-32765但它错误地提升了库的 SONAME共享库版本标识1.0.2 则是1.0.0 加上 CVE-2021-32765 修复除此之外完全相同的版本并把 SONAME 回退到正确的1.0.0。因此 changelog 中明确以红色标注不要使用 1.0.1请使用 1.0.2。从仓库源码可以印证这一点ext/hiredis-1.0.2/include/hiredis/hiredis.h 中同时定义了HIREDIS_MAJOR 1、HIREDIS_MINOR 0、HIREDIS_PATCH 2与HIREDIS_SONAME 1.0.0即代码版本是 1.0.2而 SONAME 保持为 1.0.0——这正是 1.0.2 版本的核心修正。2.1 安全背景CVE-2021-32765CVE-2021-32765 由 Microsoft Security Vulnerability Research 发现修复提交来自 Yossi Gottliebcommit 76a7b100该提交在仓库内体现为 hiredis 1.0.2 源码树相对于 1.0.0 的唯一功能差异。由于 1.0.2 与 1.0.0 仅在安全修复这一点上不同任何使用 hiredis 1.0.0 且通过 SSL/TLS 访问 Redis 的部署都建议升级到 1.0.2 以获得该修复。三、1.0.0 稳定版的四项核心能力1.0.0 是 hiredis 历史上第一个稳定版changelog 明确列出了四大新特性RESP3 协议支持、SSL/TLS 连接、运行时分配器注入、更好的 Windows 支持此外还引入了独立的连接超时与命令超时。以下结合仓库源码逐项展开。3.1 RESP3面向 Redis 6 的新协议支持RESP3 是 Redis 6 引入的回复协议相比 RESP2 增加了多种数据类型。hiredis 1.0.0 通过 #697、#805、#819、#841 等 PR 将 RESP3 支持从 Redis 移植到 hiredis并在 ext/hiredis-1.0.2/include/hiredis/hiredis.h 的redisReply结构中落地新增double dval字段承载双精度浮点值、char vtype[4]承载 verbatim 字符串的 3 字符内容类型如txt同时size_t len记录字符串长度、char *str统一承载 ERROR、STRING、VERB、DOUBLE 等类型的字符串表示。RESP3 新增的回复类型包括REDIS_REPLY_DOUBLE双精度浮点数值以字符串形式保存在str中可用strtod等转换REDIS_REPLY_BOOL布尔值保存在integer成员中0或1REDIS_REPLY_MAP元素数恒为偶数的数组功能上与REDIS_REPLY_ARRAY等价REDIS_REPLY_SET元素唯一的数组REDIS_REPLY_PUSHRedis 可自发产生的数组至少包含两个子元素第一个是 PUSH 类型如message、invalidate第二个是 PUSH 载荷子数组REDIS_REPLY_ATTR结构与 MAP 相同、用于携带回复元数据的类型截至 Redis 6.0.6 尚未实际使用REDIS_REPLY_BIGNUM任意大整数以字符串形式保存在str中REDIS_REPLY_VERBverbatim 字符串载荷在str中类型信息在vtype中。与 RESP3 强相关的是 PUSH 消息处理。Redis 6 引入的 PUSH 回复协议符号可以随时自发到达因此必须用回调方式处理。1.0.0 的默认行为是在redisContext与redisAsyncContext上安装默认处理器自动拦截并释放 PUSH 回复保证升级到 Redis 6 RESP3 后存量代码无需改动即可工作。仓库中的 ext/hiredis-1.0.2/examples/example-push.c 就是展示自定义 PUSH 处理器用法的官方示例。需要自定义行为时有两种方式在redisOptions中设置push_cb同步或async_push_cb异步并用redisConnectWithOptions/redisAsyncConnectWithOptions建立连接连接建立后调用redisSetPushCallback/redisAsyncSetPushCallback动态设置。这两类回调都会返回当前已配置的处理器便于覆盖后再恢复原值。如果希望 hiredis 完全不自动拦截、释放 PUSH 回复例如用于 MONITOR 或 SUBSCRIBE 这类阻塞式redisGetReply循环可以设置REDIS_OPT_NO_PUSH_AUTOFREE标志该标志定义于 ext/hiredis-1.0.2/include/hiredis/hiredis.h值为0x08并将回调置空或在连接后调用redisSetPushCallback(context, NULL)。注意无处理器时一次redisCommand可能产生多条回复仅适用于阻塞式读取场景。3.2 SSL/TLS 连接支持1.0.0 通过 #645、#699、#702、#708、#711、#821 等 PR 完整引入 SSL 支持并在 #821 中用新的 SSL API 取代了旧的redisSecureConnection()。构建时需显式启用默认关闭make USE_SSL1这要求系统具备 OpenSSL 开发包包含头文件。启用后SSL 支持被打入独立的libhiredis_ssl.a/libhiredis_ssl.so库原始libhiredis不受影响因此不会给不用的用户引入额外依赖。使用侧需要额外包含hiredis_ssl.h并链接libhiredis_ssl、libhiredis、-lssl -lcrypto。hiredis 的 SSL 实现建立在普通redisContext/redisAsyncContext之上先建立 TCP 连接再发起 SSL/TLS 握手。仓库提供了 ext/hiredis-1.0.2/hiredis_ssl.h 以及配套示例 ext/hiredis-1.0.2/examples/example-ssl.c 和 ext/hiredis-1.0.2/examples/example-libevent-ssl.c后者演示了 SSL 与 libevent 适配器组合使用的方式。初始化有两种路径直接使用 OpenSSL API 初始化全局状态并自行创建SSL_CTX */SSL *然后用redisInitiateSSL()完成握手使用 hiredis 提供的封装redisSSLContext持有配置可跨多个上下文复用调用redisCreateSSLContext()创建、redisInitiateSSLWithContext()完成握手。典型的封装式用法为先redisInitOpenSSL()初始化全局 OpenSSL 状态仅在应用其他位置未初始化时调用一次再redisCreateSSLContext()依次传入 CA 证书包文件、可信证书目录、客户端证书文件、客户端私钥文件、SNI 服务器名均可选出错时通过redisSSLContextGetError(ssl_error)获取错误描述随后redisConnect()建立普通连接并检查err最后redisInitiateSSLWithContext()协商 TLS。3.3 运行时分配器注入1.0.0 通过 #800 引入运行时分配器注入allocator injection并在 #824 中补充了文档与测试。hiredis 使用一组定义于 ext/hiredis-1.0.2/alloc.h 的函数指针结构hiredisAllocFuncs持有当前配置的分配/释放函数默认指向 libc 的malloc、calloc、realloc、strdup、free。该特性同时解决了 #769undefined reference to hi_malloc这一困扰定制分配场景的问题——所有分配统一走包装层OOM 等情形可在包装函数内统一处理。覆盖方式如下hiredisAllocFuncs myfuncs { .mallocFn my_malloc, .callocFn my_calloc, .reallocFn my_realloc, .strdupFn my_strdup, .freeFn my_free, }; /* 覆盖分配器函数返回当前分配器便于保存与恢复 */ hiredisAllocFuncs orig hiredisSetAllocators(myfuncs); /* 恢复为默认 libc 函数 */ hiredisResetAllocators();在 ZeroTierOne 中该机制为控制器对接自研内存管理或内存统计例如 nonfree/controller/ 中依赖 jemalloc 的场景提供了干净的接入点。3.4 更完善的 Windows 支持通过 #652MinGW 支持与 #663Windows CI等 PR1.0.0 显著改善了 Windows 支持后续又有多轮修复如 #845 改用_WIN32宏、#846 的 Windows 质量改进、#848 的 MinGW 编译修复。仓库 ext/hiredis-1.0.2/appveyor.yml 保留了 Windows 持续集成配置ext/hiredis-1.0.2/win32.h 与 ext/hiredis-1.0.2/sockcompat.c/ext/hiredis-1.0.2/sockcompat.h 提供了 BSD socket 与 WSA API 差异的兼容层。值得注意的还有redisFD类型在 Windows 与 Unix 上的差异在 Unix 上redisFD是普通intREDIS_INVALID_FD为-1在 Windows 上则是SOCKET32 位unsigned long或 64 位unsigned long longREDIS_INVALID_FD为全 1这一点已内建在 ext/hiredis-1.0.2/include/hiredis/hiredis.h 的宏定义中跨平台代码可直接使用redisFD与REDIS_INVALID_FD而无需关心平台差异。四、1.0.0 的破坏性变更与升级指南4.1redisOptions超时字段拆分1.0.0 将原先单一的options-timeout拆分为两个字段connect_timeout连接超时command_timeout命令超时。旧代码中所有使用options-timeout的地方都必须改为options-connect_timeout或按语义改用command_timeout。这一变更源自 #722redisConnectWithOptions不应设置命令超时与 #829。在 ext/hiredis-1.0.2/include/hiredis/hiredis.h 的redisOptions结构中两个字段并列定义且command_timeout可在运行时通过redisSetTimeout/redisAsyncSetTimeout更新这一点也写入了头文件注释。4.2 协议长度校验收紧Bulk 与 multi-bulk 的长度小于-1或大于LLONG_MAX现在被视为协议错误这与 RESP 规范保持一致在 32 位平台上上限进一步降低为SIZE_MAX。这一规则在 0.14.0 已经引入源自 Justin Brewer 的修复1.0.0 沿用了它。相关改动还包括sdsrange整数溢出修复#827、#830。4.3createArray长度参数改为size_tredisReplyObjectFunctions.createArray的长度参数类型从int改为size_t#597任何自定义回复对象工厂的实现者都需要同步修改函数签名。4.4 其他结构调整redisContext新增privdata/free_privdata用户自定义数据及析构函数#855与privctxhiredis 内部管理 SSL 连接的指针两个成员1.0.0 同步更新了随附的sds库到上游最新版本与 Redis 对齐这会让链接旧版 hiredis 0.13 的应用不兼容回复对象函数可通过redisReader的fn字段定制如 hiredis-rb 用其创建 Ruby 对象解析器默认最大嵌套深度为 7 的旧限制已在 1.0.0 移除#794、#797。4.5 更早版本升级提示0.13.x → 0.14.x → 1.0.0从 0.13.x 升级到 0.14.x 时需要把redisReply.len的用途从int语义调整为size_t语义进行比较0.14.0 还移除了redisReplyReaderCreate、redisReplyReaderFree、redisReplyReaderFeed、redisReplyReaderGetReply、redisReplyReaderSetPrivdata、redisReplyReaderGetObject、redisReplyReaderGetError等旧别名详见 ext/hiredis-1.0.2/CHANGELOG.md 中的替换表并将 Makefile 的DEBUG变量更名为DEBUG_FLAGS以避免与用户环境中其他软件的DEBUG变量冲突。五、hiredis 在 ZeroTierOne 控制器中的实际落地虽然 ZeroTierOne 核心网络栈本身不直接依赖 Redis但它的中央控制器Central Controller位于 nonfree/controller/在集群化部署中需要 Redis 支撑。控制器通过 redis-plus-plus底层为 hiredis访问 Redis这一点可以从 nonfree/controller/CMakeLists.txt 中看到其对redis::redis_static静态目标的依赖而 redis-plus-plus 又构建在 hiredis 之上。具体使用形态包括配置结构nonfree/controller/Redis.hpp 定义了RedisConfig包含hostname、port、password、clusterMode四个字段其中clusterMode用于区分单机 Redis 与 Redis Cluster监听器nonfree/controller/RedisListener.cpp 中的RedisNetworkListener与RedisMemberListener分别消费网络事件流与成员事件流在集群模式下使用sw::redis::RedisCluster非集群模式下使用sw::redis::Redis通过xread阻塞读取指定 Stream key处理成功后用xdel删除已消费条目并统计Metrics::redis_net_notification/redis_mem_notification等指标发布订阅与状态写回控制器还借助 Redis 的发布订阅与 Stream 机制实现控制器变更通知与状态上报见 nonfree/controller/PubSubListener.cpp、nonfree/controller/PubSubWriter.cpp 与 nonfree/controller/RedisStatusWriter.cpp 等。从源码结构看ZeroTierOne 之所以在ext/下同时捆绑 hiredis 0.14.1 与 1.0.2正是为了在不同 Linux 发行版与构建环境下提供可选的依赖来源——如 nonfree/controller/README_CENTRAL_CONTROLLER.md 所列的libhiredis-dev等系统包当系统不提供该库时ext/内的源码即可作为后备参与编译。六、测试与构建配套仓库内的 hiredis 1.0.2 源码树自带完整的测试与构建设施方便读者在本地验证本文所述行为测试程序ext/hiredis-1.0.2/test.c 覆盖了同步 API、异步 API、RESP3 回复类型、PUSH 回调、SSL、分配器注入、超时、pipelining 等核心路径ext/hiredis-1.0.2/test.sh 为测试执行脚本示例程序ext/hiredis-1.0.2/examples/ 提供example.c同步基础、example-ssl.cSSL、example-push.cRESP3 PUSH、example-libev.c/example-libevent.c/example-libuv.c/example-ae.c/example-glib.c/example-ivykis.c/example-macosx.c等各事件库适配器示例事件适配器ext/hiredis-1.0.2/adapters/ 内含 libev、libevent、libuv、aeRedis 自带事件库、glib、ivykis、macosx 的绑定头文件构建系统ext/hiredis-1.0.2/Makefile 与 ext/hiredis-1.0.2/CMakeLists.txt 双轨支持并提供hiredis.pc.in/hiredis_ssl.pc.in的 pkg-config 模板与hiredis-config.cmake.in/hiredis_ssl-config.cmake.in的 CMake 包配置模板便于第三方项目集成如 ZeroTierOne 的 cmake/redis-plus-plus.cmake。七、升级建议与注意事项小结安全优先任何通过 SSL/TLS 连接 Redis 的 hiredis 用户都应升级到 1.0.2以获取 CVE-2021-32765 修复由于 1.0.2 与 1.0.0 功能完全相同升级成本极低。避开 1.0.11.0.1 错误提升了 SONAME官方 changelog 明确要求改用 1.0.2。超时语义变化从 0.14.x 迁移时options-timeout需改为options-connect_timeout或options-command_timeout并确认两者语义是否符合预期连接超时 vs 命令超时。长度类型变化自定义回复对象工厂的createArray长度参数已是size_tredisReply.len自 0.14.0 起为size_t比较时注意类型匹配。RESP3 的 PUSH 处理默认自动拦截释放 PUSH 回复只有在 MONITOR/SUBSCRIBE 式阻塞循环中才适合关闭自动处理。版本兼容仓库同时保留 hiredis 0.14.1 与 1.0.2 两个版本实际编译哪个取决于平台与构建配置ZeroTierOne 控制器依赖的 redis-plus-plus 底层即 hiredis升级 hiredis 前应先确认对应 redis-plus-plus 版本的兼容性。延伸阅读版本变更全文ext/hiredis-1.0.2/CHANGELOG.md头文件与 API 声明ext/hiredis-1.0.2/include/hiredis/hiredis.h、ext/hiredis-1.0.2/include/hiredis/async.h、ext/hiredis-1.0.2/include/hiredis/alloc.hSSL 实现与示例ext/hiredis-1.0.2/hiredis_ssl.h、ext/hiredis-1.0.2/ssl.c、ext/hiredis-1.0.2/examples/example-ssl.cRESP3 PUSH 示例ext/hiredis-1.0.2/examples/example-push.cZeroTierOne 控制器对 Redis 的使用nonfree/controller/RedisListener.cpp、nonfree/controller/Redis.hpp、nonfree/controller/CMakeLists.txt【免费下载链接】ZeroTierOneA Smart Ethernet Switch for Earth项目地址: https://gitcode.com/GitHub_Trending/ze/ZeroTierOne创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表