Skip to content

Unsigned to signed conversion error

Büyük bir unsigned değeri signed bir tipe cast etmek negatif bir sayı üretir; downstream kod bunu küçük/in-bounds bir değer veya geriye doğru bir index olarak ele alır.

Mechanism

C'nin integer conversion'ları, temsil edilebilir olduğunda değere göre, olmadığında ise implementation-defined (pratikte bit pattern'inin modüler yeniden yorumlanması) olarak tanımlanır. Üst bit'i set olan büyük bir unsigned miktar, negatif bir signed değer hâline gelir: (int)0xFFFFFFFFu == -1. CWE-196 tehlikeyi yakalar:

Note

"signed-to-unsigned conversion'dan daha az sık bir sorun olsa da, unsigned-to-signed conversion, attacker'ların normal bir buffer overflow durumunda aksi takdirde erişemeyecekleri yerlerde stack'te aşağı hareket etmesine olanak veren tehlikeli buffer underwrite durumlarının mükemmel bir öncüsü olabilir." — CWE-196

Bu, ters yönlü kuzeni signed to unsigned conversion error (CWE-195) ile aynı değildir: orada negatif bir değer devasa pozitif bir size'a (SIZE_MAX) wrap olur; burada ise büyük bir unsigned değer negatife döner ve esas yeni tehlike buffer underwrite / negatif array index'tir (CWE-124).

Bug iki klasik şekilde ısırır. (1) Bir length veya size unsigned olarak gelir, bir üst sınıra karşı validate edilir, sonra int olarak saklanır veya geçirilir; sign flip, devasa bir size'ı len < MAX check'ini geçen ama daha sonra memcpy'de devasa bir size_t'a yeniden genişletilen negatif bir size'a çevirir. (2) Negatif bir değer doğrudan bir array index'i olarak kullanılır ve object'in altında bir buffer underwrite üretir.

Walkthrough

Sign flip ile minimal bir underwrite:

void copy_in(unsigned int user_len, char *src) {
    char buf[256];
    int len = (int)user_len;          /* 0xFFFFFFFF -> -1 */
    if (len < (int)sizeof(buf)) {     /* -1 < 256  : check PASSES */
        memcpy(buf, src, len);        /* len promoted to size_t -> 0xFFFF... */
    }                                 /* gigantic copy -> overflow/crash */
}

Signed karşılaştırma len < 256, len negatif olduğu için karşılanır, oysa memcpy'nin üçüncü argument'ı size_t'tir, dolayısıyla negatif int sign-extend edilir ve devasa bir unsigned length olarak yeniden yorumlanır.

Negatif-index underwrite:

int idx = (int)attacker_unsigned;     /* becomes negative */
table[idx] = value;                   /* writes BELOW table -> CWE-124 underwrite */
Conversion'ı gözlemleme
unsigned int u = 0xFFFFFFFFu;
printf("%d\n", (int)u);    /* -1 */
printf("%zu\n", (size_t)(int)u);  /* 18446744073709551615 on 64-bit */

Detection

Implicit narrowing/sign değişikliklerini işaretlemek için -Wconversion -Wsign-conversion ile compile et; UBSan (-fsanitize=signed-integer-overflow,implicit-conversion) ve static analyzer'lar (Coverity, clang-analyzer) şüpheli signed/unsigned karışımlarını raporlar. Sınır değerleriyle (0x7FFFFFFF, 0x80000000, 0xFFFFFFFF) fuzzing, flip'i runtime'da ortaya çıkarır.

Mitigation

Size'ları ve index'leri uçtan uca size_t/unsigned'da tut; daha sonraki kullanımla aynı signedness'te karşılaştırmalarla validate et; herhangi bir cast'ten önce makul bir maximum'un üzerindeki değerleri reddet. Checked-arithmetic yardımcılarını kullan ve güvenilmeyen input'tan türetilen length'ler için signed int kullanmaktan kaçın.

References