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
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.