暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

pgvector 0.8.7发布:修复8.8分高危漏洞,我在IvorySQL实测过了

原创 严少安 3天前
38

10 月 1 日,pgvector 发布 0.8.7。打开 CHANGELOG,只有两行,看起来就是个例行小版本。

但如果只读 CHANGELOG,你会漏掉两件事:这一版里藏着一个 CVSS 8.8 的高危漏洞(CVE-2026-103484),还有一条没写进 CHANGELOG、但对写入密集场景影响极大的 HNSW 竞态修复。10 月 5 日,PostgreSQL 官网专门为此发了新闻。

这不是一次可以排期的升级,是应该立即执行的补丁。

国庆期间我在一台 CentOS 7.9 上做了次完整实验:从源码编译最新的 IvorySQL(master 分支,PG 19beta1 内核),编译安装 pgvector 0.8.7,然后在 IvorySQL 里把这一版的每一处修复逐项验证了一遍。

这篇文章就是实验全记录。前半部分来看 0.8.7 的三处修复,后半部分是基于 IvorySQL 的具体实操过程,建议收藏。

01. 先说 CVE:两行 CHANGELOG 里藏着的 8.8 分

pgvector 0.8.7 于 2026 年 10 月 1 日发布(GitHub Release),PostgreSQL 官网 10 月 5 日跟进发布新闻。支持 PostgreSQL 13 及以上(0.8.x 系列从 0.8.0 起放弃了 PG 12)。索引磁盘格式没有变更,升级后不需要重建索引。

版本概览一张表先放这儿:

项目 说明
发布时间 2026-10-01(GitHub),2026-10-05(PostgreSQL 官网新闻)
版本性质 安全修复版本:1 个高危 CVE + 2 项功能性修复
索引格式 无变更,升级后无需重建索引
升级建议 尽快,涉及可致任意代码执行的漏洞

这一版的三处修复,先给个全景:

