[하루한줄] CVE-2024-5171: libaom의 integer overflow 취약점

URL

https://issues.chromium.org/issues/332382766

Target

  • libaom < v3.9.0
  • libvpx < v1.14.1

Explain

오픈소스 비디오 코덱 라이브러인 libaom에서 발견된 integer overflow 취약점의 세부 정보가 공개되었습니다.

취약점은 aom/src/aom_image.c의 이미지 버퍼를 새로 할당하는 img_alloc_helper() 함수에 존재합니다.

//  static aom_image_t *img_alloc_helper(
//      aom_image_t *img, aom_img_fmt_t fmt, unsigned int d_w, unsigned int d_h,
//      unsigned int buf_align, unsigned int stride_align, unsigned int size_align,
//      unsigned int border, unsigned char *img_data,
//      aom_alloc_img_data_cb_fn_t alloc_cb, void *cb_priv) {
  32   unsigned int h, w, s, xcs, ycs, bps, bit_depth;
// ...
 107   /* Calculate storage sizes given the chroma subsampling */
 108   w = align_image_dimension(d_w, xcs, size_align);
 109   h = align_image_dimension(d_h, ycs, size_align);
 110 
 111   s = (fmt & AOM_IMG_FMT_PLANAR) ? w : bps * w / bit_depth;   <==== s and w are 32-bit integer variable
 112   s = (s + 2 * border + stride_align - 1) & ~(stride_align - 1);
 113   stride_in_bytes = s * bit_depth / 8;    <==== Integer overflow occurred, causing stride_in_bytes to become a smaller value.
 114

line 32에서는 이미지 연산을 위해 사용하는 변수들을 32bit 크기의 unsigned int 타입으로 선언합니다. 이후 line 111line 112에서 이미지의 stride 크기인 stride_in_bytes를 계산하는데, 이 과정에서 계산 결과가 unsigned int 범위를 넘는 경우 integer overflow가 트리거되어 stride_in_bytes가 예상보다 작은 값이 됩니다.

stride : width와 유사하게 이미지의 한 줄(row)크기를 의미하지만, width와 다르게 padding 등을 포함한 크기입니다. stride는 반드시 width보다 크거나 같아야 합니다.

115   /* Allocate the new image */
116   if (!img) {
117     img = (aom_image_t *)calloc(1, sizeof(aom_image_t));
118 
119     if (!img) goto fail;
120 
121     img->self_allocd = 1;
122   }
123 
124   img->img_data = img_data;
125 
126   if (!img_data) {
127     const uint64_t alloc_size =
128         (fmt & AOM_IMG_FMT_PLANAR)
129             ? (uint64_t)(h + 2 * border) * stride_in_bytes * bps / bit_depth
130             : (uint64_t)(h + 2 * border) * stride_in_bytes;
131 
132     if (alloc_size != (size_t)alloc_size) goto fail;
133 
134     if (alloc_cb) {
135       const size_t padded_alloc_size = (size_t)alloc_size + buf_align - 1;
136       img->img_data = (uint8_t *)alloc_cb(cb_priv, padded_alloc_size);
137       if (img->img_data) {
138         img->img_data = (uint8_t *)aom_align_addr(img->img_data, buf_align);
139       }
140       img->img_data_owner = 0;
141     } else {
142       img->img_data = (uint8_t *)aom_memalign(buf_align, (size_t)alloc_size);     <==== Object allocation
143       img->img_data_owner = 1;
144     }
145     img->sz = (size_t)alloc_size;
146   }

이후 line 127 ~ 130 에서 예상보다 작게 계산된 stride_in_bytes를 이용해 alloc_size를 계산한 뒤 line 142에서 alloc_size만큼 이미지를 할당합니다.

할당한 새 이미지 오브젝트 imgimg_datesz필드에는 각각 할당된 메모리 주소, alloc_size가 저장됩니다.

147 
148   if (!img->img_data) goto fail;
149 
150   img->fmt = fmt;
151   img->bit_depth = bit_depth;
152   // aligned width and aligned height
153   img->w = w; 
154   img->h = h;
155   img->x_chroma_shift = xcs;
156   img->y_chroma_shift = ycs;
157   img->bps = bps;

img 오브젝트의 w필드에는 원본 w값이 저장되는데, 이후 해당 이미지를 처리할 때 img→w를 이용해 계산된 이미지 크기가 integer overflow로 예상보다 작은 stride_in_bytes로 계산된 alloc_size보다 클 수 있어 alloc_size만큼 할당된 heap을 넘어 heap overflow로 이어질 수 있습니다.

취약점의 패치는 unsigned int타입으로 선언된 변수들을 uint64_t 타입으로 변경하는 것으로 이루어졌습니다.

diff --git a/aom/src/aom_image.c b/aom/src/aom_image.c
index 3b1c33d05..60459bf71 100644
--- a/aom/src/aom_image.c
+++ b/aom/src/aom_image.c
@@ -36,7 +36,7 @@ static aom_image_t *img_alloc_helper(
   /* NOTE: In this function, bit_depth is either 8 or 16 (if
    * AOM_IMG_FMT_HIGHBITDEPTH is set), never 10 or 12.
    */
-  unsigned int h, w, s, xcs, ycs, bps, bit_depth;
+  uint64_t h, w, s, xcs, ycs, bps, bit_depth;
   unsigned int stride_in_bytes;
 
   if (img != NULL) memset(img, 0, sizeof(aom_image_t));

libaom과 유사한 구현의 libvpx의 vpx_img_alloc() 함수에도 동일한 integer overflow 취약점이 발견되어 CVE-2024-5197이 할당되었습니다.