Skip to content

Integer overflow to buffer overflow

Bir size hesaplamasındaki aritmetik bir overflow, amaçlanandan-daha-küçük bir allocation'a yol açar; böylece tam istenen size'ı varsayan sonraki write'lar buffer'ın sonunu aşar.

Mechanism

Invariant: allocate için kullanılan size ile write için kullanılan size aynı değer olmalı

Bu, birleşik güvenlik açığı CWE-680'dir. Birincil bir integer overflow/wraparound (CWE-190)'ı, sonuçta ortaya çıkan bir out-of-bounds write'a (CWE-119 / CWE-787) zincirler. Kök neden, byte sayısı n * sizeof(T)'nin sabit-genişlikli bir integer tipinde hesaplanmasıdır. Matematiksel çarpım tipin maksimumunu aştığında 2^width modulo wrap eder, dolayısıyla malloc() küçük bir argüman alır ve küçük bir buffer döndürür. Çevre kod ise hâlâ n element'e sahip olduğuna inanır ve n'e kadar index'ler/kopyalar, undersized bölgenin sonunu aşarak yazar. Overflow tek başına hiçbir şeyi corrupt etmez — "allocate edilen size" ile "yazılan size" arasındaki eşitliği sessizce bozar ve o bozulmuş invariant bug'dır.

Walkthrough

Kanonik MITRE CWE-680 örneği: num_imgs attacker-etkili ve sizeof(img_t) * num_imgs çarpımı overflow eder.

img_t table_ptr;        /* struct containing img data, 10kB each */
int   num_imgs;
...
num_imgs  = get_num_imgs();
table_ptr = (img_t*)malloc(sizeof(img_t) * num_imgs);
...

sizeof(img_t) 10240 (0x2800) ise ve attacker, çarpımın 32-bit size_t'de yapıldığı bir target'ta num_imgs = 0x40000 sağlarsa:

0x2800 * 0x40000 = 0xA0000000  (2.5 GiB) -> fits in 32 bits, huge alloc, but...
0x2800 * 0x6334  = 0xFFFD3000  -> still fits
0x2800 * 0x6667  = 0x1_0000_5800 -> truncates to 0x00005800 (22 KiB)

yani gigabyte'lar rezerve etmesi gereken bir istek, bunun yerine ~22 KiB'lık bir buffer verir, bu sırada table_ptr[0..num_imgs-1]'i dolduran loop onu çok aşar.

sizeof size_t döndürür, ama count çoğu zaman int'tir

Çarpımın tipi operand'ların terfi ettiği her ne ise odur. Signed bir int count'u unsigned size_t sizeof ile karıştırmak size_t'ye terfi eder, ama negatif ya da çok büyük bir int yine de wrap'lenmiş bir değer üretir. Count'u her zaman çarpmadan önce doğrula.

CERT C kuralı INT30-C precondition-check pattern'ini verir: çarpımı gerçekleştirmeden önce SIZE_MAX'i aşamayacağını doğrula.

/* Noncompliant: product may wrap before reaching malloc() */
pen->vertices = malloc(pen->num_vertices * sizeof(cairo_pen_vertex_t));

/* Compliant: check the upper bound first */
if (pen->num_vertices > SIZE_MAX / sizeof(cairo_pen_vertex_t)) {
   /* Handle error */
}
pen->vertices = malloc(pen->num_vertices * sizeof(cairo_pen_vertex_t));
Undersized allocation'ı AddressSanitizer altında yeniden üretmek

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    uint32_t n = 0x6667;          /* attacker-controlled count */
    size_t   bytes = (uint32_t)(0x2800u * n);  /* wraps in 32 bits */
    char    *buf = malloc(bytes); /* tiny: 0x5800 bytes */
    memset(buf, 'A', 0x2800u * (size_t)n);     /* writes ~640 MiB */
    return 0;
}
$ clang -m32 -fsanitize=address int680.c -o int680 && ./int680
==1==ERROR: AddressSanitizer: heap-buffer-overflow ... WRITE of size ...
    #0 ... in main int680.c
    0x... is located 0 bytes to the right of 22528-byte region
ASan write'ı gerçek (wrap'lenmiş) bölge size'ının "to the right of" (sağında) olarak raporlar; istenen ve allocate edilen length'ler arasındaki uyuşmazlığı açığa çıkarır.

Detection

  • SAST: önceki bir bounds check olmadan non-constant integer'ların çarpımı/toplamı olan malloc/calloc/memcpy size argümanlarını işaretle (taint source → allocation sink).
  • Runtime: wrap'i ve OOB write'ı yakalamak için AddressSanitizer (-fsanitize=address) ya da UndefinedBehaviorSanitizer (-fsanitize=integer) ile derle.
  • n * size overflow'unu dahili olarak tespit etmesi zorunlu olan calloc(n, size)'ı tercih et.

Mitigation

  • Checked-arithmetic builtin'leri kullan: C23 <stdckdint.h> ckd_mul, ya da __builtin_mul_overflow (GCC/Clang).
  • INT30-C precondition'ını uygula: if (n > SIZE_MAX / size) { error; }.
  • El yapımı malloc(n * size) yerine calloc() / reallocarray() kullan.
  • Herhangi bir size aritmetiğinden önce element count'u domain'e-özgü bir maksimuma karşı doğrula.

References