View page as slide show

Statyczna analiza kodu

Analiza statyczna kodu - analiza struktury kodu źródłowego lub kodu skompilowanego bez jego uruchomienia.

Static Analysis Software Testing (SAST) - narzędzia do automatycznej analizy statycznej

Analiza statyczna a inspekcja kodu („code review”)

Agile Development Using Microsoft Security Development Lifecycle - analiza statyczna w każdym sprincie

Źródło: http://www.microsoft.com/security/sdl/discover/sdlagile.aspx

  • analiza leksykograficzna - wyszukiwanie niebezpiecznych konstrukcji w kodzie źródłowym, zazwyczaj na podstawie zestawu reguł, różnych heurystyk i dopasowania do wzorców błedów
  • metody formalne - oparte na matematycznej definicji zachowania programu
  • metryki kodu źródłowego - ocena jakości kodu źródłowego na podstawie danych statystycznych.
  • Szybkość działania - szybkie wykrywanie błędów, których naprawa jest prosta i mało kosztowna
    • nie wymagają uruchamiania programu
    • łatwe do zrównoleglenia
  • Łatwość użycia - proste we wdrożeniu w cykl wytwarzania oprogramowania
  • Automatyzacja - integracja z narzędziami continius integrations
  • Możliwości rozszerzania: własne reguły, wtyczki, …
  • Integracja z innymi narzędziami: serwery automatyzacji, IDE, kontrola wersji
  • Dużo darmowych narzędzi
  • Wymagany dostęp do źródeł
  • Reguły wykrywają zazwyczaj proste błędy i nie są w stanie wyeliminować ręcznego sprawdzenia kodu
  • Dużo szumu, zbyt czułe - duże prawdopodobieństwo zaklasyfikowanie poprawnego fragmentu jako błędu (false positive)
  • Każde narzędzie zazwyczaj pokrywa pewien zakres testów (do 14% błędów?!). Dlatego warto korzystać z kilku różnych skanerów kodu. Istnieją narzędzia integrujące wiele narzędzi SAST, np: CodeEx, Yast (open source)
  • analiza poprawności składni
  • luki w bezpieczeństwie, także błędy które mogą pojawić się przy specyficznych danych wejściowych
  • detekcja backdoors, niebezpieczne i nieaktualne funkcje, wycieki pamięci, przepełnienie bufora, używanie niezainicjowanych zmiennych, SQL Injections,
  • jakość kodu, ocena stylu, powtórzenia kodu, nieużywane fragmenty kodu, …
  • wydajność, wykrywanie wąskich gardeł, niewydajne konstrukcje, sugestie dotyczące poprawienia wydajności
  • zgodność z dobrymi praktykami, zachowanie standardów, norm nazewniczych, problemy z przenośnością kodu

Statyczna analiza programu pozwala na:

  • zwiększenie wydajności i stabilności poprzez zasady oparte na dobrych praktykach,
  • unikniecie typowych błędów podczas programowania,
  • dostarczenie struktury do zarządzania standardami kodu.
  • wymuszenie zasad i standardów pisania kodu
  • zwiększanie bezpieczeństwa poprzez kolejny etap testowania
  • analizując sygnalizowane błędy można się sporo nauczyć na temat dobrych praktyk bezpiecznego programowania

Koszt naprawy błędu w zależności od momentu wykrycia

benefits.jpg

Źródło: http://www.microsoft.com/security/sdl/about/benefits.aspx

