TL;DR

  • pgvector 0.8.7은 0.8.6 이하의 IVFFlat 인덱스 빌드에서 차원 불일치 벡터가 백엔드 메모리 범위를 벗어나 쓰게 해 임의 코드 실행으로 이어질 수 있는 CVE-2026-103484를 수정함.
  • 취약점은 테이블 소유자에게 필요한 CREATE INDEX 권한뿐 아니라 REINDEX와 PostgreSQL 17 이후의 MAINTAIN 권한 또는 pg_maintain 역할을 통해서도 도달할 수 있음.
  • pgvector에서는 2026년에 인덱스 빌드 코드의 메모리 안전성 취약점이 세 번째로 발견됐으며, 서로 다른 원인의 취약점이 크기 메타데이터를 다루는 빌드 코드에 집중됨.
  • 새 패키지 설치만으로는 충분하지 않으며, 이전 공유 라이브러리를 불러온 기존 백엔드와 연결 풀 서버 연결을 재순환해야 수정된 코드가 적용됨.
  • 소스 빌드와 PGDG 패키지 사용자는 릴리스 페이지가 아닌 v0.8.7 태그를 확인해야 하며, 관리형 서비스 사용자는 제공자에게 버전과 배포 일정을 확인해야 함.

무엇이 잘못됐나

  • IVFFlat 인덱스 빌드는 테이블에서 표본을 추출하고, 표본에 k-means를 적용해 리스트 중심을 정한 뒤, 각 행을 가장 가까운 중심에 배정함.
  • 인덱스의 차원 수는 열의 형식 수정자(type modifier)에 지정된 값에서 가져옴. 예를 들어 vector(1536)의 차원 수는 1536이며, 빌드 과정은 이에 맞춰 작업 배열 크기를 정함.
  • 수정 전에는 k-means 단계에서 표본 벡터를 배열에 더할 때 반복 범위를 각 벡터의 자체 헤더에서 가져옴. 벡터의 차원 수가 형식 수정자와 다르면 배열 끝을 넘어 계속 기록함.
  • 수정 사항은 인덱스의 차원 수를 k-means 단계에 전달하고 모든 벡터가 그 차원 수와 일치하는지 확인함. 또한 IVFFlat과 HNSW가 벡터를 받는 빌드, 삽입, 스캔 경로 모두에 같은 검사를 추가함.
  • 보안 권고문은 공격자가 형식 수정자와 일치하지 않는 벡터를 어떻게 전달하는지 설명하지 않음.
  • 확장(extension)을 작성한다면 형식 수정자는 열에 대한 선언이지, 접근 방식(access method)이 전달받는 바이트의 속성이 아님을 유의해야 함. C 코드에서 한 값을 기준으로 버퍼 크기를 정하고 다른 값을 기준으로 반복문을 실행하면 취약점이 발생할 수 있음.

올해 세 번째 취약점

  • CVE-2026-3172는 2월 0.8.2에서 수정됐으며, 병렬 작업자를 사용하는 HNSW 인덱스 생성 또는 재인덱싱 권한이 있는 사용자가 도달할 수 있는 병렬 HNSW 빌드의 정수 래핑(integer wraparound) 문제임. 데이터 유출이나 충돌을 일으킬 수 있음.
  • CVE-2026-18022는 7월 0.8.6에서 수정됐으며, 32비트 시스템의 IVFFlat 빌드에서 발생하는 정수 래핑 문제로 범위를 벗어난 쓰기로 이어짐.
  • CVE-2026-103484는 0.8.7에서 수정된 이번 취약점이며, 32비트 시스템으로 한정되지 않음.
  • 세 취약점은 발생 원인이 다르지만 모두 인덱스 빌드 코드 영역에 속함. 빌드 코드는 쿼리마다 실행되는 대신 인덱스마다 한 번 실행돼 스캔 경로보다 훨씬 적게 실행되며, 메타데이터에서 파생된 크기를 바탕으로 많은 산술 연산을 수행함.
  • 벡터 확장에 퍼저(fuzzer)를 적용한다면 빌드 코드가 우선 대상임. 이번 취약점은 Compass Security 소속 연구자 네 명이 발견함.

