Sie sind auf Seite 1von 38

Guide Superu de programmation en langage C

Matthieu Herrb Version 2.2 Dcembre 2001

Centre National de la Recherche Scientique Laboratoire dAnalyse et dArchitecture des Systmes

Copyright 1997-1999, Matthieu Herrb. Ce document peut tre imprim et distribu gratuitement dans sa forme originale (comprenant la liste des auteurs). Sil est modi ou que de des extraits sont utiliss lintrieur dun autre document, alors la liste des auteurs doit inclure tous les auteurs originaux et celui (ou ceux) qui a (qui ont) modi le document. Copyright 1997-1999, Matthieu Herrb. This document may be printed and distributed free of charge in its original form (including the list of authors). If it is changed or if parts of it are used within another document, then the author list must include all the original authors AND that author (those authors) who has (have) made the changes.

Table des matires


I I.1 Quelques piges du langage C Fautes de frappe fatales . . . . . . . . . . . . . . . . . . . . . . . I.1.1 Mlange entre = et == . . . . . . . . . . . . . . . . . . . . I.1.2 Tableaux plusieurs dimensions . . . . . . . . . . . . . . . I.1.3 Oubli du break dans les switch . . . . . . . . . . . . . . . . . I.1.4 Passage des paramtres par adresse . . . . . . . . . . . . . . Problmes de calcul sur les nombres rels . . . . . . . . . . . . . I.2.1 galit de rels . . . . . . . . . . . . . . . . . . . . . . . . . I.2.2 Problmes darrondis . . . . . . . . . . . . . . . . . . . . . . I.2.3 Absence de dclaration des fonctions retournant des doubles Style des dclarations de fonctions . . . . . . . . . . . . . . . . . Variables non initialises . . . . . . . . . . . . . . . . . . . . . . . Ordre dvaluation indni . . . . . . . . . . . . . . . . . . . . . . Allocation dynamique de la mmoire . . . . . . . . . . . . . . . . I.6.1 Rfrence une zone mmoire non alloue . . . . . . . . . . I.6.2 Rfrence une zone mmoire libre . . . . . . . . . . . . I.6.3 Libration dune zone invalide . . . . . . . . . . . . . . . . . I.6.4 Fuites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Chanes de caractres . . . . . . . . . . . . . . . . . . . . . . . . . I.7.1 Dbordement dune chane de caractres . . . . . . . . . . . I.7.2 criture dans une chane en mmoire statique . . . . . . . . Pointeurs et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . I.8.1 Assimilation dun pointeur et dun tableau statique . . . . . I.8.2 Appel de free() sur un tableau . . . . . . . . . . . . . . . . Entres/sorties standard . . . . . . . . . . . . . . . . . . . . . . . I.9.1 Contrle des paramtres de printf et scanf . . . . . . . . . . I.9.2 Lecture de chanes de caractres . . . . . . . . . . . . . . . I.9.3 Lecture de donnes binaires . . . . . . . . . . . . . . . . . . Gestion des signaux . . . . . . . . . . . . . . . . . . . . . . . . . . Processeurs 64 bits . . . . . . . . . . . . . . . . . . . . . . . . . . I.11.1 Absence de dclarations des fonctions . . . . . . . . . . . . I.11.2 Manipulation de pointeurs . . . . . . . . . . . . . . . . . . . Pr-processeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 6 6 7 7 8 8 9 9 9 10 11 11 11 11 12 13 13 13 13 14 14 15 15 15 15 16 16 17 17 18 18 18

I.2

I.3 I.4 I.5 I.6

I.7

I.8

I.9

I.10 I.11

I.12

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

TABLE DES MATIRES

II II.1 II.2 II.3 II.4 II.5 II.6 II.7 III

Un peu dalgorithmique Introduction . . . . . . . . . . . . . . Allocation dynamique de la mmoire Pointeurs . . . . . . . . . . . . . . . Listes . . . . . . . . . . . . . . . . . Ensembles . . . . . . . . . . . . . . . Tris et recherches . . . . . . . . . . . Chanes de caractres . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

19 19 20 20 21 21 21 22 23 24 24 24 25 25 26 26 27 28 28 28 29 29 30 30 30 32 32 33 34 34 35 35 35 36

III.1

Crer des programmes srs Quelques rappels sur la scurit informatique III.1.1 Vol de mot de passe . . . . . . . . . . . III.1.2 Chevaux de Troie . . . . . . . . . . . . . III.1.3 Dni de service . . . . . . . . . . . . . . III.2 Comment exploiter les bugs dun programme III.3 Rgles pour une programmation sre . . . . . III.3.1 viter les dbordements . . . . . . . . . III.3.2 Se mer des donnes . . . . . . . . . . III.3.3 Traiter toutes les erreurs . . . . . . . . . III.3.4 Limiter les fonctionnalits . . . . . . . . III.3.5 Se mer des bibliothques . . . . . . . III.3.6 Bannir les fonctions dangereuses . . . . III.4 Pour aller plus loin... . . . . . . . . . . . . . . Questions de style Commentaires et documentation IV.1.1 Commentaires . . . . . . . IV.1.2 Documentation . . . . . . . IV.2 Typologie des noms . . . . . . . . IV.3 Dclarations . . . . . . . . . . . . IV.4 Indentation . . . . . . . . . . . . IV.5 Boucles . . . . . . . . . . . . . . IV.6 Expressions complexes . . . . . . IV.7 Conversion de types . . . . . . . IV.8 Assertions . . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

IV

IV.1

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

Rfrences bibliographiques

Introduction
Ce document a pour but de rappeler certaines rgles et techniques que tout le monde connat pour dvelopper une application de taille srieuse en langage C. Les audits mens sur des gros logiciels libres, en particulier, ceux mens par Theo De Raadt et les autres dveloppeurs du projet OpenBSD 1 montrent que de nombreux programmeurs, mme renomms, ont tendance oublier ces rgles, ce qui cre des bugs. Certaines de ces erreurs peuvent ouvrir une brche dans la scurit du systme qui hberge le programme. Les sources dinformations sur le sujet sont nombreuses et aucune na empch les bugs connus dexister ; cest pourquoi je pense que ce document est superu. En particulier, La Foire Aux Questions (FAQ) du forum Usenet comp.lang.c constitue une source dinformation beaucoup plus riche, mais en anglais. Elle est accessible par lURL : http://www.eskimo.com/~scs/C-faq/top.html Cette FAQ existe aussi sous forme de livre [1]. La bibliographie contient une liste dautres articles ou ouvrages dont la lecture vous sera plus protable. Si toutefois vous persistez vouloir lire ce document, voici un aperu de ce que vous y trouverez : Le premier chapitre fait un tour parmi les problmes les plus souvent rencontrs dans des programmes C. Le deuxime chapitre donne quelques conseils sur le choix et le type dalgorithmes utiliser pour viter de rinventer la roue en permanence. Le troisime chapitre sintresse laspect scurit informatique des algorithmes et de leurs implmentations, sachant que cet aspect doit tre intgr au plus tt dans lcriture dun programme. Enn, le quatrime chapitre traite du style du code source : indentation, choix des noms de variables, commentaires,... Toutes les recomendations prsentes ici ne sont pas des obligations respecter tout prix, mais suivre ces recommandations permettra de gagner du temps dans la mise au point dune application et facilitera sa maintenance.

1 http://www.openbsd.org/

Chapitre I Quelques piges du langage C

Le but de ce document nest pas de faire un cours de langage C. Il y a des livres pour a. Mais entre les bases du langage et la mise en uvre concrte de ses fonctionnalits, il y a parfois quelques dicults. La plupart des erreurs dcrites ici ne sont pas dtectes la compilation. Certaines de ces erreurs conduisent systmatiquement un plantage du programme en cours dexcution ou un rsultat faux, alors que dautres conduisent des situations que la norme du langage C dnit comme comportements indtermins , cest--dire que nimporte quoi peut se produire, selon les choix de programmation du compilateur ou du systme. Parfois, dans ce cas, le programme en question lair de fonctionner correctement. Une erreur ne se produira que dans certaines conditions, ou bien lorsque lon tentera de porter le programme en question vers un autre type de machine.

I.1

Fautes de frappe fatales

Cette section commence par attirer lattention du lecteur sur quelques erreurs dinattention qui peuvent tre commises lors de la saisie dun programme et qui ne seront pas dtectes par la compilation mais produiront ncessairement des erreurs plus ou moins faciles dtecter lexcution.

I.1.1

Mlange entre = et ==

Cette erreur est lune des plus frquentes, elle provient de la syntaxe du langage combine linattention du programmeur. Elle peut tre trs dicile dtecter. Cest pourquoi, il est indispensable davoir le problme lesprit en permanence. Pour ceux qui ne sauraient pas de quoi il est question ici, rappelez-vous que x = 0 est une expression valide en C qui aecte x la valeur zro et qui retourne la valeur aecte, cest dire zro. Il est donc parfaitement lgal dcrire : 6

I.1. Fautes de frappe fatales

