Niet-geïnitialiseerde variabele - Uninitialized variable

Bij computergebruik is een niet-geïnitialiseerde variabele een variabele die wordt gedeclareerd maar niet is ingesteld op een bepaalde bekende waarde voordat deze wordt gebruikt. Het zal enige waarde hebben, maar niet voorspelbaar. Als zodanig is het een programmeerfout en een veelvoorkomende bron van bugs in software.

Voorbeeld van de C-taal

Een algemene aanname van beginnende programmeurs is dat alle variabelen worden ingesteld op een bekende waarde, zoals nul, wanneer ze worden gedeclareerd. Hoewel dit voor veel talen geldt, geldt het niet voor alle talen, en dus is er kans op fouten. Talen zoals C gebruiken stapelruimte voor variabelen, en de verzameling variabelen die aan een subroutine zijn toegewezen, staat bekend als een stapelframe . Terwijl de computer naast de juiste hoeveelheid ruimte die voor de stack frame, doet meestal zo eenvoudig door instellen van de waarde van de stack pointer , en is niet bedoeld het geheugen zelf nieuwe toestand (gewoonlijk van efficiëntie betreft). Daarom zal elke inhoud van dat geheugen op dat moment verschijnen als beginwaarden van de variabelen die die adressen bezetten.

Hier is een eenvoudig voorbeeld in C:

void count( void )
{
    int k, i;
    
    for (i = 0; i < 10; i++)
    {
        k = k + 1;
    }
    
    printf("%d", k);
}

De uiteindelijke waarde van k is niet gedefinieerd. Het antwoord dat het 10 moet zijn, gaat ervan uit dat het begon bij nul, wat al dan niet waar kan zijn. Merk op dat in het voorbeeld de variabele i op nul wordt geïnitialiseerd door de eerste clausule van de for instructie.

Een ander voorbeeld kan zijn bij het omgaan met structs . In het onderstaande codefragment hebben we een struct student die enkele variabelen bevat die de informatie over een student beschrijven. De functie register_student lekt geheugen omdat het niet lukt om de leden van struct student new_student . Als we in het begin een kijkje nemen age , semester en student_number worden geïnitialiseerd. Maar de initialisatie van de first_name en last_name leden is onjuist. Dit komt omdat als de lengte van first_name en last_name tekenarrays minder zijn dan 16 bytes, strcpy we er niet in slagen om de volledige 16 bytes geheugen gereserveerd voor elk van deze leden volledig te initialiseren. Daarom lekken we na memcpy() de resulterende struct naar output wat stackgeheugen naar de beller.

struct student {
    unsigned int age;
    unsigned int semester;
    char first_name[16];
    char last_name[16];
    unsigned int student_number;
};

int register_student(struct student *output, int age, char *first_name, char *last_name)
{
    // If any of these pointers are Null, we fail.
    if (!output || !first_name || !last_name)
    {
        printf("Error!\n");
        return -1;
    }

    // We make sure the length of the strings are less than 16 bytes (including the null-byte)
    // in order to avoid overflows
    if (strlen(first_name) > 15 ||  strlen(last_name) > 15) {
      printf("first_name and last_name cannot be longer that 16 characters!\n");
      return -1;
    }

    // Initializing the members
    struct student new_student;
    new_student.age = age;
    new_student.semester = 1;
    new_student.student_number = get_new_student_number();
    
    strcpy(new_student.first_name, first_name);
    strcpy(new_student.last_name, last_name);

    //copying the result to output
    memcpy(output, &new_student, sizeof(struct student));
    return 0;
}


In elk geval, zelfs als een variabele impliciet wordt geïnitialiseerd naar een standaardwaarde zoals 0, is dit meestal niet de juiste waarde. Geïnitialiseerd betekent niet correct als de waarde een standaardwaarde is. (De standaardinitialisatie op 0 is echter een juiste praktijk voor pointers en arrays van pointers, omdat het ze ongeldig maakt voordat ze daadwerkelijk worden geïnitialiseerd naar hun juiste waarde.) In C worden variabelen met een statische opslagduur die niet expliciet zijn geïnitialiseerd, geïnitialiseerd naar nul (of nul, voor pointers).

Niet alleen zijn geïnitialiseerde variabelen een veel voorkomende oorzaak van bugs, maar dit soort bug is bijzonder ernstig, omdat het niet reproduceerbaar zijn: bijvoorbeeld, kan een variabele ongeinitialiseerde blijven alleen in bepaalde tak van het programma. In sommige gevallen kunnen programma's met niet-geïnitialiseerde variabelen zelfs softwaretests doorstaan .

Gevolgen

Niet-geïnitialiseerde variabelen zijn krachtige bugs omdat ze kunnen worden misbruikt om willekeurig geheugen te lekken of om willekeurig geheugen te overschrijven of om code-uitvoering te verkrijgen, afhankelijk van het geval. Bij het exploiteren van software die gebruikmaakt van randomisatie van de adresruimte-indeling , is het vaak vereist om het basisadres van de software in het geheugen te kennen. Het misbruiken van een niet-geïnitialiseerde variabele op een manier om de software te dwingen een aanwijzer uit de adresruimte te lekken, kan worden gebruikt om ASLR te omzeilen.

Gebruik in talen

Niet-geïnitialiseerde variabelen vormen een bijzonder probleem in talen zoals assembleertaal, C en C ++, die zijn ontworpen voor systeemprogrammering . De ontwikkeling van deze talen omvatte een ontwerpfilosofie waarin conflicten tussen prestaties en veiligheid over het algemeen werden opgelost ten gunste van de prestaties. De programmeur kreeg de last om zich bewust te zijn van gevaarlijke zaken, zoals niet-geïnitialiseerde variabelen.

In andere talen worden variabelen bij het maken vaak geïnitialiseerd op bekende waarden. Voorbeelden zijn:

  • VHDL initialiseert alle standaardvariabelen in een speciale 'U'-waarde. Het wordt gebruikt in simulatie, voor foutopsporing, om de gebruiker te laten weten wanneer de initiële waarden niet schelen , via de meerwaardige logica , de uitvoer beïnvloeden.
  • Java heeft geen niet-geïnitialiseerde variabelen. Velden van klassen en objecten die geen expliciete initialisatie hebben en elementen van arrays worden automatisch geïnitialiseerd met de standaardwaarde voor hun type (false voor boolean, 0 voor alle numerieke typen, null voor alle referentietypes). Lokale variabelen in Java moeten zeker worden toegewezen voordat ze worden geopend, anders is het een compilatiefout.
  • Python initialiseert lokale variabelen naar NULL (onderscheiden van None ) en verhoogt een UnboundLocalError wanneer een dergelijke variabele wordt benaderd voordat deze (opnieuw) wordt geïnitialiseerd op een geldige waarde.
  • D initialiseert alle variabelen tenzij expliciet gespecificeerd door de programmeur om dit niet te doen.

Zelfs in talen waarin niet-geïnitialiseerde variabelen zijn toegestaan, zullen veel compilers proberen het gebruik van niet-geïnitialiseerde variabelen te identificeren en deze als compilatietijdfouten te rapporteren .

Zie ook

Referenties

  1. 2007-09-07. p. 126 . Ontvangen 2008-09-26 . Paragraaf 6.7.8, paragraaf 10.
  2. Sun Microsystems . Ontvangen 2008-10-18 .

Verder lezen

  • CWE-457 Gebruik van niet-geïnitialiseerde variabele [1] .