Lint - UNIX V7 1979, historyczny program od którego nazwy często określa się narzędzia do analizy i szukania błedów

  • rats (rough auditing tool for security) C, C++, Perl, PHP, Python
  • PMD Java, JavaScript, XML, XSL (zawiera CPD analiza powtórzeń: C, C++, C#, PHP, Ruby, Fortran)
  • Yasca Java, C/C++, HTML, JavaScript, ASP, ColdFusion, PHP, COBOL, .NET, and other languages + integracja z innymi SAT
  • VisualCodeGrepper C/C++, C#, VB, PHP, Java and PL/SQL

Wiele innych:

Kompilatory oprócz sprawdzania składni również potrafią sygnalizować możliwość występowania błędu w kodzie.

GCC warning options

  • -pedantic
    tylko czyste ISO C i ISO C++
  • -Wall
    wszystkie ostrzeżenia
  • -Werror
    ostrzeżenia generują błąd kompilacji
  • -Wextra
    dodatkowe ostrzeżenia wykraczające poza Wall

Visual C++: warning level 4

  • analiza kodu c/C++
  • CLI, GUI, biblioteka (API)
  • Raporty: txt, XML, HTML
  • Cel:
    • wykrywanie błędów, które nie są wykrywane przez kompilatory
    • brak fałszywych alarmów - wykrywanie tylko prawdziwych błędów
  • wielowątkowy
  • wsparcie: Code::Blocks (wbudowany), Hudson, wtyczka Visual Studio
  • możliwość dodawania własnych reguł opartych na wyrażeniach regularnych

Cppcheck
Online Demo

  • wykroczenia poza zakres tablicy
  • wycieki pamięci
  • odwołania do adresu NULL
  • niezainicjowane zmienne
  • używanie przestarzałych lub niebezpiecznych funkcji
  • wykrywanie niedostępnych fragmentów kodu oraz powtórzeń kodu
  • wiele innych …
  • Lista reguł i testów
      cppcheck --doc
      cppcheck --errorlist
  • Jest wiele typów błędów, które ciężko jest wykryć, np błędy związane są z konfiguracją lub logiką biznesową
  • Problemem są zależności, dodatkowe moduły, biblioteki, konteksty dodawane przez frameworki
  • Skomplikowane algorytmy i architektury bardzo ciężko jest analizować
// calculate the number of days
int days = hours / 23;
int main()
{
   char *x;
   int a[20];
 
   *x = 5;
   a[20] = 5;
 
   return 0;
}
cppcheck --enable=all test1.c 
Checking test1.c...
[test1.c:4]: (style) Variable 'x' is not assigned a value.
[test1.c:8]: (style) Variable 'a' is assigned a value that is never used.
[test1.c:8]: (error) Array 'a[20]' accessed at index 20, which is out of bounds.
[test1.c:7]: (error) Uninitialized variable: x
Checking usage of global functions..
#include <stdio.h>
int main()
{
    char c;
    while (c != 'x');
    {
        c = getchar();
        if (c = 'x') return 0;
        switch (c) {
        case '\n':
        case '\r':
            printf("Newline\n");
        default:
            printf("%c",c);
        }
    }
    return 0;
}

Źródło: wikipedia.org

Śledzenie wywołań funkcji:

void f1(char *s)
{
   s[20] = 0;
}
 
void f2()
{
   char a[10];
   if (x + y == 2) {
      f1(a);
   }
}
Array 'a[10]' index 20 out of bounds

Prawie to samo ale cppcheck już nie wykrywa błędu

void f3(char *s)
{
   if (x + y == 2) {
      s[20] = 0;
   }
}
 
void f4()
{
   char a[10];
   f3(a);
}
void f()
{
   char *a = malloc(10);
   if (x + y == 2) {
      return;
   }
   free(a);
}

Wynik cppcheck

[mem.c:5]: (error) Memory leak: a
void f2(int x)
{
   char *a = 0;
   if (x == 10)
      a = malloc(10);
   if (x == 20)
      free(a);
}

Brak błedu?!

  • kiedy analizować: przed/po commicie, po każdym buildzie, po zapisaniu pliku
  • IDE i ich edytory sprawdzają składnię też (rozszerzenia Resharper)
  • testy po stronie developera lub po stronie repozytorium (serwera autoamtyzacji)
  • skrypty uruchamiane po zatwierdzeniu zmiany + powiadomienia (email, inne alerty), w SVN hooks/post-commit
  • uniemożliwienie zatwierdzenia kodu z błędami, w SVN hooks/pre-commit
  • serwery automatyzacji, np. Hudson

Przykłady: FindBugs, Cppcheck+TortoiseSVN SVN pre-commit