if (x = 0) { /* traitement lorigine */ ... } else { /* traitement des autres valeurs */ ... } Malheureusement le traitement particulier de x = 0 ne sera jamais appel, et en plus dans le traitement des x = 0 la variable x vaudra 0 ! Pour ceux qui ne lauraient pas vu, il fallait crire : if (x == 0) { /* traitement lorigine */ ... } else { /* traitement des autres valeurs */ ... } Certains suggrent dutiliser systmatiquement la construction suivante, qui a le mrite de provoquer une erreur la compilation si lon oublie lun des = : if (0 == x) . Cependant, ce problme nest pas limit au cas de la comparaison avec une constante. Il se pose aussi lorsque lon veut comparer deux variables.

I.1.2

Tableaux plusieurs dimensions

En C les indices dun tableau plusieurs dimensions scrivent avec autant de paires de crochets quil y a dindices. Par exemple pour une matrice deux dimensions on crit : double mat[4][4]; x = mat[i][j]; Le risque derreur provient du fait que la notation mat[i,j] (qui est employe dans dautres langages) est galement une expression valide en langage C.

I.1.3

Oubli du break dans les switch

Noubliez pas le break la n de chaque case dans une instruction switch. Si le break est absent, lexcution se poursuit dans le case suivant, ce qui nest en gnral pas le comportement voulu. Par exemple : void print_chiffre(int i) { switch (i) { case 1: printf("un");

Chapitre I. Quelques piges du langage C

case 2: printf("deux"); case 3: printf("trois"); case 4: printf("quatre"); case 5: printf("cinq"); case 6: printf("six"); case 7: printf("sept"); case 8: printf("huit"); case 9: printf("neuf"); } } Dans cette fonction tous les break ont t oublis. Par consquent, lappel print_chiffre(7); achera : septhuitneuf Ce qui nest peut-tre pas le rsultat escompt.

I.1.4

Passage des paramtres par adresse

En langage C, les paramtres des fonctions sont toujours passs par valeur : il sont copis localement dans la fonction. Ainsi, une modication dun paramtre dans la fonction reste localise cette fonction, et la variable de lappelant nest pas modie. Pour pouvoir modier une variable de la fonction appelante, il faut raliser un passage par adresse explicite. Par exemple, une fonction qui permute deux nombres rels aura le prototype : void swapf(float *x, float *y); Et pour permuter les deux nombres x et y, on crira : swapf(&x, &y); Si on a oubli dinclure le prototype de la fonction swap() avant de lappeler, le risque est grand doublier de passer les adresses des variables. Cest une erreur frquement commise avec la fonction scanf() et ses variantes.

I.2

Problmes de calcul sur les nombres rels

Avant dattaquer un programme quelconque utilisant des nombres rels, il est indispensable davoir pris conscience des problmes fondamentaux induits par la reprsentation approche des nombres rels sur toute machine informatique [2]. Dans de nombreux cas, la prise en compte de ces dicults se fait simplement, mais il peut savrer ncessaire davoir recours des algorithmes relativement lourds, dans le cas par exemple ou la prcision de la reprsentation des rels par la machine nest pas susante [3].

I.2. Problmes de calcul sur les nombres rels

I.2.1

galit de rels

Sauf coup de chance, lgalit parfaite ne peut tre obtenue dans le monde rel, il faut donc toujours tester lgalit prs. Mais il faut faire attention de choisir un qui soit en rapport avec les valeurs tester. Nutilisez pas : double a, b; ... if(a == b) /* Faux */ Mais quelque-chose du genre : #include <math.h> if(fabs(a - b) <= epsilon * fabs(a)) qui permet un choix de epsilon indpendant de lordre de grandeur des valeurs comparer ( condition que epislon soit strictement positif).

I.2.2

Problmes darrondis

La bibliothque standard C propose des fonctions pour convertir des nombres rels en entiers. oor() arrondit lentier immdiatement infrieur, ceil() arrondit lentier immdiatement suprieur. Ces fonctions comportent deux piges : elles retournent un type double. Il ne faut pas oublier de convertir explicitement leur valeur en type int lorsquil ny a pas de conversion implicite. dans le cas dun argument ngatif, elles ne retournent peut-tre pas la valeur attendue : floor(-2.5) == -3 et ceil(-2.5) == -2. La conversion automatique des types rels en entiers retourne quant elle lentier immdiatement infrieur en valeur absolue : (int)-2.3 == -2. Pour obtenir un arrondi lentier le plus proche on peut utiliser la macro suivante : #define round(x) (int)((x)>0?(x)+0.5:(x)-0.5)

I.2.3

Absence de dclaration des fonctions retournant des doubles

Le type double occupe sur la plupart des machines une taille plus importante quun int. Comme les fonctions dont le type nest pas dclar explicitement sont considres comme retournant un int, il y aura problme si la valeur retourne tait en ralit un double : les octets supplmentaires seront perdus. Cette remarque vaut pour deux types de fonctions : les fonctions systme retournant des doubles. Limmense majorit de ces fonctions appartiennent la bibliothque mathmatique et sont dclares dans le chier math.h. Une exception noter est la fonction strtod() dnie dans stdlib.h. les fonctions des programmes utilisateur. Normalement toutes les fonctions doivent tre dclares avant dtre utilises. Mais cette dclaration nest pas rendue obligatoire par le compilateur. Dans le cas de fonctions retournant un type plus grand quun int, cest indispensable. Utilisez des chiers den-tte (.h) pour dclarer le type de toutes vos fonctions.

10

Chapitre I. Quelques piges du langage C

I.3

Style des dclarations de fonctions

Lexistence de deux formes direntes pour la dclaration des paramtres des fonctions est source de problmes diciles trouver. Style K&R En C classique (galement appel Kernigan et Ritchie ou K&R pour faire plus court), une fonction se dclare sous la forme [4] : int fonction(a) int a; { /* corps de la fonction */ ... } Et la seule dclaration possible dune fonction avant son utilisation est celle du type retourn sous la forme : int fonction(); Dans ce cas, tous les paramtres formels de types entiers plus petits que long int sont promus en long int et tous les paramtres formels de types rels plus petit que double sont promus en double Avant lappel dune fonction, les conversions suivantes sont eectues sur les paramtres rels1 : les types entiers (char, short, int) sont convertis en long int les types rels (oat) sont convertis en double Style ANSI La norme ansi concernant le langage C a introduit une nouvelle forme de dclaration des fonctions [5] : int fonction(int a) { /* corps de la fonction */ ... } Avec la possibilit de dclarer le prototype complet de la fonction sous la forme : int fonction(int a); Si on utilise ce type de dclaration, aucune promotion des paramtres nest eectue dans la fonction. De mme, si un prototype ansi de la fonction apparat avant son appel, les conversions de types eectues convertiront les paramtres rels vers les types dclars dans le prototype.
1 ici

rel sapplique paramtre, en opposition formel et non type (en opposition entier )

I.4. Variables non initialises

11

Mlange des styles Si aucun prototype ansi dune fonction (de la forme int fonction(int a)) na t vu avant son utilisation, le compilateur peut (selon les options de compilation) eectuer automatiquement les conversions de type cites plus haut, alors quune fonction dclare selon la convention ansi attend les paramtres avec le type exact qui apparat dans la dclaration. Si on mlange les prototypes ansi et les dclarations de fonctions sous forme K&R, il est trs facile de produire des programmes incorrects ds que le type des paramtres est char, short ou oat.

I.4

Variables non initialises

Les variables dclares lintrieur des fonctions ( automatiques ) sont alloues sur la pile dexcution du langage et ne sont pas initialises. Par contre les variables dclares statiques sont garanties initialises zro.

I.5

Ordre dvaluation indni

Sauf exceptions, le C ne dnit pas lordre dvaluation des lments de mme prcdence dans une expression. Pire que a, la norme ansi dit explicitement que le rsultat dune instruction qui dpend de lordre dvaluation nest pas dni si cet ordre nest pas dni. Ainsi, leet de linstruction suivante nest pas dni : a[i] = i++; voici un autre exemple de code dont le comportement nest pas dni : int i = 3; printf("%d\n", i++ * i++); Chaque compilateur peut donner nimporte quel rsultat, mme le plus inattendu dans ces cas. Ce genre de construction doit donc tre banni. Les parenthses ne permettent pas toujours de forcer un ordre dvaluation total. Dans ce cas, il faut avoir recours des variables temporaires. Lexception la plus importante cette rgle concerne les oprateurs logiques && et ||. Non seulement lordre dvaluation est garanti, mais en plus lvaluation est arrte ds que lon a rencontr un lment qui xe dnitivement la valeur de lexpression : faux pour && ou vrai pour ||.

I.6

Allocation dynamique de la mmoire

Un des mcanismes les plus riches du langage C est la possibilit dutiliser des pointeurs qui, combine avec les fonctions malloc() et free() ouvre les portes de lallocation dynamique de la mmoire. Mais en raison de la puissance dexpression du langage et du peu de vrications ralises par le compilateur, de nombreuses erreurs sont possibles.

I.6.1

Rfrence une zone mmoire non alloue

La valeur dun pointeur dsigne ladresse de la zone mmoire vers laquelle il pointe. Si cette adresse ne correspond pas une zone de mmoire utilisable par le programme en cours, une

12

Chapitre I. Quelques piges du langage C

erreur (segmentation fault) se produit lexcution du programme. Mais, mme si ladresse est valide et ne produit pas derreur, il faut sassurer que la valeur du pointeur correspond une zone alloue correctement (avec malloc(), ou sous forme statique par une dclaration de tableau) par le programme. Lexemple le plus frquent consiste rfrencer le pointeur NULL, qui par construction ne pointe vers aucune adresse valable. Voici un autre exemple de code invalide : int *iptr; *iptr = 1234; printf("valeur : %d\n", *iptr); iptr nest pas initialis et laectation *iptr = 1234; ne linitialise pas mais crit 1234 une adresse alatoire.

