
# Spring AI ParagraphManager 형제 자체 루프 OOM 최소 재현 (CVE-2026-47851의 불완전한 수정)
org.springframework.ai:spring-ai-pdf-document-reader의org.springframework.ai.reader.pdf.config.ParagraphManager에서 발생하는 무한 루프 / OOM에 대한 최소 재현 코드입니다.
spring-projects/security-advisories에 비공개 GitHub Security Advisory로 보고되었습니다. 공개 공개가 아닙니다. 이 저장소는 책임 있는 공개 및 검증 목적으로만 공유됩니다.
ParagraphManager.generateParagraphs는 주기 감지나 반복 상한 없이
getNextSibling()을 통해 개요 형제 항목을 순회합니다. 개요 항목이 자신의
/Next 포인터(즉, 형제 자체 루프)를 선언하는 PDF는 루프가 종료되지 않게
만듭니다. 각 반복은 부모의 children 목록에 새 를 추가하여
로 JVM 힙을 고갈시킵니다.
ParagraphOutOfMemoryErrorCVE-2026-47851 수정(깊이 제한 + 방문 집합)에 추가된 검사는
firstChild 주기만 다루며 형제 자체 루프는 다루지 않습니다. 깊이는 레벨 1을
넘어 성장하지 않고, 방문 집합은 firstChild만 기록하므로 형제 항목은 집합에
들어가지 않습니다. 따라서 이 변형은 최신 릴리스(2.0.1)에서도 재현됩니다.
| 항목 | 값 |
|---|---|
| 영향받는 구성 요소 | org.springframework.ai:spring-ai-pdf-document-reader |
| 영향받는 버전 | < 2.0.2 (알려진 모든 릴리스; 1.x 및 2.0.0에는 CVE-2026-47851 수정조차 포함되지 않음) |
| 취약한 클래스 | org.springframework.ai.reader.pdf.config.ParagraphManager |
| 트리거 | 악성 PDF가 포함된 new ParagraphPdfDocumentReader(resource) |
| 영향 | OutOfMemoryError, 프로세스 종료 — DoS |
| 심각도 | 7.5 HIGH — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| 관련 | CVE-2026-47851 (불완전한 수정) |
pom.xml — Maven 빌드, spring-ai-pdf-document-reader:2.0.1에 의존
src/main/java/ReleaseVerify.java — 실제 사용자 코드 (공식 API 호출)
poc/selfloop.pdf — 396바이트 악성 PDF (형제 자체 루프)
poc/normal.pdf — 384바이트 대조 PDF (/Next 자체 참조가 없다는 점만 제외하고 동일)
두 PDF는 정확히 12바이트 차이가 납니다. selfloop.pdf에는 개요 항목에
/Next 5 0 R이 하나 더 있어 항목이 자신을 다음 형제로 가리킵니다.
mvn -q package
target/springai-release-verify-1.0.0.jar(fat jar, PDFBox 3.0.7 포함)을
생성합니다.
# 악성 PDF — 수 초 내 OOM
java -Xmx96m -cp target/springai-release-verify-1.0.0.jar ReleaseVerify para poc/selfloop.pdf
# 대조 PDF — 100ms 미만으로 정상 완료
java -Xmx96m -cp target/springai-release-verify-1.0.0.jar ReleaseVerify para poc/normal.pdf
OOM은 ParagraphPdfDocumentReader 생성자 내부(즉, new ParagraphManager(document) 호출)에서
발생합니다. get()은 결코 도달하지 않습니다.
이 저장소는 비공개로 보고된 권고의 검증을 지원하기 위해서만 존재합니다.
PDF 파일은 PDFBox 및 기타 적합한 PDF 파서에 무해하며, Spring AI의
ParagraphManager 순회에서 논리 오류를 유발할 뿐입니다. 소유하지 않은
시스템에 대해 사용하지 마십시오.