修复 级别 是否写入 CHANGELOG 一句话概括
IVFFlat 构建缓冲区溢出(CVE-2026-103484) CVSS 8.8 高危 已列出 有建索引权限即可越界写,可能导致任意代码执行
avg 聚合空结果集报错(PR #1012) 正确性 已列出 无匹配行时误报「vector must have at least 1 dimension」
HNSW INSERT/VACUUM 竞态(PR #1010) 并发、索引损坏 未列出 并发写入产生重复邻居,召回率悄悄劣化

重点说 CVE-2026-103484。NVD 已收录,PostgreSQL 作为 CNA 给出的 CVSS 3.1 评分 8.8(HIGH),攻击向量是 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H。翻译成白话:网络可达、利用难度低、普通数据库用户就够、无需交互,一旦利用成功,机密性、完整性、可用性全部打穿。关联 CWE-787(越界写)和 CWE-1284(输入数量校验不当),影响 0.8.6 及以下所有版本。

漏洞的本质不复杂:输入向量的维数和元素数量这类运行时数据,在 C 扩展层没有做严格校验。具备 CREATE INDEX 权限的用户构造一个特殊的 IVFFlat 索引构建请求,就能触发越界写。关键在于,pgvector 是跑在 PostgreSQL 后端进程内的 C 扩展,越界写一旦成功,攻击面直接覆盖数据库服务器进程本身。修复提交是 ee00d39ab30c2a7cf9510abf9288c624d91db156(v0.8.7 tag 的直接父提交),由 Compass Security 的 Emanuele Barbeno、Cyrill Bannwart、Urs Mueller 与 Lukasz D 报告。

更让人警惕的是趋势。把时间线拉出来看看 0.8.x 系列最近的节奏:

版本 日期 内容
0.8.2 2026-02-25 修复并行 HNSW 索引构建缓冲区溢出(CVE-2026-3172)
0.8.6 2026-07-29 IVFFlat 内存与转换修复
0.8.7 2026-10-01 修复 IVFFlat 索引构建缓冲区溢出(CVE-2026-103484)

八个月内,两起索引构建类的内存安全问题,一起 HNSW、一起 IVFFlat。我的结论:pgvector 的版本号必须纳入日常补丁管理流程,不能再当「装一次管一年」的静态组件。

02. 另外两条修复:一条让你报错,一条让你"慢性中毒"

avg 空集报错(PR #1012)

现象很好复现:对向量列做 avg() 聚合,查询没有匹配行时(WHERE 过滤后为空,或者并行聚合的某个 worker 分片为空),会抛 vector must have at least 1 dimension,而不是按 SQL 语义返回 NULL。

根因在并行聚合的实现。PostgreSQL 的并行聚合用 INITCOND = '{0}' 当初始工作状态,表或扫描分片没行时,初始状态就是一个 0 维浮点数组。多个后台 worker 各自产生空分片后,vector_combine 合并函数会对这个初始化状态执行 CheckDim(0) 维数检查,于是误报。修复方式是修改 vector_combine_impl,仅当两个传入的并行状态都完全为空时条件性跳过维数检查,其余路径的校验原样保留,不会放松对畸形输入的防护。PR 还附带了一个强制并行执行限制的 TAP 测试用例,贡献者是 Vaibhav Jain。

对业务的影响很实际:如果你的报表里有 SELECT avg(embedding) FROM ... WHERE tag = ... 这类可能查空的查询,0.8.7 之前会间歇性失败。它不常出现,但一旦出现就是线上事故。

HNSW 竞态(PR #1010,CHANGELOG 漏列)

这条要单独敲黑板:它没有写进官方 CHANGELOG 的 0.8.7 条目,但经 Git 历史确认确实包含在本版本中(提交 3b72e5b 于 2026 年 8 月 4 日合并进 master,早于 v0.8.7 的 tag,且是 v0.8.7 的祖先提交)。只读 CHANGELOG 的运维同学会漏掉它,而对高并发写入场景,它的影响比 CVE 更隐蔽。

破坏机制值得细看一下,三步:

  1. INSERT 写入新元素的第一条桥接边(i=0)时执行盲写,不检查连接是否已存在,新元素由此在图遍历中变为全局可达;
  2. 并发的 VACUUM Phase 2(RepairGraph)在修复相邻节点时发现了这个新元素,检查过连接后把它写进邻接表;
  3. INSERT 恢复后更新其余候选连接(i=1),再次盲写,把同一元素第二次写进同一个邻接数组。

结果:重复邻居。浪费固定节点出度容量(m 或 2m)、阻塞有效导航路径、显著拉低 ANN 检索召回率。查询执行器虽然会用 visited 哈希集合对最终结果去重,但索引结构本身的物理损伤无法自愈。

修复方式是移除 checkExisting 标志,让 UpdateNeighborOnDisk 无条件执行 ConnectionExists 检查,与 VACUUM 的行为对齐,从算法上消除这个不对称竞态。性能开销可以忽略:目标邻居页在更新前已经被加载并加锁,不产生额外磁盘 I/O,唯一成本是有界邻接数组的 CPU 扫描(数组大小受 m 限制,通常 16 到 100)。贡献者是 Google 的 upenderbh。

如果你维护的是「高频写入 + 自动 VACUUM 并存」的 HNSW 索引(日志向量、增量样本库都是典型场景),0.8.7 之前存在索引被慢慢破坏、召回率数周内悄然劣化的隐患。注意,这个修复救不了已经损坏的索引。升级之后建议用 pageinspect 抽查邻居数组,或者干脆重建一次,消除隐患。

也顺手分享一个工作习惯:核对安全更新别只读 CHANGELOG,把 compare/v0.8.6...v0.8.7 的提交清单翻一遍才完整。这次漏列如果不是去对 Git 历史,估计不少团队升完级还以为只有两条修复。

03. 实验环境:从源码编译 IvorySQL

说回实验本身。选 IvorySQL 是因为它回答了一个很多 Oracle 存量客户在问的问题:迁到 PG 系之后,AI 业务要的向量能力怎么办?IvorySQL 的思路是双解析器(PG Parser + Oracle Parser)、双端口(5432 + 1521)、PL/iSQL 存储过程,Oracle 迁移平滑落地;同时又生自 PG 生态,pgvector 这类明星扩展理论上可以原封不动装进来。理论如此,但实测才知道。

实验环境:CentOS 7.9,gcc 4.8.5,8 核 / 16 GB 内存 / 50 GB 磁盘。拉取的 master 快照信息如下:

项目 值
commit 63fb0bfe6159fb91b03012eea09f3db7cd46db8c(2026-09-18)
PG 内核 19beta1
IvorySQL 版本 5beta1

第一步,装编译依赖:

yum install -y readline-devel zlib-devel flex bison \ libicu-devel openssl-devel perl-ExtUtils-Embed libuuid-devel

两个坑提前说。CentOS 7 自带 OpenSSL 1.0.2,不满足 PG 新内核要求的 1.1.1 及以上,configure 时要加 --without-openssl(或者自己升级 OpenSSL)。另外 --with-uuid=e2fs 依赖 libuuid-devel,漏装会在 configure 阶段报 library 'uuid' is required。

第二步,拉源码。 GitHub 直连被重置的话,Gitee 镜像可用:

git clone --depth 1 https://gitee.com/IvorySQL/IvorySQL.git src

第三步,配置编译:

cd src ./configure --prefix=/usr/local/ivorysql \ --without-openssl --with-uuid=e2fs --with-oraport=1521 make -j8 make install make -C contrib install

--with-oraport=1521 是 IvorySQL 特有选项,启用 Oracle 协议端口监听。核心安装用 make && make install 就够了。编译产物确认:postgres (PostgreSQL) 19beta1,psql 19beta1。

第四步,初始化并启动(Oracle 模式):

useradd ivorysql mkdir -p /data/ivorysql/data /data/ivorysql/logs chown ivorysql /data/ivorysql/data /data/ivorysql/logs su ivorysql -c "/usr/local/ivorysql/bin/initdb -m oracle \ -D /data/ivorysql/data --locale=C --encoding=UTF8" su ivorysql -c "/usr/local/ivorysql/bin/pg_ctl -D /data/ivorysql/data \ -l /data/ivorysql/logs/ivorysql.log -w start"

连接验证,同一实例、双协议端口,这就是 IvorySQL 的招牌:

$ psql -p 5432 -d postgres -c "select version();"
 PostgreSQL 19beta1 (IvorySQL 5beta1) on x86_64-pc-linux-gnu ...

$ psql -p 5432 -d postgres -c "show ivorysql.port;"
 ivorysql.port
---------------
 1521

04. 编译 pgvector 0.8.7:一处内核 API 漂移

网络受限环境下,git clone 可能反复超时,codeload 直链下载 tar 包是最稳的路径:

curl -sL -o pgvector.tar.gz \ https://codeload.github.com/pgvector/pgvector/tar.gz/refs/tags/v0.8.7 tar xzf pgvector.tar.gz && mv pgvector-0.8.7 pgvector

编译前把 PG_CONFIG 指到 IvorySQL 的目录:

cd pgvector export PG_CONFIG=/usr/local/ivorysql/bin/pg_config make -j8

第一次编译,报错了:

src/ivfbuild.c:987:3: error: too few arguments to function 'plan_create_index_workers'
src/hnswbuild.c:1076:2: error: too few arguments to function 'plan_create_index_workers'

原因很清楚:IvorySQL 基于 PG 19beta1 内核,这个内核给 plan_create_index_workers() 新增了第三个参数 int override_workers(0 表示自动探测,大于 0 表示显式指定并行 worker 数),而 pgvector 0.8.7 的发布基线还是面向更早内核的调用方式。这是扩展追新内核时的典型接口漂移,不是 bug,属一行级适配。

补丁就是两个文件各加第三个参数,传 0,保持原有的自动探测语义:

// src/ivfbuild.c:987 parallel_workers = plan_create_index_workers(RelationGetRelid(buildstate->heap), RelationGetRelid(buildstate->index), 0); // src/hnswbuild.c:1076 parallel_workers = plan_create_index_workers(RelationGetRelid(heap), RelationGetRelid(index), 0);

重新编译安装,vector.control、vector--0.8.7.sql、vector.so 全部就位。这一步也顺带印证了 0.8.7 的升级建议:索引磁盘格式无变更,升级不需要重建索引,因为改动集中在构建和并发逻辑,而不是存储布局。

05. 逐项验证:在 IvorySQL 上复现每一处修复

创建扩展并确认版本:

CREATE EXTENSION vector; SELECT extname, extversion FROM pg_extension WHERE extname='vector'; -- vector | 0.8.7

注意看这个实例上同时装着什么:Oracle 兼容扩展 ivorysql_ora、GB18030-2022 字符集支持、plisql(PL/iSQL 过程语言),以及 vector 0.8.7。一套实例,Oracle 语法和向量检索都在上面了。

postgres=# \dx
                                         List of installed extensions
     Name     | Version | Default version |   Schema   |                     Description
--------------+---------+-----------------+------------+------------------------------------------------------
 gb18030_2022 | 1.0     | 1.0             | pg_catalog | support gb18030 2022 with extension
 ivorysql_ora | 1.0     | 1.0             | sys        | Oracle Compatible extension on Postgres Database
 plisql       | 1.0     | 1.0             | pg_catalog | PL/iSQL procedural language
 plpgsql      | 1.0     | 1.0             | pg_catalog | PL/pgSQL procedural language
 uuid-ossp    | 1.1     | 1.1             | sys        | generate universally unique identifiers (UUIDs)
 vector       | 0.8.7   | 0.8.7           | public     | vector data type and ivfflat and hnsw access methods
(6 rows)

5.1 基础能力:向量类型与距离检索

CREATE TABLE items (id serial PRIMARY KEY, embedding vector(3)); INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'), ('[1,1,1]'); SELECT id, embedding, embedding <-> '[1,1,1]' AS l2 FROM items ORDER BY embedding <-> '[1,1,1]';
 id | embedding |         l2
----+-----------+--------------------
  3 | [1,1,1]   |                  0
  1 | [1,2,3]   |   2.23606797749979
  2 | [4,5,6]   | 7.0710678118654755

L2 距离升序,语义正确。

5.2 验证修复一:avg 空集语义(#1012)

SELECT avg(embedding) FROM items WHERE false;
 avg
-----
     -- 返回 NULL,旧版本此处直接报错
(1 row)

正常路径不受影响:

SELECT avg(embedding) FROM items; avg ------------------------- [2,2.6666667,3.3333333]

空集返回 NULL,标准 SQL 语义恢复。报表里那类「按标签过滤后可能为空」的聚合查询,从此少一类间歇性故障。

5.3 验证修复二:IVFFlat 构建路径(CVE-2026-103484)

最直接的验证就是索引能正常创建:

CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 2); -- CREATE INDEX

顺手看一眼 \d items,IVFFlat 和 HNSW 两个索引可以共存于同一张表,互不干扰:

Indexes:
    "items_pkey" PRIMARY KEY, btree (id)
    "items_embedding_idx" ivfflat (embedding) WITH (lists='2')
    "items_embedding_idx1" hnsw (embedding vector_l2_ops)

再强调一遍漏洞的利用条件:仅需低权限用户加建索引权限。生产环境请把「能创建索引」当敏感权限管理,业务账号不应随手持有 DDL 权限。

5.4 验证修复三:HNSW 索引路径(#1010,CHANGELOG 未列)

CREATE INDEX ON items USING hnsw (embedding vector_l2_ops); -- CREATE INDEX

前面说过,这条修复对「写入密集 + 频繁 VACUUM」的场景比 CVE 更隐蔽。它不报错、不崩溃,只是让召回率在数周内悄悄劣化,而且磁盘上的结构损伤无法自愈。已经在旧版本上长期跑并发写入 HNSW 的同学,升级后重建一次索引。

5.5 附加能力抽查

SELECT '[1,2,3]'::halfvec(3) <-> '[4,5,6]'::halfvec(3); -- 5.196152422706632

halfvec(半精度向量)正常。sparsevec 的输入语法是 {index:value,...}/dim 格式,比如 '{1:1,3:2}'::sparsevec(3),这个和 0.8.7 的修复点无关,知道格式就行。

06. 升级指南:十分钟动作,关掉一个高危窗口

综合前面的分析,升级理由按优先级排:

  1. CVE-2026-103484 没有讨价还价的空间。 网络可达、利用难度低、普通用户即可、无需交互,影响 0.8.6 及以下所有版本。这不是可以排期的改进项,是应该立即执行的补丁。
  2. avg 空集报错是生产里真实存在的间歇性故障。 并行聚合架构下任何一个 worker 分片为空都可能触发,不常出现,出现就是线上报表失败。
  3. HNSW 竞态让召回率慢性死亡。 不报错不崩溃,只悄悄劣化,最符合「温水煮青蛙」特征的缺陷。对 RAG、推荐这类以召回率为命脉的应用,这一条单独就值得升级。
  4. 升级成本几乎为零。 索引格式无变更,无需重建索引(HNSW 长期并发写入场景除外,建议主动重建一次);升级动作就是 make && make install 加每库一条 ALTER EXTENSION vector UPDATE;。

操作清单,可以直接收藏:

-- 1. 确认当前版本 SELECT extversion FROM pg_extension WHERE extname = 'vector'; -- 2. 升级二进制后,在每个使用向量扩展的数据库执行 ALTER EXTENSION vector UPDATE; -- 3. 回归验证 SELECT extversion FROM pg_extension WHERE extname = 'vector'; -- 应为 0.8.7 SELECT avg(embedding) FROM your_table WHERE false; -- 应返回 NULL

不同安装方式的升级路径:

  • 源码安装:git pull 或切到 v0.8.7 tag,make && make install,然后每个库执行 ALTER EXTENSION vector UPDATE;
  • 发行版包:PGDG 或发行版仓库升级包,包脚本没自动执行的话手动跑 ALTER EXTENSION vector UPDATE;
  • 云托管:Neon、Supabase 这类托管服务一般会在维护窗口自动升级或提供版本选择,关注厂商公告

07. 三个判断

判断一:IvorySQL 对 PG 生态的继承是真实的。 这次实验里,pgvector 0.8.7 在 IvorySQL 上除了一行内核 API 适配外零障碍运行,CREATE EXTENSION、HNSW/IVFFlat 索引、聚合函数全部符合预期。Oracle 迁移项目不必为 AI 能力另起炉灶,一套实例同时服务 PL/iSQL 老代码和向量新业务。

判断二:在新内核加新扩展,要有适配意识。 追最新内核就意味着扩展可能踩到接口漂移,这次的 plan_create_index_workers 第三参数就是典型。生产环境我建议以 IvorySQL 稳定版为基线(比如 5.6,PG 18.6 内核),扩展生态的兼容性更成熟。生产环境千万不要上 beta 版本。

判断三:安全更新不分平台。 无论你跑在 PostgreSQL 还是 IvorySQL 上,只要装了 pgvector 0.8.6 及以下,CVE-2026-103484 就与你有关。升级十分钟的事,不要等 Agent 顺着安全漏洞爬进来就为时已晚了。

向量数据库的竞争进入深水区之后,胜负手不再是「有没有向量类型」,而是生态兼容性、修复响应速度和迁移友好度的综合较量。IvorySQL 用双端口承接存量 Oracle 业务、用 PG 生态承接 AI 新业务;pgvector 0.8.7 则示范了一个健康的开源扩展如何对待一个 8.8 分的 CVE:快速发布、明确披露、零迁移成本。


Have a nice day ~ ☕

公众号「 少安事务所 」,专注于数据 & AI 领域技术传播。

如果这篇文章为你带来了灵感或启发,请帮忙『点赞、转发、推荐』,感谢!ღ( ´・ᴗ・` )~

最后修改时间:2026-10-10 10:11:54
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论