I.6.2

Rfrence une zone mmoire libre

partir du moment o une zone mmoire a t libre par free(), il est interdit dadresser son contenu. Si cela se produit, on ne peut pas prdire le comportement du programme. Cette erreur est frquente dans quelques cas courants. Le plus classique est la libration des lments dune liste chane. Lexemple suivant nest PAS correct : typedef struct LISTE { int valeur; struct LISTE *suivant; } LISTE; void libliste(LISTE *l) { for (; l != NULL; l = l->suivant) { free(l); } /* for */ } En eet la boucle for excute l = l->suivant aprs la libration de la zone pointe par l. Or l->suivant rfrence le contenu de cette zone qui vient dtre libre. Une version correcte de libliste() est : void libliste(LISTE *l) { LISTE *suivant; for (; l != NULL; l = suivant) { suivant = l->next; free(l); } /* for */ }

I.7. Chanes de caractres

13

I.6.3

Libration dune zone invalide

Lappel de free() avec en argument un pointeur vers une zone non alloue, parce que le pointeur est initialise vers une telle zone, (cf I.6.1) ou parce que la zone a dj t libre (cf I.6.2) est une erreur. L aussi le comportement du programme est indtermin.

I.6.4

Fuites

On dit quil y a fuite de mmoire lorsquun bloc allou par malloc nest plus rfrenc par aucun pointeur, et quil ne peut donc plus tre libr. Par exemple, la fonction suivante, cense permuter le contenu de deux blocs mmoire, fuit : elle perd le pointeur sur la zone tampon utilise, sans la librer. void mempermute(void *p1, void *p2, size_t length) { void *tmp = malloc(length); memcpy(tmp, p1); memcpy(p1,p2); memcpy(p2,tmp); } En plus cette fonction ne teste pas le rsultat de malloc(). Si cette dernire fonction retournait NULL, on aurait dabord une erreur de rfrence vers une zone invalide. Pour corriger cette fonction, il sut dajouter free(tmp); la n du code. Mais dans des cas rels, garder la trace des blocs mmoire utiliss, pour pouvoir les librer nest pas toujours aussi simple.

I.7

Chanes de caractres

Les chanes de caractres sont gres par lintermdiaire des pointeurs vers le type char. Une particularit syntaxique permet dinitialiser un pointeur vers une chane constante en zone de mmoire statique. Toutes les erreurs lies lallocation mmoire dynamique peuvent se produire plus quelques autres :

I.7.1

Dbordement dune chane de caractres

Cela se produit principalement avec les fonctions telles que gets(), strcpy(), strcat() ou sprintf() qui ne connaissent pas la taille de la zone destination. Si les donnes crire dbordent de cette zone, le comportement du programme est indtermin. Ces quatre fonctions sont viter au maximum. La pire de toutes est gets() car il ny a aucun moyen dempcher lutilisateur du programme de saisir une chane plus longue que la zone alloue en entre de la fonction. Il existe des fonctions alternatives, utiliser la place : fgets() remplace gets() strlcpy() remplace strcpy() strlcat() remplace strcat()

14

Chapitre I. Quelques piges du langage C

snprintf() remplace sprintf(). Malheureusement cette fonction nest pas disponible sur tous les systmes. Mais il existe un certain nombre dimplmentation domaine public de snprintf(). Exemple : Le programme suivant nest pas correct : char buf[20]; gets(buf); if (strcmp(buf, "quit") == 0) { exit(0); } Utilisez plutt : char buf[20]; fgets(buf, sizeof(buf), stdin); if (strcmp(buf, "quit") == 0) { exit(0); } Il est noter que les fonctions strncat() et strncpy() souvent prsentes comme remplacement srs de strcat() et strcpy() ne le sont pas tant que a. Ces fonctions tronquent les arguments quelles copient sans forcment insrer un caractre NUL pour marquer la n du rsultat. strlcpy() et strlcat() ont t introduites pour corriger ce dfaut. Ces fonctions terminent toujours la chane rsultat par un NUL.

I.7.2

criture dans une chane en mmoire statique

La plupart des compilateurs et des diteurs de liens modernes stockent les chanes de caractres initialises lors de la compilation avec des constructions du genre : char *s = "ceci est une chane constante\n";

dans une zone mmoire non-modiable. Cela signie que la fonction suivante (par exemple) provoquera une erreur lexcution sur certaines machines : s[0] = toupper(s[0]); Lutilisation du mot-cl const permet de dtecter cette erreur la compilation : const char *s = "ceci est une chane constante\n";

I.8

Pointeurs et tableaux

Une autre puissance dexpression du langage C provient de la possibilit dassimiler pointeurs et tableaux dans certains cas, notamment lors du passage des paramtres aux fonctions. Mais il arrive que cette facilit provoque des erreurs.

I.9. Entres/sorties standard

15

I.8.1

Assimilation dun pointeur et dun tableau statique

Il arrive mme aux programmeurs expriments doublier que lquivalence entre pointeurs et tableaux nest pas universelle. Par exemple, il y a une dirence importante entre les deux dclarations suivantes : char tableau[] = "ceci est une chaine"; char *pointeur = "ceci est une chaine"; Dans le premier cas, on alloue un seul objet, un tableau de 20 caractres et le symbole tableau dsigne directement le premier caractre. Dans le second cas, une variable de type pointeur nomme pointeur est alloue dabord, puis une chane constante de 20 caractres et ladresse de cette chane est stocke dans la variable pointeur.

I.8.2

Appel de free() sur un tableau

Un tableau est une zone mmoire alloue soit statiquement la compilation pour les variables globales, soit automatiquement sur la pile pour les variables locales des fonctions. Comme laccs ses lments se fait de manire qui ressemble beaucoup laccs aux lments dune zone de mmoire alloue dynamiquement avec malloc(), on peut les confondre au moment de rendre la mmoire au systme et appeler par erreur free() avec un tableau en paramtre. Si les prototypes de free() et malloc() sont bien inclus dans la porte de la fonction en cours, cette erreur doit au minimum provoquer un warning la compilation.

I.9

Entres/sorties standard

La bibliothque de gestion des entres et sorties standard du langage C a t conue en mme temps que les premires versions du langage. Depuis la ncessit de conserver la compatibilit avec les premires versions de cette bibliothque ont laiss subsister un certain nombre de sources derreur potentielles.

I.9.1

Contrle des paramtres de printf et scanf

Les fonctions printf() et scanf() ainsi que leurs drives (fprintf(), fscanf(), sprintf(), sscanf(), etc.) acceptent un nombre variable de paramtres de types dirents. Cest la chane de format qui indique lors de lexcution le nombre et le type exact des paramtres. Le compilateur ne peut donc pas faire de vrications. Ainsi, ces fonctions auront un comportement non prvisible si : le nombre de paramtres pass est infrieur au nombre de spcications de conversion (introduites par %) dans la chane de format, le type dun paramtre ne correspond pas au type indiqu par la spcication de conversion correspondante, la taille dun paramtre est infrieure la taille attendue par la spcication de conversion correspondante. Certains compilateurs (dont gcc) appliquent un traitement particulier ces fonctions et vrient le type des paramtres lorsque le format est une chane constante qui peut tre analyse la compilation. Rappelons les lments les plus frquemment rencontrs :

16

Chapitre I. Quelques piges du langage C

les paramtres de scanf() doivent tre les adresses des variables lire, pas les variables elles-mmes. Il faut crire : scanf("%d", &n); et non : scanf("%d", n); pour lire un double avec scanf(), il faut utiliser la spcication de format %lf, par contre pour lacher avec printf() %f sut. Lexplication de cette subtilit se trouve la section I.3. vous de la trouver.

I.9.2

Lecture de chanes de caractres

La lecture et lanalyse de texte formant des chanes de caractres est un problme souvent mal rsolu. Le cadre thorique gnral pour raliser cela correctement est celui de lanalyse lexicographique et syntaxique du texte, et des outils existent pour produire automatiquement les fonctions ralisant ces analyses. Nanmoins, dans beaucoup de cas on peut se contenter de solutions plus simples en utilisant les fonctions scanf(), fgets() et getchar(). Malheureusement ces fonctions prsentent quelques subtilits qui rendent leur usage problmatique. scanf("%s", s) ; lit un mot de lentre standard, spar par des espaces, tabulations ou retours la ligne. Cette fonction saute les sparateurs trouvs la position courante jusqu trouver un mot et sarrte sur le premier sparateur trouv aprs le mot. En particulier si le sparateur est un retour la ligne, il reste dans le tampon dentre. gets(s) lit une ligne complte, y compris le retour la ligne nal. c = getchar() ; et scanf("%c", &c) ; lisent les caractres un un. La seul dirence entre les deux est leur manire de retourner les erreurs en n de chier. Le mlange de ces trois fonctions peut produire des rsultats inattendus. En particulier appel getchar() ou gets() aprs scanf("%s", s); retourneront toujours comme premier caractre le sparateur qui a termin le mot lu par scanf(). Si ce sparateur est un retour chariot, gets() retournera une ligne vide. Pour lire des textes comportant des blancs et des retours la ligne, utilisez exclusivement fgets() ou getchar(). Lutilisation de scanf() avec le format %s est rserver la lecture de chiers structurs simples comportant des mots-cls spars par des espaces, des tabulations ou des retours la ligne.

