Les langages a lecture-seule ou ecriture-seule

Pour se détendre… voici une nouvelle classification des langages :)

Il existe différentes catégories de langages de programmation :

  • des langages à typage explicite (C, C++, Pascal, …) dans lesquels les variables doivent être déclarées, ou à typage implicite (Javascript, Actionscript) où il n’y a pas besoin de déclarer les variables.
  • des langages statiques, ou dynamiques (les éléments du programmes sont connus à l’exécution, pas à la compilation)

et bien d’autres catégories, c’est connu. Mais saviez vous qu’on pouvait aussi donner des attributs de lecture-seule ou d’écriture seule à un langage ?

 

Le langage à lecture seule

Comme un fichier qui est en lecture seule, un langage à lecture seule est un code qui ne peut pas être modifié. Il peut être lu et compris, mais il n’est pas possible d’imaginer pouvoir modifier le code écrit.

Un exemple de code à lecture seule est le code de la librairie STL de C++. Si le code est lisible (à oeil entraîné), la modification d’un seul caractère est impossible tellement le code est précis, pointu et réfléchi.

        // TEMPLATE FUNCTION for_each
template<class _InIt,
    class _Fn1> inline
    _Fn1 _For_each(_InIt _First, _InIt _Last, _Fn1 _Func)
    {    // perform function for each element
    for (; _First != _Last; ++_First)
        _Func(*_First);
    return (_Func);
    }

Les langages à lecture seule ont une syntaxe subtile, et ont des caractères magiques qui modifient complètement le sens d’une expression.

 

Le langage à écriture seule

A ne pas confondre avec le langage à écriture seule qui, comme sa définition le laisse entendre, peut être écrit, mais ne peut pas être lu.

Un exemple valant mieux qu’un long discours, voici un exemple de code à écriture seule :

