[non-cocoa][fuzz] OOB read in APNG with hidden default image
Component: WebCore image decoders | 56c0a88
APNG는 PNG에 frame sequence를 얹은 포맷입니다. 여기서 default image는 APNG를 인식하지 못하는 decoder가 렌더링하는 기본 PNG를 가리킵니다. 그런데 파일은 이 default image가 애니메이션에 포함되지 않는다고 선언할 수도 있습니다. 이 구분 때문에 non-Cocoa decoder는 libpng의 row callback과 end callback에 걸쳐 "현재 frame"이라는 서로 겹치는 두 개념을 동시에 관리하게 됩니다. default image를 숨긴 조작된 파일은 이 둘을 어긋나게 만들 수 있습니다.
이 commit은 decoder가 libpng callback state를 추적하는 방식을 단순화하는 쪽으로 fuzzing에서 발견된 out-of-bounds read를 수정했습니다. 결과적으로 frame lifecycle이 두 관점을 서로 맞추는 작업에 더 이상 기대지 않게 되었습니다. 해당 버그는 security 분류 없이 공개적으로 보고된 건입니다.
Significance
AVIF/WebP/JPEG XL out-of-bounds write와 나란히, 이번 주 non-Cocoa image decoder에서 fuzzing으로 확인된 두 번째 memory-safety 버그에 해당합니다. 두 건 모두 GTK와 WPE port에서 평범한 <img> decode 경로를 통해 신뢰할 수 없는 콘텐츠에 닿습니다. 또한 원인이 codec library 자체가 아니라 frame lifecycle을 관리하는 코드 쪽에 있다는 점도 같습니다.
Audit directions
앞으로 눈여겨볼 패턴은, decoder가 온전히 소유하지 않고 codec library의 callback들 사이에 걸쳐 존재하는 상태입니다. 이런 구조에서는 "지금 어느 frame인가"에 대한 두 관점이 조작된 입력에서 서로 벌어질 수 있습니다. Narrow: library callback으로 구동되는 다른 non-Cocoa multi-frame decoder도 같은 형태를 갖습니다. 각 decoder에서 한 callback은 읽기만 하고 갱신은 다른 callback에서만 이뤄지는 frame index나 frame pointer를 찾아보십시오. 그리고 예상된 callback 순서를 건너뛰거나 뒤바꾸는 파일이 그 값에 어떤 영향을 주는지 확인할 필요가 있습니다. Wider: AVIF/WebP/JPEG XL 수정이 반대편에서 건드린 것도 결국 같은 frame lifecycle 관리 코드였습니다. 그쪽에서는 backing store의 frame별 rect가 복사 과정에서 누락되고 있었습니다. 따라서 decoder와 ImageBackingStore를 함께 놓고, decoder가 쓰고 있다고 믿는 frame과 backing store가 기술하는 frame이 따로 추적되는 지점을 점검해 볼 만합니다. Widest: header가 저장된 요소를 자신이 속한 sequence 바깥에 있다고 선언할 수 있는 포맷이라면, 항상 정렬을 유지해야 하는 두 개의 count가 생깁니다. 검토할 때의 신호는, callback handler가 자기 자신은 한 번도 쓰지 않는 멤버로부터 frame 위치를 유도하는 경우입니다.