I.9.3

Lecture de donnes binaires

Les fonctions fread() et fwrite() de la bibliothque des entres/sorties standard permettent de lire et dcrire des donnes binaires directement, sans les coder en caractres achables. Les chiers de donnes produits ainsi sont plus compacts et plus rapides lire, mais les risques derreur sont importants. Le rdacteur et le lecteur doivent tre absolument daccord sur la reprsentation des types de donnes de base ainsi crites. Il est fortement conseill de prvoir une signature du chier permettant de vrier quil respecte bien le format attendu par lapplication qui va le lire.

I.10. Gestion des signaux

17

I.10

Gestion des signaux

Un signal peut interrompre lexcution dun programme nimporte quel moment. On ne connait donc pas ltat des variables lors de lexcution de la routine de traitement du signal. Par consquent, seules des fonctions rentrantes peuvent tre utilises dans un gestionnaire de signal. Chaque systme documente normalement la liste de ces fonctions autorises dans les gestionnaires de signaux. Appeler toute autre fonction reprsente un problme potentiel. Il est a noter que les fonctions de manipulation des entres/sorties standard du C (printf(), etc.) ne sont pas rentrantes. Appeler lune de ces fonctions est donc interdit, bien que pratique courante ; tous les programmes qui ne respectent pas cette rgle courrent le risque de plantages plus ou moins alatoires et plus ou moins frquents cause de cette violation des rgles. Parmi les autres fonctions interdites, notons malloc() et exit(). Utiliser _exit() la place de cette dernire. Voici la liste des fonctions autorises sur le systme OpenBSD : Fonctions standard _exit() access() cfsetispeed() cfsetospeed() close() creat() execve() fcntl() fsync() getegid() getpgrp() getpid() link() lseek() pathconf() pause() rename() rmdir() setuid() sigaction() sigllset() sigismember() sigsuspend() sleep() tcow() tcush() tcsetattr() tcsetpgrp() uname() unlink() write() Fonctions POSIX temps-rel aio_error() aio_return() aio_suspend() Fonctions ANSI C strcpy() strlcpy() strcat() strlcat() strncpy() syslog_r() strncat() Autres fonctions clock_gettime() fdatasync() sem_post() sigpause() sigqueue() sigset() timer_getoverrun() timer_gettime() timer_settime() alarm() chdir() dup() fork() geteuid() getppid() mkdir() pipe() setgid() sigaddset() signal() stat() tcgetattr() time() utime() cfgetispeed() chmod() dup2() fpathconf() getgid() getuid() mkfo() raise() setpgid() sigdelset() sigpending() sysconf() tcgetpgrp() times() wait() cfgetospeed() chown() execle() fstat() getgroups() kill() open() read() setsid() sigemptyset() sigprocmask() tcdrain() tcsendbreak() umask() waitpid()

I.11

Processeurs 64 bits

De plus en plus de processeurs ont une architecture 64 bits. Cela pose de nombreux problmes aux utilisateurs du langage C. Beaucoup de programmes ne se compilent plus ou pire,

18

Chapitre I. Quelques piges du langage C

se compilent mais ne sexcutent pas correctement lorsquils sont ports sur une machine pour laquelle les pointeurs sont plus grands quun int. En eet, sur les machines 64 bits, le modle le plus frquemment utilis est celui nomm LP64 : les types long int et tous les pointeurs sont stocks sur 64 bits, alors que le type int nutilise que 32 bits. La liste des cas ou lquivalence entiers/pointeurs est utilise implicitement est malheureusement trop longue et trop complexe pour tre cite entirement. On ne verra que deux exemples parmi les plus frquents.

I.11.1

Absence de dclarations des fonctions

De mme que pour le type double, partir du moment o les pointeurs nont plus la taille dun entier, toutes les fonctions passant des pointeurs en paramtre ou retournant des pointeurs doivent tre dclares explicitement (selon la norme ansi) avant dtre utilises. Les programmes qui staient contents de dclarer les fonctions utilisant des double rencontreront des problmes srieux.

I.11.2

Manipulation de pointeurs

Il arrive assez frquemment que des programmes utilisent le type int ou unsigned int pour stocker des pointeurs avant de les raecter des pointeurs, ou rciproquement quils stockent des entiers dans un type pointeur et cela sans avoir recours une union. Dans le cas ou les deux types nont pas la mme taille, on va perdre une partie de la valeur en le transformant en entier, avec tous les problmes imaginables lorsquil sagira de lui rendre son statut de pointeur.

I.12

Pr-processeur

Le pr-processeur du langage C (cpp) pose lui aussi certains problmes et peut tre lorigine de certaines erreurs. certaines erreurs de compilation inexplicables proviennent de la re-dnition par le prprocesseur dun symbole de votre programme. Nutilisez jamais didenticateurs risquant dtre utiliss aussi par un chier den-tte systme dans vos programmes. En particulier, tous les identicateurs commenant par le caractre soulign (_) sont rservs au systme. Lors de lcriture des macros, attention au nombre dvaluation des paramtres : puisquil sagit de macros et non de fonctions, les paramtres sont valus autant de fois quils apparaissent dans le corps de la macro, et non une seule fois au moment de lappel. Ainsi : #define abs(x) ((x)<0?-(x):(x)) value son argument deux fois. Donc abs(i++) incrmentera i deux fois. Comme dans lexemple prcdent, utilisez toujours des parenthses autour des paramtres dans lexpansion de la macro. Cest le seul moyen de garantir que les dirents oprateurs seront valus dans lordre attendu.

Chapitre II Un peu dalgorithmique

Le but de cette section est donner quelques pistes pour lutilisation des algorithmes que vous aurez appris par ailleurs, par exemple dans [6].

II.1

Introduction

Voici en introduction, quelques rgles donnes par R. Pike dans un article sur la programmation en C [7] et reprises rcemment dans un livre indispensable [8]. La plupart des programmes sont trop compliqus, cest--dire plus compliqus que ncessaire pour rsoudre ecacement le problme qui leur est pos. Pourquoi ? Essentiellement parce quils sont mal conus, mais ce nest pas le but de ce document de discuter de conception, le sujet est trop vaste. Mais limplmentation des programmes est galement trop souvent complique inutilement. L, les quelques rgles suivantes peuvent aider amliorer les choses. Rgle 1 On ne peut prdire o un programme va passer son temps. Les goulets dtranglement se retrouvent des endroits surprenants. Nessayez pas damliorer le code au hasard sans avoir dtermin exactement o est le goulet. Mesurez. Nessayez pas doptimiser un programme sans avoir fait des mesures srieuses de ses performances. Et refaites-les rgulirement. Si un algorithme plus sophistiqu napporte rien, revenez plus simple. Les algorithmes sophistiqus sont lents quand n est petit, et en gnral n est petit. Tant que vous ntes pas srs que n sera vraiment grand, nessayez pas dtre intelligents. (Et mme si n est grand, appliquez dabord la rgle 2). Les algorithmes sophistiqus sont plus bugus que les algorithmes simples, parce quils sont plus durs implmenter. Utilisez des algorithmes et des structures de donnes simples. Les structures de donnes suivantes permettent de traiter tous les problmes : 19

Rgle 2

Rgle 3

Rgle 4

20

Chapitre II. Un peu dalgorithmique

tableaux, listes chanes, tables de hachage, arbres binaires. Bien sr, il peut tre ncessaire de les combiner. Rgle 5 Les donnes dominent. Si vous avez choisi les bonnes structures de donnes, les algorithmes deviennent presque vidents. Les structures de donnes sont bien plus fondamentales que les algorithmes qui les utilisent. Ne rinventez pas la roue. Il existe des bibliothques de code disponibles librement sur le rseau Internet pour rsoudre la plupart des problmes algorithmiques classiques. Utilisez-les !. Il est presque toujours plus coteux de refaire quelque chose qui existe dj, plutt que daller le rcuprer et de ladapter.

Rgle 6

II.2

Allocation dynamique de la mmoire

En plus des piges cits au paragraphe I.6, on peut observer les rgles suivantes : vitez les allocations dynamiques dans les traitements critiques. Lchec de malloc() est trs dicile traiter. Tenez compte du cot dune allocation : malloc() utilise lappel systme sbrk() pour rclamer de la mmoire virtuelle au systme. Un appel systme est trs long excuter. Allouez dynamiquement les objets dont la taille nest pas connue davance et peut varier beaucoup. Il est toujours dsagrable dimposer une taille maximum un objet parce que le programmeur a prfr utiliser un tableau de taille xe. Librez au plus tt les objets non utiliss. Limitez les copies dobjets allous dynamiquement. Utilisez les pointeurs.

II.3

Pointeurs

Les pointeurs sont des outils puissants, mme si mal utiliss il peuvent faire de gros dgts, crivait Rob Pike dans [7] aprs stre plant un ciseau bois dans le pouce... Les pointeurs permettent des notations simples pour dsigner les objets. Considrons les deux expressions : nodep node[i] La premire est un pointeur vers un nud, la seconde dsigne un nud (le mme peuttre) dans un tableau. Les deux formes dsignent donc la mme chose, mais la premire est plus simple. Pour comprendre la seconde, il faut valuer une expression, alors que la premire dsigne directement un objet. Ainsi lusage des pointeurs permet souvent dcrire de manire plus simple laccs aux lments dune structure complexe. Cela devient vident si lon veut accder un lment de notre nud : nodep->type node[i].type

