Integer coercion error¶
Bir integer'ı tipler arasında cast etmek, extend etmek ya da truncate etmek, onun değerini veya işaretini çevre mantığın beklemediği şekilde değiştirir; çoğu zaman geçilmiş bir bounds check'i bir overflow'a çevirir.
Mechanism¶
Invariant
Integer tipleri arasındaki her implicit ya da explicit conversion değeri korumalı, ya da programcı değerin hedefe sığdığını kanıtlamış olmalı. C bunu sessizce bozar: daha geniş bir tipi daha dar olana atamak truncate eder, dar signed bir tipi daha geniş olana atamak sign-extend eder ve signed/unsigned karıştırmak bit pattern'ını yeniden yorumlar. CWE-192 bu üç mekanizmayı gruplar — type casting, extension ve truncation. Tehlike zamansaldır: bir değer bir tipte doğrulanır (orada güvenli görünür), sonra aynı bitlerin başka bir şey ifade ettiği başka bir tipe coerce edilir — küçük signed bir değer dev bir unsigned size_t olur, ya da kontrol edilmiş bir short daha büyük bir miktarı truncate eder. Bounds check yanlış temsili korur.
Walkthrough¶
CWE-192'nin sign-extension örneği, bir değerin bir upper-bound check'i geçmesini, sonra length olarak kullanılan bir unsigned tipe genişletildiğinde devasa olmasını gösterir:
int GetUntrustedInt() {
return(0x0000FFFF);
}
void main(int argc, char **argv) { // illustrative; helper fns (GetUntrustedInt,
// DiePainfully, GetUserInput) omitted for brevity
char path[256];
char *input;
short s;
unsigned int sz;
s = GetUntrustedInt(); // 0x0000FFFF stored into a 16-bit short -> -1
if (s > 256) { // -1 > 256 is false: check PASSES
DiePainfully("go away!\n");
}
sz = s; // BUG: short -1 converts to unsigned 4294967295
input = GetUserInput("Enter pathname:");
strncpy(path, input, sz); // copies up to 4 GB into a 256-byte buffer
path[255] = '\0';
printf("Path is: %s\n", path);
}
İki coercion zincirlenir: 0x0000FFFF'i bir short'a truncate etmek -1 verir, bu da s > 256'dan rahatça geçer; sonra sz = s -1'i 4294967295'e sign-convert eder, dolayısıyla strncpy path[256]'yı vahşice aşar.
CWE-192 malloc varyantı aynı tuzağı allocation tarafında gösterir:
// illustrative; ExitError() and ParsePacketHeaders() omitted for brevity
int numHeaders;
PacketHeader *headers;
numHeaders = packet->headers; // attacker-controlled signed int
if (numHeaders > 100) { // only the upper edge is checked
ExitError("too many headers!");
}
// numHeaders == -3 passes the check; multiply then coerce to size_t
headers = malloc(numHeaders * sizeof(PacketHeader));
ParsePacketHeaders(packet, headers);
Negatif bir numHeaders > 100 guard'ını atlar ve çarpım malloc için size_t'ye dönüştürülür — başarısız olan devasa bir isteğe (DoS), ya da wrap sonrası parse loop'un overrun ettiği minik bir allocation'a dönüşür.
CERT C INT31-C unsigned→signed truncation'ı en saf haliyle izole eder:
#include <limits.h>
void func(void) {
unsigned long int u_a = ULONG_MAX;
signed char sc;
sc = (signed char)u_a; // BUG: truncates to a misinterpreted value
}
Uyumlu düzeltmesi cast'ten önce range-check yapar:
#include <limits.h>
void func(void) {
unsigned long int u_a = ULONG_MAX;
signed char sc;
if (u_a <= SCHAR_MAX) {
sc = (signed char)u_a; // proven to fit
} else {
/* Handle error */
}
}
Gerçekten kullanılacak olan tipte doğrula
Bir değeri short ya da int olarak kontrol edip sonra size_t/unsigned olarak kullanmak, tekrar eden footgun'dır. Ya değeri son tipinde doğrula, ya da önce convert et sonra doğrula. Signed bir tip için doğru olan bir bound, değer unsigned olarak yeniden yorumlandığında anlamsız olabilir.
Detection¶
- Compiler uyarıları:
-Wconversion -Wsign-conversionimplicit narrowing ve sign değişimlerini yüzeye çıkarır; explicit cast'ler bunları susturur, dolayısıyla cast'leri manuel denetle (INT31-C cast'in bug'ı düzeltmeden "uyarıyı ortadan kaldırdığını" belirtir). - UBSan (
-fsanitize=implicit-conversion) kayıplı implicit conversion'ları runtime'da işaretler.
Mitigation¶
- Convert etmeden önce bir değeri hedef tipin limitlerine (
SCHAR_MAX,UINT_MAX, …) karşı range-check et. - Data flow'u, çoklu/karmaşık cast'ler gereksiz olacak şekilde tasarla (CWE-192 mimari rehberi); bir bounds check ile kullanımı boyunca iyi-anlaşılmış tek bir temsil tut.
- Uygun olduğunda belirsiz conversion'ları trap eden diller/tipler tercih et.