권한으로 여겨지지 않는 권한

  • IVFFlat 인덱스 생성 권한이 있는 데이터베이스 사용자는 적어 보일 수 있지만, CREATE INDEX에는 테이블 소유권이 필요하고 많은 애플리케이션은 마이그레이션 프레임워크가 사용하는 역할로 데이터베이스에 연결함. 따라서 해당 역할은 애플리케이션 자체와 애플리케이션을 통해 SQL을 실행할 수 있는 주체를 포함할 수 있음.
  • REINDEX도 같은 빌드 코드를 통해 인덱스를 다시 생성하므로 대상 권한에 포함됨. PostgreSQL 17부터는 MAINTAIN 권한 또는 미리 정의된 pg_maintain 역할을 통해 소유 객체가 없는 역할에도 이 권한을 부여할 수 있음.
  • 2월 보안 권고문은 재인덱싱을 명시적으로 언급함. 어떤 역할에 관련 권한이 있는지 확인해야 함.
  • 지속적인 해결책은 스키마 소유권을 가진 마이그레이션 역할과 SELECT, INSERT, UPDATE, DELETE만 가진 애플리케이션 역할을 분리하는 것임. 기존 시스템에 적용하기는 번거롭지만, 애플리케이션 역할 자체가 이 취약점에 노출되는지 여부를 가르는 차이임.

제대로 업그레이드하기

  • 수정 사항은 벡터 공유 라이브러리에 있으므로, 이전 라이브러리를 불러온 백엔드는 종료될 때까지 이전 코드를 계속 실행함. 연결 풀의 연결은 며칠 동안 유지될 수 있음.
  • 새 패키지를 설치한 뒤에는 패키지가 설치되기 전에 시작한 모든 백엔드를 재순환해야 함. pg_stat_activity에서 backend_type = 'client backend'인 항목 중 backend_start가 패키지 설치 시각보다 이른 프로세스를 확인하면 됨. 예시 기준 시각은 2026-10-05 09:00:00-07임.
  • 필요하면 pg_terminate_backend()로 백엔드를 종료할 수 있음. PgBouncer를 사용한다면 관리 콘솔에서 RECONNECT를 실행해 각 서버 연결이 반환될 때 닫을 수 있으며, 그렇지 않으면 기본값이 3600초인 server_lifetime에 따라 연결이 순차적으로 종료됨.
  • vector를 shared_preload_libraries에 넣었다면 서버를 재시작해야 함. 이 설정은 필수 사항이 아님.
  • pg_extension.extversion은 실행된 SQL 스크립트 버전을 나타낼 뿐, 각 백엔드가 어떤 라이브러리를 매핑했는지는 증명하지 않음.

소스 빌드 또는 PGDG 패키지를 사용하는 경우

  • 실제 설치 버전이 0.8.7인지 확인해야 함. v0.8.7 태그의 변경 기록에는 릴리스 날짜가 10월 1일로 기재돼 있지만, master 브랜치의 변경 기록은 0.8.7을 아직 미출시 상태로 표시하며 오버플로 문제도 언급하지 않음.
  • pgvector는 GitHub 릴리스가 아니라 Git 태그를 게시하므로, GitHub의 Releases 페이지를 감시하는 알림에는 릴리스가 표시되지 않음. 태그를 확인해야 함.

관리형 서비스를 사용하는 경우

  • 실제 실행 버전과 배포 시점은 서비스 제공자가 제공하는 내용과 일정에 따름. 제공자에게 현재 버전과 0.8.7 배포 시점을 문의하고, 업데이트 전까지 인덱스를 생성할 수 있는 역할을 면밀히 확인해야 함.