II.4. Listes

21

II.4

Listes

Utilisez de prfrence les listes simplement chanes. Elles sont plus faciles programmer (donc moins de risque derreur) et permettent de faire presque tout ce que lon peut faire avec des listes doublement chanes. Le formalisme du langage LISP est un trs bon modle pour lexpression des oprations sur les listes. Dnissez ou utilisez un formalisme gnrique pour les listes dune application.

II.5

Ensembles

Les ensembles de taille arbitrairement grande sont un peu diciles implmenter de manire ecace. Par contre, lorsquon a aaire des ensembles de taille raisonnable (moins dune centaine dlments) et connue davance, il est facile de les implmenter de manire plutt ecace : llment n de lensemble est reprsent par le bit n (mod 32) de llment n/32 dun tableau dentiers. Les oprations lmentaires sur ce type densemble se codent de manire triviale laide des oprateurs binaires &, |, ~.

II.6

Tris et recherches

Nessayez pas de programmer un tri. Il existe des algorithmes performants dans la bibliothque standard C (qsort()). Ou bien utilisez les algorithmes de Knuth [9]. Il en est de mme pour les problmes de recherche de donnes dans un ensemble. Voici un petit ventail des possibilits : recherche linaire : lalgorithme le plus simple. les lments nont pas besoin dtre tris. La complexit dune recherche est en O(n), si n est le nombre dlments dans la liste. Lajout dun lment se fait en temps constant. Cela reste la structure adapte pour tous les cas o le temps de recherche nest pas le principal critre. Cette mthode est propose par la fonction lsearch() de la bibliothque standard C. arbres binaires : les donnes sont tries et la recherche se fait par dichotomie. Les oprations de recherche et dajout se font en O(log(n)). Il existe de nombreuses variantes de ce type dalgorithmes, en particulier une version prte lemploi fait partie de la bibliothque C standard : bsearch(). tables de h-coding : une fonction de codage (appele fonction de hachage) associe une cl numrique chaque lment de lensemble des donnes (ou un sous-ensemble signicativement moins nombreux). Si la fonction de hachage est bien choisie, les ajouts et les recherches se font en temps constant. En pratique, la cl de hachage nest jamais unique et ne sert qu restreindre le domaine de recherche. Une seconde tape faisant appel une recherche linaire ou base darbre est ncessaire. Certaines versions de la bibliothque standard C proposent la fonction hsearch().

22

Chapitre II. Un peu dalgorithmique

II.7

Chanes de caractres

Les chanes de caractres donnent lieu de nombreux traitements et posent pas mal de problmes algorithmiques. Voici quelques conseils pour une utilisation saine des chanes de caractres : vitez le limiter arbitrairement la longueur des chanes : prvoyez lallocation dynamique de la mmoire en fonction de la longueur. si vous devez limiter la longueur dun chane, vriez quil ny a pas dbordement et prvoyez un traitement de lerreur. utilisez de prfrence les fonctions de lecture caractre par caractre pour les chanes. Elle permettent les meilleures reprises en cas derreur. prvoyez lavance linternationalisation de votre programme : au minimum, considrez que lensemble des caractres traiter est celui du codage ISO Latin-1. utilisez les outils lex et yacc pour les traitements lexicographiques et syntaxiques un peu complexes : vos programmes gagneront en robustesse et en ecacit.

Chapitre III Crer des programmes srs

Bien souvent un programmeur se satisfait dun programme qui a lair de fonctionner correctement parce que, apparemment, il donne un rsultat correct sur quelques donnes de test en entre. Que des donnes compltement errones en entre produisent des comportements anormaux du programme ne choque pas outre-mesure. Certaines catgories de programmes ne peuvent pas se contenter de ce niveau (peu lev) de robustesse. Le cas le plus frquent est celui de programmes orant des services un grand nombre dutilisateurs potentiels, sur le rseau Internet par exemple, auxquels on ne peut pas faire conance pour soumettre des donnes senses en entre. Cela est particulirement crucial pour les programmes sexcutant avec des privilges particuliers (par exemple excuts sous lidentit du super-utilisateur sur une machine Unix). En eet, les bugs causs par les dbordements de tableaux ou les autres cas dcrasement de donnes involontaires peuvent tre utiliss pour faire excuter un programme du code autre que celui prvu par le programmeur. Lorsque ce code est le fruit du hasard (des donnes brusquement interprtes comme du code), lexcution ne vas pas trs loin et se termine gnralement par une erreur de type bus error ou segmentation violation . Par contre, un programmeur mal intentionn peut utiliser ces dfauts en construisant des jeux de donnes dentre qui font que le code excut accidentellement ne sera plus rellement le fruit du hasard, mais bel et bien un morceau de programme prpar intentionnellement et destin en gnral nuire au systme ainsi attaqu [10]. Par consquent, les programmes ouverts lutilisation par le plus grand nombre doivent tre extrmement vigilants avec toutes les donnes quils manipulent. De plus, comme il est toujours plus facile de respecter les rgles en les appliquant ds le dbut, on gagnera toujours prendre en compte cet aspect scurit dans tous les programmes, mme si initialement ils ne semblent pas promis une utilisation sensible du point de vue scurit. Garnkel et Spaord ont consacr le chapitre 22 de leur livre sur la scurit Unix et Internet [11] lcriture de programme srs. Leurs recommandations sont souvent reprises par

23

24

Chapitre III. Crer des programmes srs

dautres auteurs. La robustesse supplmentaire acquise par un programme qui respecte les rgles nonces ici sera toujours un bnce pour lapplication nale, mme si les aspects scurit ne faisaient pas partie du cahier des charges initial. Un programme conu pour la scurit est en gnral aussi plus robuste face aux erreurs communes, dpourvues darrires penses malveillantes.

III.1

Quelques rappels sur la scurit informatique

Il y a principalement trois types dattaques contre un systme informatique : le vol de mots de passe, les chevaux de Troie et les dnis de service. Les virus informatiques se propagent en gnral par le mcanisme du cheval de Troie. Cette section se termine par lanalyse dun exemple simple dutilisation dun bug courant dans les programmes pour introduire un cheval de Troie.

III.1.1

Vol de mot de passe

Le vol de mot de passe permet dutiliser un compte informatique de manire non autorise sans avoir mettre en uvre de processus compliqu pour contourner les mcanismes didentication du systme. En plus de copier simplement le mot de passe crit sur un Post-It coll cot de lcran ou dans le tiroir du bureau dun utilisateur, les techniques utilises pour voler un mot de passe sont : linterception dune communication o le mot de passe transite en clair, le dchirement dun mot de passe cod rcupr soit par interception soit parce quil tait laiss lisible explicitement, lintroduction dun cheval de Troie dans le code dun programme charg de lidentication.

III.1.2

Chevaux de Troie

La technique du cheval de Troie remonte lantiquit, mais linformatique lui a donn une nouvelle jeunesse. Dans ce domaine on appelle cheval de Troie un bout de code insr dans un programme ou dans des donnes qui ralise linsu de lutilisateur une tche autre que celle ralise par le programme ou les donnes qui lui servent de support. Un cheval de Troie peut accomplir plusieurs rles : ouvrir un accs direct au systme (un shell interactif). Ctait la fonction du cheval de Troie original. se reproduire an de sinstaller sur dautres programmes ou dautres donnes an de pntrer dautres systmes. agir sur les donnes normales du programme : les dtruire, les modier, les transmettre un tiers,... Le cheval de Troie peut tre introduit de deux manires dans le systme : il peut tre prsent ds le dpart dans le code du systme et tre activ distance, ou bien il peut tre transmis de lextrieur par une connexion a priori autorise au systme.

III.2. Comment exploiter les bugs dun programme

25

III.1.3

Dni de service

Ce type dattaque tendance se multiplier. Il peut constituer une simple gne pour les utilisateurs dun service, mais peut galement servir de menace pour un chantage de plus grande ampleur. Le dni de service consiste bloquer compltement un systme informatique en exploitant ses bugs partir dune connexion a priori autorise. On peut bloquer un programme simplement en provoquant son arrt suite un bug, si le systme na pas prvu de mcanisme pour dtecter larrt et redmarrer automatiquement le programme en question. Dautre types de blocages sont possibles en provoquant par exemple le passage dun programme dans un tat cul de sac dans lequel il continuera apparatre comme fonctionnel au systme de supervision, mais o il ne pourra pas remplir son rle. Enn, il peut tre possible de provoquer un dni de service en gnrant avec un petit volume de donnes en entre une charge de travail telle sur le systme que celui-ci ne pourra plus rpondre normalement. En gnral le dni de service est une forme dgnre du cheval de Troie : dfaut de pouvoir dtourner un logiciel son prot, on va se contenter de le bloquer.

III.2

Comment exploiter les bugs dun programme

