← All issues

[2] WebGPU CommandEncoder OOB Read/Write via Dimension Confusion

Severity: High | Component: WebGPU CommandEncoder (Swift) | b2c89c5

Web content에서 표준 WebGPU API를 통해 GPU buffer 메모리에 controlled OOB write가 발생하며, 공격자가 texture dimension으로 offset 크기를 제어할 수 있어 High로 평가되었습니다. diff에서 확인 가능한 variable swap을 기반으로 confidence 0.95로 산출되었습니다.

Swift 구현이 Objective-C++ 버전과 일치하지 않아 out-of-bounds read/write가 발생했습니다. 다수의 문제를 발견하고 패치를 작성한 Jon Butler에게 감사를 표합니다.

Source/WebGPU/WebGPU/CommandEncoder.swift (copyTextureToBuffer y축 수정)

- var yTimesDestinationBytesPerImage = y
- guard destinationBytesPerImage <= UInt32.max else {
+ var yTimesDestinationBytesPerRow = y
+ guard destinationBytesPerRow <= UInt32.max else {
return
}
- (yTimesDestinationBytesPerImage, didOverflow) = yTimesDestinationBytesPerImage.multipliedReportingOverflow(
- by: UInt32(destinationBytesPerImage)
+ (yTimesDestinationBytesPerRow, didOverflow) = yTimesDestinationBytesPerRow.multipliedReportingOverflow(
+ by: UInt32(destinationBytesPerRow)
)

Source/WebGPU/WebGPU/CommandEncoder.swift (copyBufferToTexture z축 수정)

var zTimesSourceBytesPerImage = z
- guard let sourceBytesPerRowU32 = UInt32(exactly: sourceBytesPerRow) else {
+ guard let sourceBytesPerImageU32 = UInt32(exactly: sourceBytesPerImage) else {
return
}
- (zTimesSourceBytesPerImage, didOverflow) = zTimesSourceBytesPerImage.multipliedReportingOverflow(by: sourceBytesPerRowU32)
+ (zTimesSourceBytesPerImage, didOverflow) = zTimesSourceBytesPerImage.multipliedReportingOverflow(by: sourceBytesPerImageU32)

Source/WebGPU/WebGPU/CommandEncoder.swift (누락된 overflow check 추가)

- let sum = UInt(destinationOffset) + UInt(widthTimesBlockSize)
+ var sum = UInt(destinationOffset)
+ (sum, didOverflow) = sum.addingReportingOverflow(UInt(widthTimesBlockSize))
+ guard !didOverflow else {
+ return
+ }

이 패치는 CommandEncoder.copyTextureToBufferCommandEncoder.copyBufferToTexture의 Swift 구현에서 잘못된 offset 계산 여러 곳을 수정합니다. 핵심 수정 사항은 y축 row offset입니다. y * destinationBytesPerImagey * destinationBytesPerRow로 변경되었습니다. 보완 수정으로 copyBufferToTexture에서는 z축 slice offset이 수정되었습니다. z * sourceBytesPerRow에서 z * sourceBytesPerImage로 변경된 것입니다. 추가로 destinationOffset + widthTimesBlockSize에 대한 누락된 overflow check가 추가되었으며, bounds check의 early-return이 continue로 변경되어 전체 abort 대신 해당 row만 건너뛰도록 수정되었습니다.

WebGPU의 copyTextureToBuffercopyBufferToTexture는 GPU texture와 GPU buffer 사이에서 픽셀 데이터를 복사하는 command encoder 연산입니다. 복사 연산에는 세 가지 파라미터가 사용됩니다. bytesPerRow는 buffer 내 연속된 texel row 사이의 stride이며, bytesPerImage는 연속된 depth slice 사이의 stride로 bytesPerRow * rowsPerImage에 해당합니다. 그리고 3D copySize(width, height, depth)가 있습니다. 위치 (x, y, z)에 있는 texel의 buffer offset은 baseOffset + z * bytesPerImage + y * bytesPerRow + x * bytesPerBlock으로 계산됩니다. WebKit의 WebGPU 구현에는 이 함수의 Objective-C++ 버전과 Swift 버전이 모두 존재합니다. 이번 commit은 재작성 과정에서 C++ 참조 구현과 달라진 Swift 버전을 수정합니다.

근본 원인은 다축 buffer offset 연산에서 발생한 dimension variable 교환입니다. 패치 이전에 copyTextureToBuffer는 y축 per-row offset을 올바른 y * destinationBytesPerRow 대신 y * destinationBytesPerImage로 계산했습니다. bytesPerImage = bytesPerRow * heightInRows이므로, 이 버그는 row iteration마다 offset을 texture 높이 배수만큼 과도하게 증가시킵니다. texture 높이가 1보다 큰 경우, 두 번째 row부터 계산된 buffer offset이 실제 destination buffer 범위를 크게 벗어나며, destination GPU buffer에 대한 out-of-bounds write가 발생합니다.

대칭적으로 copyBufferToTexture는 z축 slice offset을 z * sourceBytesPerImage 대신 z * sourceBytesPerRow로 계산했습니다. slice stride를 과소 계산하여 multi-slice 복사 시 잘못된 source buffer 위치를, 경우에 따라서는 OOB 위치를 읽게 됩니다. 한편 destinationOffset + widthTimesBlockSize에 overflow check가 누락되어 있어, integer overflow를 통해 이후의 bounds check를 우회할 가능성도 존재합니다.

bytesPerRowbytesPerImage는 동일한 numeric type(UInt32)을 공유합니다. 두 변수가 모두 단순 정수이기 때문에 Swift의 type system은 교환을 방지하지 못했습니다. newtype wrapper나 dimensional type을 사용했다면 컴파일 시점에 이 혼동을 잡을 수 있었을 것입니다.

🔒

상세 취약점 분석, 공격 가능성 평가, 보안 영향 분석이 포함되어 있습니다

더 확인하려면 구독해 주세요

🔒

이 취약점 패턴의 변종을 찾기 위한 구체적인 탐색 방향이 포함되어 있습니다

더 확인하려면 구독해 주세요