(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?
a.defaultView||a.parentWindow:!1}function cv(a){if(!cj[a])
{var b=f("<"+a+">").appendTo("body"),d=b.css("display");b.remove();
if(d==="none"||d===""){ck||(ck=c.createElement("iframe"),
ck.frameBorder=ck.width=ck.height=0),c.body.appendChild(ck);

C’est tout simplement le début de la librairie Javascript JQuery. Ca a été écrit, mais impossible de le lire !

Plus généralement, seul l’auteur d’un code en écriture seule est capable de savoir ce qu’il y a dedans. C’est synonyme de code-spaghetti. Cela peut être volontaire : certains développeurs cultivent cet attribut car il peut s’agir d’une manière de conserver son poste !

Note : l’un des “inventeurs” du concept de langage à lecture seule est Miguel de Icaza.

C++ code analysis in Visual Studio 2012

Lire cet article en français

lots-of-crashes_thumb1

The problem with this kind of message is that it does not make your customers very found of your application. And what if it happens during trial period?

Good news is, code analysis is one answer to this kind of problem.

Code analysis examines source code at compilation time, and finds bugs before they even happen. It is one of the debugging tools a developer can use. It complements the usual test procedures (functional testing, unit testing …) Its secret is to find unsafe code patterns in source code. And it is efficient.

Code analysis is one of the tools that can be used to create safer applications. It is part of Microsoft SDL methodology (Security Development Lifecycle).

I’m sorry to inform you that Code analysis knows which bugs you create. Hence, it can find them in your code:

  • dereferencing null pointer
  • buffer overrun
  • uninitialized variable
  • memory leak
  • concurrent access problem

Code Analysis in Visual C++ 2012 (VS11) and MFC

 

The team which has created Code analysis for C++ used the huge code base of Windows. They know what type of code is safe, and what type of code can cause a crash!

Code analysis in Visual Studio 2010 is only available in Visual Studio Ultimate and Premium. But Microsoft decided it is important for every C++ developer to have this tool handy. So, a light version of C++ code analysis is included in Visual Studio 11 Express, and all of the 200+ rules are in Visual Studio 11 Professional (from Code Analysis talk at Build Conference)

Code analysis has been improved in Visual Studio 11 in many ways: it is easier to use, thanks to the new Code Analysis window, and it is more powerful, more readable (more explicit messages) and reported problems are more relevant. Code analysis uses rules to find unsafe code patterns. The rules are improved, and cover a wider range of code, including multitasking, and soon 64-bit compilation.

Visual Studio 11 Developer Preview, a pre-release version of Visual Studio vNext, allows everyone to use C++ code analysis and its bug traps, under Windows 7 (Visual Studio 11 is temporary name of next release of Visual Studio.)

When you use C++ MFC application wizard (menu File/New project/MFC Application), a SDL checks checkbox can activate Code analysis:

MFC-app-wizard-11-SDL-check_thumb3

When the project is created, Code analysis can be run very easily:

Visual--studio-2010-build-menu-code-

Code analysis must recompile the project. It is slightly longer than a simple compilation.

Note : for non-MFC C++ projects, code analysis can also be activated in the project properties (menu Project/Properties), in C/C++ general tab.

 

A few bugs detected by Code Analysis

 

All indices of arrays are scrutinized by Code analysis to detect potential overflow, especially in the loops. If an array index goes out of the array boundaries, the problem will be reported.

For example, in CDocument class, let’s create an array and use it like this:

class CMFCApplication1Doc : public CDocument
{
    // ... static const int m_size = 100;
    char m_tab[m_size];
    // ... };
void CMFCApplication1Doc::ShiftArray()
{
    for (int i=0; i< m_size; i++)
    {
        m_tab[i] = m_tab[i+1];
    }
}

Of course there is a problem. It may not be caught by a code review anyway, especially if the code is not as simple as here. Code analysis finds the bug, and notices that valid indices for m_tab array range from 0 to 99, while index 100 is used.:

visual-studio-2012-code-analysis_thu

The code analysis window has a search field. Results can be filtered. Above, only the results for files whose names contain “doc” are displayed.

Here is another unsafe code sample:

void CMFCApplication1Doc::SetDelay(int delay)
{
    CDelayHolder *pObj = nullptr;
    bool isFound = false;
    for (int index = 0; index <10; index++)
    {
        if (index >= delay)
        {
            pObj = new CDelayHolder();
        }
        else {
            if (!isFound)
                isFound = WaitDelay(delay);
            else pObj->Wait(delay);
        }
    }
}

This code is a bit weird but I have seen even stranger code! Code analysis finds the problem and explains it. It occurs during the second time through the loop:

visual-studio-2012-code-analysis-loo

 

Buffer overrun, invalid pointers, memory leaks, and many other problems are sought in the code. Code analysis uses more than 200 rules to check code safety.

 

Customizing Code analysis

 

You can customize the rules of the code analyzer as needed. Create a new rule file (menu File/New File/Code Analysis rule set), and you can disable, enable, or treat certain rules as error or warning.

visual-studio-2012-code-analysis-lis

Code Analysis tab in the Project properties let you select a Code analysis rule set.

Code analysis behavior can also be customized in C++ code, using #pragma warning :

#pragma warning(suppress: 6011) #pragma warning (error: 6001) 

 

Semantic code annotation

 

It is very easy to get a diagnostic on code quality with Visual Studio 11 Code analysis. It is possible to get even more accurate diagnostics if you help the code parser to do its job. SAL (Source code Annotation Language) is a list of keywords you can add in your source code. SAL keywords are recognized by the Code analysis parser, to make the results more accurate.

SAL can be seen as semantic comments in code. They are used by the analyzer to make verifications. SAL keywords are defined in the file sal.h. This is the file you want to check if you want to know how it works.

_In_ and _Out_ are the most used SAL keywords, in a function declaration. For example:

void GetTextProperty(_In_ LPCTSTR szPropertyName,
                    _Out_ LPTSTR szOutBuffer,
                    _In_ int nBufferSize);

_In_ means the function will only read from the buffer. The caller must provide the buffer and initialize it.

_Out_ means the function will only write to the buffer. The caller must provide the buffer, and the function will initialize it.

Code analysis will check that szPropertyName is correctly initialized before the function is called.

Microsoft Windows development team uses SAL, and more than 3 millions SAL annotations have been added to Windows source code. Thus, Windows include files contain useful annotations for our applications. For example, GlobalLock Win32 function is defined in winbase.h:

_Ret_maybenull_ LPVOID WINAPI GlobalLock (_In_ HGLOBAL hMem); 

This is quite explicit: hMem parameter must be initialized before function call and must not be null, while the return value might be null.

Thus, Code analysis will display two messages with following sample code:

HGLOBAL hMem = nullptr;
char *p = (char *)GlobalLock(hMem);
*p = 'a';

visual-studio-2012-code-analysis-glo

The first message indicates that you must not pass a null pointer to GlobalLock. The second says that, since GlobalLock can return a null pointer, it is illegal to dereference it without testing if it is not null.

MFC code also uses SAL. For example, CWnd::GetWindowText is declared as follow:

int GetWindowText(_Out_writes_to_(nMaxCount, return + 1) LPTSTR lpszStrBuf,
    _In_ int nMaxCount) const;

So, Code analysis checks that lpszStrBuf is an array of at least nMaxCount bytes, and nMaxCount must not be null.

A buffer overrun problem is detected in this code sample:

TCHAR buffer[80];
AfxGetMainWnd()->GetWindowText(buffer, 100);

visual-studio-2012-code-analysis-get

 

Parallel code analysis

 

C++ Code analysis can help finding bugs in parallel code. More than 100 rules are particularly relevant on this topic.

One of the common problems of parallel code is avoiding concurrent access to a variable. An error at this level can cause a crash or an application dead lock.

Following code sample uses an integer count, protected by a critical section cs.

_Garded_by_ is a new SAL keyword indicating that a variable must be protected by a lock. Here, cs is the lock which avoids concurrent access on count. Code analysis can now detect any access to count which is not guarded by cs.

typedef struct {
    _Garded_by_(cs) int count;
    CRITICAL_SECTION cs;
}COUNT; 

bool CMFCApplication1Doc::UpdateCount(_In_ COUNT *p, _In_ int diff)
{
    EnterCriticalSection(&p->cs);
    if (p->count < diff)
        return false;
    p->count -= diff;
    LeaveCriticalSection(&p->cs);
    return !p->count;
}

Two problems are detected by Code analysis: a return inside the critical section, and an unprotected access to count at the end of the function:

visual-studio-2012-code-analysis-cri[2]

 

C++ Code analysis – conclusion

 

These last samples impress me. For decades, we were debugging code to find memory overflow in code! And these examples only demonstrate a part of the awesome power of the C++ Code analyzer of Visual Studio 2012. The analyzer will be included in all Visual Studio 2012 versions, including Visual C++ 2012 express, as explained in the Code Analysis talk at Build conference.

One of the conclusions we can draw is you must not use * pointers, operators new and delete anymore, but use C++11 smart pointers . If you are not using shared_ptr or unique_ptr  today, it is essential for you to get started. Smart pointers are really safer than C-style * pointers.

With the new features of C++11 and this new C++ Code analysis feature, it will be very difficult to write unsafe C++ code!

L’analyse de code C++ dans Visual Studio 2012 : un outil puissant

Read this post in english

lots-of-crashes

Le problème avec ce genre de message, c’est qu’il décourage un client d’utiliser une application. Si ça se passe pendant la période d’essai d’un logiciel ?

L’analyse de code est l’un des remèdes à ce genre de problème.

L’analyse de code examine le code lors de la compilation pour trouver les causes des bugs avant qu’ils ne se produisent chez un client. C’est un outil supplémentaire pour déboguer une application, et vient en complément des procédures de test habituelles (tests fonctionnels, tests unitaires…) Elle trouve des bugs lors de la compilation en recherchant dans le code des patterns produisant des bugs.

L’analyse de code est l’un des outils permettant de développer des applications plus sures que Microsoft met à disposition des développeurs. Elle est fait partie de la méthodologie Microsoft SDL (Security Development Lifecycle, ou Sécuriser les Développements Logiciels).

L’analyseur sait quels bugs vous allez créer, et les recherche dans votre code ! Par exemple :

  • déréférencement de pointeurs nuls
  • débordement de buffer
  • utilisation de variables non initialisées
  • fuites de mémoire
  • problèmes d’accès concurrent

L’analyse de code C++ dans Visual C++ 2012 et les MFC

 

L’équipe qui a créé l’analyse de code C++ s’est basé sur l’immense base de code de Windows. Ils savent quel type de code est sûr, et quel type de code peut provoquer un crash !

L’analyse de code de Visual Studio 2010 était seulement incluse dans les versions Premium et Ultimate. Mais Microsoft a pensé qu’il était important d’aider le plus de monde possible à utiliser ces outils, alors certaines règles d’analyse de code sont d’ores et déjà incluses dans Visual Studio 11 Express, et la totalité des 200+ règles seront dans les versions Professionnelles et Ultimate de Visual Studio 11.

L’analyse de code C++ dans Visual Studio 11 a été améliorée de deux façons : elle est plus facile à utiliser, notamment grâce à la nouvelle fenêtre Code Analysis, plus puissante, plus lisible (messages plus explicites) et les problèmes signalés sont plus pertinents. Les règles sont améliorées, et couvrent un éventail de code plus large, notamment le multitâche, et bientôt le support de la compilation 64 bits.

Visual Studio 11 Developer Preview, une pré-version du prochain Visual Studio, permet déjà d’utiliser l’analyse de code et ses pièges à bugs, sous Windows 7. Visual Studio 11 est le nom temporaire de la version qui succèdera à Visual Studio 2010. Il n’est disponible qu’en anglais pour l’instant.

Lorsqu’on utilise l’assistant de génération d’application C++ MFC (menu File/New project/MFC Application), une option SDL checks permet d’activer l’analyse de code :

MFC-app-wizard-11-SDL-check

Lorsque le projet est généré, un menu permet de lancer l’analyse du code du projet très simplement :

Visual -studio-2010-build-menu-code-analysys

L’analyse de code nécessite juste la recompilation du projet. C’est légèrement plus long qu’une simple recompilation.

 

Exemples de bugs détectés

 

Tous les indices des tableaux sont examinés à la loupe, notamment dans le boucles pour détecter les débordements éventuels. Et si un indice déborde d’un tableau dans une boucle, le problème sera signalé.

Dans la classe CDocument, déclarons un tableau, et utilisons le comme suit :

class CMFCApplication1Doc : public CDocument
{
    // ...
    static const int m_size = 100;
    char m_tab[m_size];
    // ...
};
void CMFCApplication1Doc::ShiftArray()
{
    for (int i=0; i< m_size; i++)
    {
        m_tab[i] = m_tab[i+1];
    }
}

Bien sûr il y a un problème. Une relecture de code pas très attentive peut la laisser passer, surtout si le code est plus fourni que dans l’exemple. L’analyse de code trouve le problème et signale que les index valides pour le tableau m_tab vont de 0 à 99, et que l’indice 100 est invalide :

visual studio 2012 code analysis

On peut remarquer qu’en saisissant un texte dans le champ de recherche, en haut de la fenêtre d’analyse de code, on filtre les résultats. Ci-dessus, seuls les résultats concernant les fichiers dont le nom contient “doc” sont affichés.

Voici un autre exemple de code à problème :

void CMFCApplication1Doc::SetDelay(int delay)
{
    CDelayHolder *pObj = nullptr;
    bool isFound = false;
    for (int index = 0; index <10; index++)
    {
        if (index >= delay)
        {
            pObj = new CDelayHolder();
        }
        else
        {
            if (!isFound)
                isFound = WaitDelay(delay);
            else
                pObj->Wait(delay);
        }
    }
}

Oui c’est du code un peu bizarre, mais qui existe ! L’analyseur de code trouve le problème et l’explique. Il se produit lors du deuxième passage dans la boucle :

visual studio 2012 code analysis-loop

 

Débordement de tableau ou de mémoire, pointeur invalide, fuite mémoire,  et bien d’autres problèmes sont recherchés dans le code. L’analyse utilise plus de 200 règles pour vérifier la sécurité du code.

 

Paramétrer l’analyse de code

 

Il est possible de paramétrer les règles de l’analyseur de code selon ses besoins. Il suffit de créer un nouveau fichier de règles (menu File/New Code Analysis rule set), et de désactiver ou traiter certaines règles comme erreur ou avertissement.

visual studio 2012 code analysis-list

L’onglet Code Analysis des propriétés du projet permet de voir ou de modifier la liste des règles d’analyse actives.

On peut aussi modifier le comportement de l’analyse dans le code en utilisant #pragma warning :

#pragma warning(suppress: 6011) #pragma warning (error: 6001) 

 

L’annotation sémantique du code

 

Il est très facile d’obtenir un diagnostic sur la qualité du code avec Visual Studio 11. Mais il est possible d’aller encore plus loin en aidant l’analyseur de code à faire son travail. SAL (Source code Annotation Language), est une liste de mots-clés qui, ajoutés dans le code source, seront reconnus par l’analyseur de code et affineront les résultats.

On peut voir SAL comme des commentaires sémantiques sur le code, destinés à être utilisés par l’analyseur pour qu’il vérifient certaines conditions. Ils sont définis dans le fichier sal.h.

Les mot-clés SAL les plus utilisé sont _In_ et _Out_, dans une déclaration de fonction :

void GetTextProperty(_In_ LPCTSTR szPropertyName,
                    _Out_ LPTSTR szOutBuffer,
                    _In_ int nBufferSize);

Les mots-clés _In_ et _Out_ sont utilisés par l’analyseur de code qui vérifiera que szPropertyName n’est jamais nul.

L’équipe qui développe Windows chez Microsoft utilise SAL, et plus de 3 millions d’annotations SAL ont été ajoutées au code de Windows. C’est ainsi que les fichiers include de Windows comportent des annotations qui sont utiles pour nos programmes. Par exemple, la fonction Win32 GlobalLock est définie ainsi dans winbase.h :

_Ret_maybenull_ LPVOID WINAPI GlobalLock (_In_ HGLOBAL hMem); 

C’est assez explicite : le paramètre hMem ne doit pas être nul en entrée, et la valeur de retour de la fonction peut être nulle.

Ainsi, le code suivant provoque l’affichage de deux messages lors de l’analyse de code :

HGLOBAL hMem = nullptr;
char *p = (char *)GlobalLock(hMem);
*p = 'a';

visual studio 2012 code analysis-globallock

Le premier message signale qu’il n’est pas permis de passer un pointeur nul à GlobalLock. Le second indique que, puisque GlobalLock peut retourner un pointeur nul, alors il est illégal de le déréférencer sans tester s’il est nul.

Le code des MFC utilise également SAL. Par exemple, la méthode CWnd::GetWindowText est déclarée ainsi :

int GetWindowText(_Out_writes_to_(nMaxCount, return + 1) LPTSTR lpszStrBuf,
    _In_ int nMaxCount) const;

L’analyseur de code vérifie que le paramètre lpszStrBuf pointe vers une zone mémoire accessible en écriture de nMaxCount octets, et que nMaxCount ne doit pas être nul.

Et le code suivant génère une erreur de débordement de mémoire :

TCHAR buffer[80];
AfxGetMainWnd()->GetWindowText(buffer, 100);

visual studio 2012 code analysis-getwindowtext

 

L’analyse du code multitâche

 

L’analyse de code C++ permet de trouver des bugs dans du code multitâche. Plus de 100 règles concernent particulièrement ce sujet.

Un des problèmes communs lors de l’écriture de code multitâche est l’accès concurrent à une variable. Il faut en protéger l’accès, et une erreur à ce niveau peut provoquer un écrasement de la valeur ou un blocage de l’application (dead lock).

L’exemple de code suivant utilise un entier count protégé par une section critique cs.

_Garded_by_ est un mot-clé SAL qui indique qu’une variable doit être protégée par un verrou, ici la section critique cs. Ainsi l’analyse de code pourra détecter les accès à count qui ne sont pas protégés par cs.

typedef struct
{
    _Garded_by_(cs) int count;
    CRITICAL_SECTION cs;
}COUNT; 

bool CMFCApplication1Doc::UpdateCount(_In_ COUNT *p, _In_ int diff)
{
    EnterCriticalSection(&p->cs);
    if (p->count < diff)
        return false;
    p->count -= diff;
    LeaveCriticalSection(&p->cs);
    return !p->count;
}

Il y a deux problèmes, les deux repérés par l’analyse de code C++ : un return au milieu de la section critique, et un accès à count non protégé :

visual studio 2012 code analysis-critical section

 

L’analyse de code C++ – conclusion

 

Ces derniers exemples m’impressionnent… Pendant des dizaines d’années on a cherché à la main les débordements de zone mémoire ! Et ces exemples ne montrent qu’une partie de l’impressionnante puissance de l’analyseur de code C++.

L’une des conclusions que l’on tire de l’utilisation de l’analyseur de code sur un programme conséquent est qu’il ne faut plus utiliser les pointeurs *, les opérateurs new et delete, mais les remplacer par les pointeurs intelligents de C++11. Si vous n’êtes pas un adepte de shared_ptr ou unique_ptr, il faut absolument vous y mettre. Non seulement il n’y a plus à utiliser delete, mais l’usage de ces pointeurs intelligents produit du code beaucoup plus sur. Je pense à certains énormes projets en C++ que j’ai pu croiser, et qui gagneraient énormément en sécurité (plantages et fuites mémoires) à utiliser les pointeurs intelligents.

Entre les nouveautés de C++11 et ce nouvel analyseur de code pour C++ natif, il va bientôt devenir difficile d’écrire du code C++ avec des bugs !

Rappelons juste, pour la fin, qu’une version allégée de l’analyseur de code C++ sera disponible gratuitement dans Visual C++ express 11 (la prochaine version de Visual C++), et que la version complète, avec toutes les règles, sera incluse dans toutes les versions payantes de Visual Studio 11.