Un expos exhaustif des techniques utilises par les pirates informatiques pour exploiter les bugs ou les erreurs de conception dun logiciel dpasse largement le cadre de ce document. Nous allons simplement prsenter ici un exemple classique de bug qui tait prsent dans des dizaines (voire des centaines) de programmes avant que lon ne dcouvre la manire de lexploiter pour faire excuter un code arbitraire au programme qui le contient. Voici une fonction dun programme qui recopie la chane de caractres quelle reoit en argument dans un buer local (qui est situ sur la pile dexcution du programme). int maFonction(char *in) { char buf[80]; strcpy(buf, in); ... return 0; } Lors de lexcution de cette fonction, lorganisation des donnes sur la pile sera celle dcrite sur la gure III.2. Sur cette gure, il apparat clairement quun dbordement par le haut du tableau buf va craser sur la pile ladresse de retour de la fonction. Dans le cas dune erreur involontaire, cela conduira un saut une adresse invalide et provoquera donc une erreur du type segmentation violation. Par contre, on peut exploiter cette lacune pour faire excuter au programme en question un morceau de code arbitraire. Il sut pour cela de sarranger pour que les quatre premiers octets du dbordement soient une adresse donne sur la pile, dans la zone dja alloue, et que le reste

26

Chapitre III. Crer des programmes srs

Fig. III.1 Organisation des donnes sur la pile lors dans maFonction.

du dbordement soit un programme en code machine de la bonne longueur pour commencer pile ladresse que lon a mise dans ladresse de retour. Ainsi, la n de lexcution de maFonction, le processeur va dpiler une mauvaise adresse de retour et continuer son excution dans le code pass en excs dans le tableau in. Ainsi expose, cette technique semble assez rudimentaire et dicile mettre en uvre. Il est cependant courant de trouver sur Internet dans des forums spcialiss des scripts tout faits capabables dexploiter ce type de vulnrabilit dans les applications les plus courantes des systmes existants.

III.3

Rgles pour une programmation sre

La liste des rgles qui suivent nest pas imprative. Des programmes peuvent tre srs sans respecter ces rgles. Elle nest pas non plus susante : dune part parce quil est impossible de faire une liste exhaustive (on dcouvre chaque semaine de nouvelles manires dexploiter des programmes apparement innocents), et dautre part parce que seule une conception rigoureuse permet de protger un programme contre les erreurs volontaires ou non des utilisateurs.

III.3.1

viter les dbordements

Cest la rgle principale. Il ne faut jamais laisser la possibilit une fonction dcrire des donnes en dehors de la zone mmoire qui lui est destine. Cela peu paratre trivial, mais cest cependant des problmes de ce type qui sont utiliss dans la grande majorit des problmes de scurit connus sur Internet.

%&

'())

()

0'12

))

345

()

A B87 6

A6 @ 987          

"   ! $ #

 

III.3. Rgles pour une programmation sre

27

Il y a en gros deux techniques pour arriver cela. Je ne prendrai pas partie pour une technique ou une autre, par contre il est relativement vident que lon ne peut pas (pour une fois) les mlanger avec succs. Allouer dynamiquement toutes les donnes. En nimposant aucune limite statique la taille des donnes, le programme sadapte la taille relle des donnes et peut toujours les traiter sans risque derreur, condition que la machine dispose de susamment de mmoire. Cest dailleurs l que rside la dicult majeure de cette mthode. Lorsque la mmoire vient manquer, il est souvent trs dlicat de rcuprer lerreur proprement pour mettre un diagnostic clair, librer la mmoire dj alloue mais inutilisable et retourner dans un tat stable pour continuer fonctionner. Une autre dicult provient de la dicult dans certains cas de prdire la taille dune donne. On se trouve alors contraint de rallouer plusieurs fois la zone mmoire o elle est stocke, en provoquant autant de recopies de ces donnes, ce qui ralentit lexcution du programme. Travailler uniquement avec des donnes de taille xe alloues statiquement. Avec cette technique on sinterdit tout recours la fonction malloc() ou ses quivalents. Toutes les donnes extrieures sont soit tronques pour contenir dans les buers de lapplication soit traites squentiellement par morceaux susamment petits. Dans ce cas le traitement des erreurs est plus simple, par contre certains algorithmes deviennent complexes. Par exemple, comment raliser par exemple un tri de donnes arbitrairement grandes sans allocation dynamique de mmoire ? Il faut remarquer ici que les systmes de mmoire virtuelle aident gommer les dfauts respectifs des deux approches. La possibilit dallouer des quantits de mmoire bien suprieures la mmoire physique disponible aide retarder lapparition du manque de mmoire dans le premier cas. La facult de la mmoire virtuelle ne rclamer de la mmoire physique que pour les donnes rellement rfrences permet dans la seconde approche de prvoir des tailles de donnes statiques relativement grandes sans monopoliser trop de ressources si les donnes relles sont trs souvent beaucoup plus petites.

III.3.2

Se mer des donnes

Si vous considrez que lutilisateur de votre programme veut nuire votre systme, toutes les donnes quil fournit en entre sont potentiellement dangereuses. Votre programme doit donc analyser nement ses entres pour rejeter intelligemment les donnes suspectes, sans pnaliser outre mesure les utilisateurs honntes. Le premier point vrier a dj t voqu : il faut viter que la taille des donnes dentre ne provoque un dbordement interne de la mmoire. Mais il y a dautres points vrier : Vrier les noms des chiers. En eet lutilisateur peut essayer dutiliser les privilges potentiels de votre programme pour craser ou eacer des chiers systme. Vrier la syntaxe des commandes. Chaque fois quun programme commande lexcution dun script, il est possible dexploiter la syntaxe particulirement riche du shell Unix pour faire faire une commande autre chose que ce pourquoi elle est conue. Par exemple si un programme excute le code suivant pour renommer un chier : sprintf(cmd, "mv %s %s.bak", fichier, fichier); system(cmd); pour renommer un chier, si fichier contient la valeur :

28

Chapitre III. Crer des programmes srs

toto toto.bak ; cat /etc/passwd ; la fonction system() va excuter : mv toto toto.bak ; cat /etc/passwd ; toto toto.bak ; cat /etc/passwd; Ce qui va acher le chier des mots de passe crypts du systme en plus du rsultat initialement attendu. Ne pas laisser lutilisateur fournir une chane de format pour les fonctions de type printf(). Utiliser toujours un format xe ou correctement ltr. printf(buf); est dangeureux si buf est fournit par lutiliateur. Il faut toujours utiliser printf("%s", buf); la place. Vrier lidentit de lutilisateur. Dans tous les cas o cela est possible, lidentication des utilisateurs ne limite pas directement ce quil peuvent faire, mais aide mettre en place des mcanismes de contrle daccs.

III.3.3

Traiter toutes les erreurs

viter que des erreurs se produisent nest pas toujours possible. Prvoyez des traces des problmes de scurit, non visibles directement par lutilisateur. Par contre il est indispensable de prvenir lutilisateur de lexistence et de la nature de ces traces (loi Informatique et liberts + eet dissuasif)

III.3.4

Limiter les fonctionnalits

Certaines fonctionnalits dun programme peuvent tre dangereuses. Il faut y songer ds la spcication pour viter de fournir au pirate les moyens de parvenir facilement ses ns. possibilit de crer un shell achage de trop dinformation traitements sans limites

III.3.5

Se mer des bibliothques

Les rgles ci-dessus devraient tre respectes par les programmeurs qui ont ralis les bibliothques de fonctions utilises par votre application (bibliothque C standard, interface graphique, calcul matriciel,...). Mais en tes-vous certains ? Lexprience montre que des problmes du style de ceux voqus ici sont prsents dans de nombreux logiciels commerciaux, comme dans les logiciels libres. En gnral il nest pas possible dauditer tout le code des bibliothques utilises, soit parce que celui-ci nest pas disponible, soit simplement par manque de temps et/ou de comptence. Interrogez-vous sur le type dalgorithme utilis par les fonctions appeles par votre code. Si lun deux prsente des risques de dbordement interne, vriez deux fois les donnes, lentre et la sortie pour dtecter du mieux possible les ventuelles tentatives dattaque. En cas de doute sur un rsultat votre programme doit le signaler au plus tt, et ne pas utiliser ce rsultat.

III.4. Pour aller plus loin...

29

III.3.6

Bannir les fonctions dangereuses

Certaines fonctions de la bibliothque C standard sont intrinsquement dangereuses parce que leur smantique ne permet pas de respecter les rgles prsentes ci-dessus. Il faut donc sinterdire imprativement de les utiliser. Il peut y avoir des cas o ces fonctions peuvent tre utilises malgr tout de manire sre. A mon avis, mme dans ces cas, il faut les viter et leur prfrer une version sre. Cela facilite la vrication a posteriori du code, en vitant de provoquer des fausses alarmes qui peuvent tre coteuses dsamorcer. De plus, le raisonnement qui vous a amen considrer une utilisation dune fonction dangeureuse comme sre peut tre faux ou incomplet et donc le risque nest pas limin compltement. Ne pas utiliser gets() scanf() sprintf() strcat() strcpy() mktemp() remplacer par fgets() strtol(), strtod(), strtok(),... snprintf() strlcat() strlcpy() mkstemp() remarque(s) risque de dbordement idem risque de dbordement idem idem section critique : entre la cration et louverture du chier temporaire possibilit dexploiter le shell

system()

fork() & exec()

III.4

Pour aller plus loin...

Le numro davril 1998 du magazine lectronique Sunworld Online propose une mthodologie de dveloppement de logiciels srs (SDSDM Software Development Security Design Methodology). Larticle est accessible ladresse : http\unskip\penalty\@M\://www.sunworld.com/ swol-04-1998/swol-04-security.html Adam Shostack, consultant en scurit informatique, a rdig un guide pour la relecture du code destin tourner dans un rewall : http\unskip\penalty\@M\://www.homeport.org/ ~adam/review.html qui est souvent cit comme rfrence pour lvaluation des logiciels de scurit. Le projet FreeBSD propose un ensemble de recommendations au dveloppeurs qui souhaitent contribuer au projet : http\unskip\penalty\@M\://www.freebsd.org/security/programmers. html Matt Bishop a t lun des prcurseurs de la notion de programmation robuste [12]. Ses articles sur le sujet font rfrence. http\unskip\penalty\@M\://olympus.cs.ucdavis.edu/ ~bishop/secprog.html Enn, le chapitre de [11] consacr la programmation sre est disponible en ligne : ftp\ unskip\penalty\@M\://ftp.auscert.org.au/pub/auscert/papers/secure_programming_checklist

Chapitre IV Questions de style

Les rgles prsentes ici ne sont pas impratives, il sagit juste dexemples de bonnes habitudes qui facilitent la relecture dun programme. Ce qui est le plus important, cest de penser quun programme doit pouvoir tre lu et compris par quelquun dextrieur, qui ne connat pas forcment tout du logiciel dont est extrait le morceau quil relit. La lisibilit dun code source (qui peut sanalyser avec les rgles de la typographie) est une trs bonne mesure de sa qualit. Les guides de style pour les programmeurs C sont trs nombreux dans la littrature. Presque chaque ouvrage sur le langage propose son style. Toutes les grandes entreprises et les grands projets de logiciel ont leurs rgles. Un guide a servi de modle de nombreux programmeurs : le Indian Hill C Style and Coding Standards des Laboratoires Bell [13]. Ce guide a t amend et modi de trs nombreuses fois, mais sert de rfrence implicite commune de nombreux autres guides.

IV.1
IV.1.1

Commentaires et documentation
Commentaires

Bien commenter un programme est sans doute la chose la plus dicile de toute la chane de dveloppement. Cest un des domaines o le mieux est lennemi du bien sapplique avec le plus dvidence. De manire gnrale, le commentaire permet dapporter au lecteur dun programme une information que le programme lui-mme ne fournit pas assez clairement. Pour valuer la qualit dun commentaire, le recours aux rgles de la typographie est prcieux : un commentaire ne doit pas tre surcharg de ponctuation ou de dcorations. Plus il sera sobre, plus il sera lisible. Un bon commentaire a surtout un rle introductif : il prsente ce qui suit, lalgorithme utilis ou les raisons dun choix de codage qui peut paratre surprenant. 30

IV.1. Commentaires et documentation

31

Un commentaire qui paraphrase le code et vient aprs coup napporte rien. De mme, il vaut mieux un algorithme bien programm et bien prsent avec des noms de variables bien choisis pour aider sa comprhension, plutt quun code brouillon trs compact suivi ou prcd de cent lignes dexplications. Lexemple extrme du commentaire inutile est : i++; /* ajoute 1 a la variable i */

Dailleurs, avec ce genre de commentaires, le risque de voir un jour le code et le commentaire se contredire augmente considrablement. Enn, il est srement utile de rappeler quand crire les commentaires : tout de suite en crivant le programme. Prtendre repasser plus tard pour commenter un programme cest une promesse divrogne qui est trs dicile tenir. En-ttes de chiers Il peut tre utile davoir en tte de chaque chier source un commentaire qui attribue le copyright du contenu son propritaire, ainsi quun cartouche indiquant le numro de version du chier, le nom de lauteur et la date de la dernire mise jour, avant une description rapide du contenu du chier. Exemple (ici len-tte est maintenue automatiquement par RCS) : /*** *** Copyright (c) 1997,1998 CNRS-LAAS *** *** $Source: /home/matthieu/cvs/doc/cours/C/style.tex,v $ *** $Revision: 1.10 $ *** $Date: 2001/03/06 17:28:54 $ *** $Author: matthieu $ *** *** Fonctions de test sur les types du langage ***/ Remarque : la notice de copyright rdige ainsi na aucune valeur lgale en France. Pour une protection ecace dun programme, il faut le dposer auprs dun organisme spcialis. Nanmoins, en cas de conit, la prsence de cette notice peut constituer un lment de preuve de lorigine du logiciel. En-ttes de fonctions Avant chaque fonction, il est trs utile davoir un commentaire qui rappelle le rle de la fonction et de ses arguments. Il est galement trs utile dindiquer les direntes erreurs qui peuvent tre dtectes et retournes. Exemple : /** ** insertAfter - insre un bloc dans la liste aprs un bloc ** particulier ** ** paramtres: ** list: la liste dans laquelle insrer le bloc. NULL cre une ** nouvelle liste

32

Chapitre IV. Questions de style

** pBloc: pointeur sur le bloc a insrer ** ** retourne: la nouvelle liste **/ Donner le type des paramtres ne sert rien, car la dclaration formelle de la fonction, avec le type exact suit immdiatement. Commentaires dans le code On peut presque sen passer si lalgorithme est bien prsent (voir aussi remarque sur la complexit au chapitre II). Il vaut mieux privilgier les commentaires qui rpondent la question pourquoi ? par rapport ceux qui rpondent la question comment ?. Les commentaires courts peuvent tre placs en n de ligne. Ds quun commentaire est un peu long, il vaut mieux faire un bloc avant le code comment. Pour un bloc, utiliser une prsentation sobre du genre : /* * Un commentaire bloc * Le texte est mis en page de manire simple et claire. */ Les commentaires placs dans le code doivent tre indents comme le code quils prcdent. Nessayez pas de crer des cadres compliqus, justis droite ou avec une apparence 3D. Cela napporte aucune information, et est trs dur maintenir propre lorsquon modie le commentaire.

IV.1.2

Documentation

Maintenir une documentation part sur le fonctionnement interne dun programme est une mission quasiment impossible. Cest une mthode viter. Il vaut mille fois mieux intgrer la documentation au programme sous forme de commentaires. Cette logique peut tre pousse un peu plus loin en utilisant dans les commentaires les A commandes dun logiciel de formattage de documents (tro, L TEX, etc.). Il sut alors davoir un outil qui extrait les commentaires du source et les formatte pour retrouver un document externe avec une prsentation plus riche que des commentaires traditionnels. Knuth a formalis cette approche sous le nom de programmation littraire [14]

IV.2

Typologie des noms

La typologie des noms (choix des noms des variables, des fonctions, des chiers) est un lment primordial dans la lisibilit dun programme. Celle-ci doit respecter plusieurs contraintes : cohrence choisissez une logique dans le choix des noms de variables et gardez-l. signication choisissez des noms qui ont un sens en relation avec le rle de la variable ou de la fonction que vous nommez. modularit indiquez lappartenance dun nom un module.

IV.3. Dclarations

33

non-ambiguit vitez les constructions ambiges pour distinguer deux variables semblables (variations sur la casse par exemple), utilisez des moyens simples (suxes numriques). Attention, certains diteurs de liens imposent que les 6 (six !) premiers caractres dun identicateur soient discriminants. Il existe plusieurs conventions de choix des noms de variables et de fonctions dcrites dans la littrature. Parmi celles, ci on peut citer la notation hongroise prsente entre autres par C. Simonyi [15] qui code le type des objets dans leur nom. Sans entrer dans un mcanisme aussi systmatique, il est bon de suivre quelques rgles : les noms avec un underscore en tte ou en queue sont rservs au systme. Ils ne doivent donc pas tre utiliss par un utilisateur de base. mettre en majuscules les constantes et les noms de macros dnies par #define. Les macros qui se comportent comme une fonction peuvent avoir un nom en minuscules. exemples : #define VITESSE_MAX (1.8) #define MAX(i,j) ((i) > (j) ? (i) : (j)) #define bcopy(src,dst,n) memcpy((dst),(src),(n)) MAX est identi comme une macro (et doit le rester). En eet cette macro value deux fois lun de ses arguments. crire max risquerait de le faire oublier et de conduire des erreurs. mettre en majuscules galement les noms de types dnis par typedef et les noms de structures. utiliser de prfrence le mme nom pour une structure et le type dnit pour elle. exemple : typedef struct POS { double x; double y; double theta; } POS; les constantes dans les enum commencent par une majuscule. les autres noms (variables, fonctions,... ) commencent par une minuscule et sont essentiellement en minuscules. Quand un nom comporte plusieurs mots, on peut utiliser une majuscule pour introduire chaque nouveau mot. viter les noms trop proches typographiquement. Par exemple les caractres l et 1 sont diciles distinguer, il en sera de mme des identicateurs u1 et ul. si une fonction retourne une valeur qui doit tre interprte comme valeur boolenne dans un test, utiliser un nom signicatif du test. Par exemple valeurCorrecte() plutt que testValeur(). la longueur dun nom nest pas une vertu en soi. Un index de tableau na pas besoin dtre plus complexe que i. Les variables locales dune fonction peuvent souvent avoir des noms trs courts. les variables globales et les fonctions doivent au contraire avoir des noms qui comportent le maximum dinformation. Mais attention, des noms trop longs rendent la lecture dicile.

IV.3

Dclarations

Utilisez le C ANSI, et incluez systmatiquement des prototypes des fonctions que vous utilisez. Tous les compilateurs C ANSI ont une option pour produire un avertissement ou une erreur quand une fonction est appele sans que son prototype nait t dclar.

34

Chapitre IV. Questions de style

Bien entendu, dclarez un type toutes vos variables et toutes vos fonctions. La dclaration implicite en entier est une source derreurs. Pour dclarer des types compliqus, utilisez des typedefs. Cela rend le code plus lisible et plus modulaire.

IV.4

Indentation

Lindentation permet de mettre en valeur la structure de lalgorithme. Il est capital de respecter une indentation cohrente avec cette structure. Mais, comme pour la typologie des noms de variables, il ny a pas de rgles uniques. Personnellement, jutilise un systme dindentation bien rsum par lexemple suivant. Il a lavantage dune certaine compacit. if (condition) { /* 1er cas */ x = 2; } else { /* 2nd cas */ x = 3; } Dautres prfrent aligner les accolades ouvrantes et fermantes qui se correspondent sur une mme colonne : if (condition) { /* 1er cas */ x = 2; } else { /* 2nd cas */ x = 3; } Lincrment de base de lindentation doit tre susant pour permettre de distinguer facilement les lments au mme niveau. Quatre caractres semble une bonne valeur. Il existe plusieurs outils qui maintiennent lindentation dun programme automatiquement. Lditeur emacs propose un mode spcique pour le langage C qui indente les lignes tout seul au fur et a mesure de la frappe, selon des rgles programmables. Lutilitaire Unix indent permet de refaire lindentation de tout un chier. Un chier de conguration permet de dcrire son style dindentation favori.

IV.5

Boucles

vitez absolument de transformer votre code en plat de spaghetti. Le langage C permet de nombreuses constructions qui dtournent le cours normal de lexcution du programme : break, continue, goto...

IV.6. Expressions complexes

35

Toutes ces constructions doivent tre vites ds quelles rendent dicile le suivi du droulement dun programme. Par la suite, sil faut prendre un compte un nouveau cas, cela ne pourra se faire quen ajoutant des nuds dans le plat... Mais attention, dans un certain nombre de cas, notamment le traitement des erreurs, lutilisation judicieuse dun break ou dun goto est plus lisible quune imbrication profonde de tests.

IV.6

Expressions complexes

D-com-po-sez les expressions trop complexes en utilisant ventuellement des variables intermdiaires. Cela diminue le risque derreur lors de la saisie et augmente la lisibilit pour la suite. Dans la plupart des cas, il est plus ecace de laisser le compilateur optimiser le code et de privilgier la lisibilit du source. Pour dclarer un type complexe, utilisez plusieurs typedefs intermdiaires. Par exemple, pour dclarer un tableau de dix pointeurs sur fonctions entires avec un paramtre entier, les deux typedefs suivants sont bien plus lisibles que ce que lon obtiendrait en essayant de lcrire directement (laiss en exercice pour le lecteur). typedef int (*INTFUNC)(int); typedef INTFUNC TABFUNC[10];

IV.7

Conversion de types

Attention, terrain glissant. Normalement, il ne devrait pas y en avoir. Avant dutiliser un cast, demandez-vous toujours sil ny a pas un problme dans votre programme qui vous oblige faire ce cast. Les pommes ne sont pas des poires, cest vrai aussi des types informatiques. Si vraiment vous avez des types qui peuvent reprsenter plusieurs objets dirents, les unions sont peut-tre un peu plus lourdes manier, mais elles orent des possibilits de vrication au compilateur. En eet, le plus grand pige tendu par les cast, est que vous obligez le compilateur accepter ce que vous tapez, en lui tant tout droit la critique. Or il est possible de faire des erreurs partout, y compris dans lutilisation des cast. Mais le compilateur na plus aucun moyen de les dtecter.

IV.8

Assertions

Le mcanisme des assertions permet de dclarer des prdicats sur les variables dune fonction qui doivent tre vrais (appels aussi invariants). En cas de situation anormale (en gnral la suite dune erreur de logique du programme) lassertion fausse provoquera un arrt du programme. Les assertions sont introduites par le chier den-tte assert.h et sont crites sous la forme : assert()(expression) Ce mcanisme simple permet daider la mise au point dalgorithmes un peu complexes, la fois parce quils guident le programmeur pendant le codage et quils permettent daider dtecter les erreurs.

Rfrences bibliographiques
[1] S. Summit. C Programming FAQs : Frequently Asked Questions. Addison-Wesley, 1995. [2] D. Goldberg. What every computer scientist should know about oating-point arithmetic. ACM Computing Surveys, 23(1) :548, March 1991. [3] D.E. Knuth. Seminumerical Algorithms, volume 2 of The Art of Computer Programming. Addison-Wesley, 1973. [4] B.W. Kernighan and D.M. Ritchie. The C Programming Language. Prentice-Hall, 1978. [5] B.W. Kernighan and D.M. Ritchie. The C Programming Language. Prentice-Hall, 2nd edition, 1988. [6] D.E. Knuth. Fundamental Algorithms, volume 1 of The Art of Computer Programming. Addison-Wesley, 1973. [7] R. Pike. Notes on Programming in C. [8] B. W. Kernighan and R. Pike. The Practice of Programming. Addison-Wesley Professional Computing Series, 1999. [9] D.E. Knuth. Sorting and Searching, volume 3 of The Art of Computer Programming. Addison-Wesley, 1973. [10] Aleph One (aleph1@underground.org). Smashing the stack for fun and prot. Phrack, N(49), November 1996. [11] S. Garnkel and G. Spaord. Practical Unix and Internet Security. OReilly and Associates, 2nd edition, 1996. [12] M. Bishop. Robust programming. In ECS153, 1998. [13] L.W. Cannon, R.A. Elliot, L.W. Kirchho, J.H. Miller, J.M. Milner, R.W. Mitze, E.P. Shan, and N.O. Whittington. Indian Hill C style and coding standards. Bell Labs. [14] D.E. Knuth. Literate programming. Computer Journal, 28(2) :97111, 1984. [15] C. Simonyi and M. Heller. The hungarian revolution. Byte, pages 131138, Aot 1991.

36

Index
Symboles & . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 && . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 = ...................................... 6 == . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 _exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 || . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 ~ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 _ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 64 bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 A allocation mmoire . . . . . . . . . . . . . . . . . . . . 11 ansi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 arrondi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 assert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 assert.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 B boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 break . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7, 8, 34 bsearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 C calcul rel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 caractres chanes de . . . . . . . . . . . . . . . . . . . . . . . . 13 case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 ceil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11, 13 commentaires . . . . . . . . . . . . . . . . . . . . . . . . . 30 const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 D dclarations . . . . . . . . . . . . . . . . . . . . . . . 10, 33 documentation . . . . . . . . . . . . . . . . . . . . . . . . 32 donnes binaires . . . . . . . . . . . . . . . . . . . . . . . . . . 16 double . . . . . . . . . . . . . . . . . . . . . . . . . . 9, 10, 18 E galit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 emacs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 en-tte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 entres/sorties . . . . . . . . . . . . . . . . . . . . . . . . 15 exec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 F FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 fgets . . . . . . . . . . . . . . . . . . . . . . . . . . . 13, 16, 29 oat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 oor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 fprintf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 fread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 free . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1113, 15 fscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 fuites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 fwrite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 G getchar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 gets . . . . . . . . . . . . . . . . . . . . . . . . . . . 13, 16, 29 goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 H hsearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 I indent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 indentation . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

37

38

INDEX

int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9, 18 K Kernigan et Ritchie . . . . . . . . . . . . . . . . . . . 10 L lex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . long int . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10, LP64 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . lsearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

strtol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28, 29 T tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7, typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33, types conversion de . . . . . . . . . . . . . . . . . . . . . typologie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22 18 18 21

14 34 35 32

M malloc . . . . . . . . . . . . . . 1113, 15, 17, 20, 27 math.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 mkstemp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 mktemp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 N non-initialises variables . . . . . . . . . . . . . . . . . . . . . . . . . 11 O ordre dvaluation . . . . . . . . . . . . . . . . . . . . . 11 P passage par adresse . . . . . . . . . . . . . . . . . . . . 8 pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 prprocesseur . . . . . . . . . . . . . . . . . . . . . . . . . 18 printf . . . . . . . . . . . . . . . . . . . . . . . . . . 1517, 28 Q qsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 S sbrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 scanf . . . . . . . . . . . . . . . . . . . . . . . . 8, 15, 16, 29 short . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 snprintf . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14, 29 sprintf . . . . . . . . . . . . . . . . . . . . . . . . . 1315, 29 sscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 stdlib.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 strcat . . . . . . . . . . . . . . . . . . . . . . . . . . 13, 14, 29 strcpy . . . . . . . . . . . . . . . . . . . . . . . . . . 13, 14, 29 strlcat . . . . . . . . . . . . . . . . . . . . . . . . . 13, 14, 29 strlcpy . . . . . . . . . . . . . . . . . . . . . . . . . 13, 14, 29 strncat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 strncpy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 strtod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9, 29 strtok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

U union . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 unsigned int . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Usenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Y yacc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

Das könnte Ihnen auch gefallen