Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
2802 connectés 

  FORUM HardWare.fr
  Programmation
  C

  Serveur HTTP et Stack smashing detected

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Serveur HTTP et Stack smashing detected

n°1623467
Sebxoii
I've made a huge tiny mistake.
Posté le 14-10-2007 à 16:40:29  profilanswer
 

Salut à tous,
 
Je dois réaliser un petit serveur HTTP en TP cette année, et je me retrouve complètement bloqué face à une erreur que je n'avais jamais rencontrée auparavant.
 
D'après gdb, le problème se situe à la sortie de manageRequest ( ligne 265 ), mais je n'ai pas réussi à lui soutirer plus d'informations.
 
De plus, le problème ne survient que lors de l'appel à la fonction execCGI, même si le programme ne plante pas directement dans cette fonction. Dans le cas d'une demande d'un fichier html ou d'une image, tout se passe normalement et le thread se termine sans soucis.
 
Si cela peut aider certains d'entre vous, voici le code source complet ainsi que les fichiers qui me servent à tester le serveur : http://sebxoiii.free.fr/Programmation/httpServer.zip
 
Merci d'avance.
 

Code :
  1. /*--------------------------------------------------------------------------*/
  2. #include "netUtils.h"
  3. /*--------------------------------------------------------------------------*/
  4. #define BUFFER_SIZE 0x1000
  5. typedef struct {
  6. int extFd;
  7. SSL * ssl;
  8. int intFd;
  9. struct
  10. {
  11.  char buffer[BUFFER_SIZE];
  12.  char * ptr;
  13.  unsigned int remaining;
  14. } incoming,outgoing;
  15. char requestMethod[0x100];
  16. char requestUri[0x100];
  17. char contentLength[0x100];
  18. char fileName[0x100];
  19. } Request;
  20. Request *createRequest(int fd,SSL_CTX * ctx)
  21. {
  22. Request * req=(Request *)malloc(sizeof(Request));
  23. req->extFd=fd;
  24. req->ssl=(SSL *)0;
  25. req->intFd=-1;
  26. req->incoming.buffer[0]='\0';
  27. req->incoming.ptr=(char *)0;
  28. req->incoming.remaining=0;
  29. req->outgoing.buffer[0]='\0';
  30. req->outgoing.ptr=(char *)0;
  31. req->outgoing.remaining=0;
  32. req->requestMethod[0]='\0';
  33. req->requestUri[0]='\0';
  34. req->contentLength[0]='\0';
  35. req->fileName[0]='\0';
  36. if(ctx)
  37. {
  38.  /* ... A COMPLETER ... : initialiser req->ssl */
  39. }
  40. return req;
  41. }
  42. void destroyRequest(Request * req)
  43. {
  44. int r;
  45. if(req->ssl)
  46. {
  47. /* ... A COMPLETER ... : detruire req->ssl */
  48. }
  49. RESTART_SYSCALL(r,close(req->extFd));
  50. if(req->intFd!=-1)
  51. {
  52.  RESTART_SYSCALL(r,close(req->intFd));
  53. }
  54. free(req);
  55. }
  56. int requestRead(Request * req,void * buffer,unsigned int size)
  57. {
  58. return req->ssl ? errno=EBADF,-1 /* ... A COMPLETER ... : lire req->ssl */
  59.                 : read(req->extFd,buffer,size);
  60. }
  61. int requestWrite(Request * req,const void * buffer,unsigned int size)
  62. {
  63. return req->ssl ? errno=EBADF,-1 /* ... A COMPLETER ... : ecrire req->ssl */
  64.                 : write(req->extFd,buffer,size);
  65. }
  66. MAKE_READLINE_FUNCTION(requestReadLine,requestRead,Request *)
  67. MAKE_WRITEFULLY_FUNCTION(requestWriteFully,requestWrite,Request *)
  68. /*--------------------------------------------------------------------------*/
  69. void contentType(Request *req, char *type)
  70. {
  71. char *buffer=req->fileName;
  72. unsigned int i=0;
  73. /* Détection du point qui précède l'extension. */
  74. do
  75. {
  76.  i++;
  77. }
  78. while(buffer[i]!='.' && i!=strlen(buffer));
  79. /* Une fois le point détecté, modification de l'adresse du début du buffer avant qu'elle coincide avec celle du début de l'extension. */
  80. buffer=&buffer[i+1];
  81. /* Il ne reste ensuite plus qu'à comparer le chaine "buffer" qui ne contient désormais plus que l'extension avec les extensions courantes.*/
  82. if(strcmp(buffer,"jpg" )==0)   {strcpy(type,"image/jpeg" );}
  83. else if(strcmp(buffer,"gif" )==0) {strcpy(type,"image/gif" );}
  84. else if(strcmp(buffer,"c" )==0)  {strcpy(type,"text/plain" );}
  85. else if(strcmp(buffer,"txt" )==0) {strcpy(type,"text/plain" );}
  86. else if(strcmp(buffer,"html" )==0) {strcpy(type,"text/html" );}
  87. else if(strcmp(buffer,"bin" )==0) {strcpy(type,"application/octet-stream" );}
  88. /*else if(strcmp(buffer,"cgi" )==0) {strcpy(type,"cgi" );}*/ /*Si CGI ou autre exécutable le test est réalisé au niveau de access. */
  89. else        {strcpy(type,"application/octet-stream" );}
  90. }
  91. void suppressionParametresCGI(Request *req,char *parametresCGI)
  92. {
  93. unsigned int i=0;
  94. /* Detection du premier point d'interrogation. */
  95. do
  96. {
  97.  i++;
  98. }
  99. while(req->fileName[i]!='?' && i!=strlen(req->fileName));
  100. /* Sauvegarde des paramètres CGI. */
  101. strcpy(parametresCGI,&(req->fileName[i]));
  102. /* Suppression des parametres CGI du champ fileName. */
  103. req->fileName[i]='\0';
  104. }
  105. void execCGI(Request *req)
  106. {
  107. int fdCGI[2],r=0;
  108. pid_t result;
  109. r=pipe(fdCGI); /* Création d'un tube de communication : 2 file descriptors (un en écriture, un en lecture). */
  110. if(r==-1){ perror("pipe" ); exit(EXIT_FAILURE); }
  111. result=fork(); /* Création d'un nouveau processus. */
  112. switch(result)
  113. {
  114.  case -1: perror("fork" ); exit(EXIT_FAILURE);
  115.  /* Processus enfant :*/
  116.  case 0:  close(fdCGI[0]); /* Fermeture de l'un des file descriptors. */
  117.     dup2(fdCGI[1],STDOUT_FILENO); /* Inversion de l'entrée standard et du descripteur de fichier créé en écriture. */
  118.     execlp(req->fileName,req->fileName,NULL); /* Remplacement du processus courant par un processus lançant le script cgi. */
  119.  /* Processus parent :*/
  120.  default: close(fdCGI[1]); /* Fermeture de l'un des file descriptors. */
  121.     do /* Boucle utilisée pour lire le code HTML créé par le script CGI et envoyé par le tube. */
  122.     {
  123.      req->outgoing.remaining=readLine(fdCGI[0],req->outgoing.buffer,BUFFER_SIZE);
  124.      req->outgoing.remaining=requestWriteFully(req,req->outgoing.buffer,req->outgoing.remaining);
  125.     } while (req->outgoing.remaining != 0);
  126.     close(fdCGI[0]); /* Fermeture du file descriptor restant. */
  127. }
  128. }
  129. void envoiBuffer(Request *req)
  130. {
  131. req->outgoing.remaining=requestWriteFully(req,req->outgoing.buffer,strlen(req->outgoing.buffer));
  132. }
  133. void badRequest(Request *req)
  134. {
  135. strcpy(req->outgoing.buffer,"HTTP/1.1 400 Bad Request\n" );
  136. req->outgoing.remaining=strlen(req->outgoing.buffer);
  137. envoiBuffer(req);
  138. }
  139. void okRequest(Request *req)
  140. {
  141. strcpy(req->outgoing.buffer,"HTTP/1.1 200 OK\n" );
  142. req->outgoing.remaining=strlen(req->outgoing.buffer);
  143. envoiBuffer(req);
  144. }
  145. void notFoundRequest(Request *req)
  146. {
  147. strcpy(req->outgoing.buffer,"404 Not Found\n" );
  148. req->outgoing.remaining=strlen(req->outgoing.buffer);
  149. envoiBuffer(req);
  150. }
  151. void *manageRequest(void * data)
  152. {
  153. int r=0,ficD=0;
  154. char parametresCGI[0x20];
  155. Request * req=(Request *)data;
  156. pthread_detach(pthread_self());
  157. /* Récupération de la première ligne. */
  158. req->incoming.remaining=requestReadLine(req,req->incoming.buffer,BUFFER_SIZE);
  159. sscanf(req->incoming.buffer,"%s %s",req->requestMethod,req->requestUri);
  160. printf("Requete : %s %s\n",req->requestMethod,req->requestUri);
  161. /* Récupération du reste de l'entête de la requête HTTP. */
  162. do
  163. {
  164.  req->incoming.remaining=requestReadLine(req,req->incoming.buffer,BUFFER_SIZE);
  165.  printf("%s",req->incoming.buffer);
  166. } while(strcmp(req->incoming.buffer,"\n" )!=0 && strcmp(req->incoming.buffer,"\r\n" )!=0);
  167. /* Traitement de la requête. */
  168. if(strcmp(req->requestMethod,"GET" )!=0 && strcmp(req->requestMethod,"POST" )!=0) /* Si ce n'est pas une requête GET. */
  169. {
  170.  badRequest(req);
  171. }
  172. else if(strcmp(req->requestMethod,"GET" )==0)/* Si c'est une requête GET. */
  173. {
  174.  okRequest(req);
  175.  /* Fabrication du champ fileName. */
  176.  strcpy(req->fileName,"." );
  177.  strcat(req->fileName,req->requestUri);
  178.  /* Suppression des des éventuels paramètres CGI. */
  179.  suppressionParametresCGI(req,parametresCGI);
  180.  /* Récupération des informations. */
  181.  {
  182.   struct stat infos;
  183.   RESTART_SYSCALL(r,stat(req->fileName,&infos));
  184.   if(r==-1) { perror("stat" ); return (void *)0; }
  185.   /* S'il s'agit d'un dossier on ajoute index.html*/
  186.   if(S_ISDIR(infos.st_mode))
  187.   {
  188.    strcat(req->fileName,"/index.html" );
  189.   }
  190.   /* Sinon, on n'ajoute rien et on ouvre le fichier. */
  191.   RESTART_SYSCALL(ficD,open(req->fileName,O_RDONLY));
  192.   if(ficD==-1) /* Si l'ouverture pose problème. */
  193.   {
  194.    notFoundRequest(req);
  195.    return (void *)0;
  196.   }
  197.   /* Envoi de l'entête. */
  198.   {
  199.    char type[0x10];
  200.    contentType(req,type);
  201.    if(access(req->fileName,X_OK!=-1)) /* S'il ne s'agit pas d'un fichier exécutable : */
  202.    {
  203.     /* Envoi de la taille de l'entête. */
  204.     sprintf(req->outgoing.buffer,"Content-Length : %lu\n",infos.st_size);
  205.     envoiBuffer(req);
  206.     /* Envoi du type de l'entête */
  207.     sprintf(req->outgoing.buffer,"Content-Type : %s\n\n",type);
  208.     envoiBuffer(req);
  209.     /* Si l'ouverture fonctionne sans problème. */
  210.     do
  211.     {
  212.      req->outgoing.remaining=readFully(ficD,req->outgoing.buffer,BUFFER_SIZE);
  213.      req->outgoing.remaining=requestWriteFully(req,req->outgoing.buffer,req->outgoing.remaining); /* Impossibilité d'utiliser envoiBuffer car la taille du buffer est ici déterminée par le nombre d'octets lu par readFully. */
  214.     } while (req->outgoing.remaining != 0);
  215.     close(ficD); /* Fermeture du descripteur de fichier. */
  216.    }
  217.    else /* S'il s'agit d'un exécutable : */
  218.    {
  219.     execCGI(req);
  220.    }
  221.   }
  222.  }
  223. }
  224. else if(strcmp(req->requestMethod,"POST" )==0)/* Si c'est une requête POST. */
  225. {
  226.  printf("Filename : %s, Uri : %s\n",req->fileName,req->requestUri);
  227. }
  228. destroyRequest(req);
  229. return (void *)0;
  230. }
  231. /*--------------------------------------------------------------------------*/
  232. int main(void)
  233. {
  234. int httpSock=-1,r;
  235. struct sigaction act;
  236. pthread_t th;
  237. /* Ecoute sur le port 8080. */
  238. httpSock=listenTcp(8080);
  239. if(httpSock==-1)
  240. {
  241.  perror("listen http" );
  242.  exit(EXIT_FAILURE);
  243. }
  244. /* Ignorer une écriture impossible dans un pipe */
  245. memset(&act,0,sizeof(struct sigaction));
  246. act.sa_handler=SIG_IGN;
  247. RESTART_SYSCALL(r,sigaction(SIGPIPE,&act,(struct sigaction *)0));
  248. for(;;)
  249. {
  250.  unsigned long ipAddress;
  251.  int portNumber;
  252.  int fd=acceptTcp(httpSock,&ipAddress,&portNumber);
  253.  if(fd==-1)
  254.  {
  255.   perror("acceptTcp" );
  256.  }
  257.  else
  258.  {
  259.   Request * req=createRequest(fd,(SSL_CTX *)0);
  260.   r=pthread_create(&th,(pthread_attr_t *)0,&manageRequest,req);
  261.   if(r)
  262.   {
  263.    fprintf(stderr,"pthread_create: %s\n",strerror(errno));
  264.    destroyRequest(req);
  265.   }
  266.  }
  267. }
  268. return EXIT_SUCCESS; /* never reached */
  269. }
  270. /*--------------------------------------------------------------------------*/

mood
Publicité
Posté le 14-10-2007 à 16:40:29  profilanswer
 

n°1623580
tpierron
Posté le 15-10-2007 à 03:06:21  profilanswer
 

Oué, il est hyper casse gueule ton code, je remettrais tout à plat, si j'étais toi, avec une gestion un peu maniaque des chaines de caractères. Le C n'est pas d'une très grande aide dans ce domaine, mais il est possible de faire des trucs potables avec un peu plus de rigueur.
 
Il y a des buffers overflow à gogo dans ton code, mais le plus probable se trouve sans doute à cette ligne :
 
char type[0x10];
contentType(req,type);
 
Un content-type possible est "application/octet-stream", ce qui fait largement plus que 16 octets. D'où écrasement de la valeur de retour et plantage quasi certain lorsque le thread quitte.

n°1623585
Sebxoii
I've made a huge tiny mistake.
Posté le 15-10-2007 à 07:55:52  profilanswer
 

Casse gueule mon code ? http://membres.lycos.fr/sebxoiii/Smileys/transpi.gif
 
C'est clair qu'il est pas formidable, mais je t'avouerais qu'on n'a jamais vraiment eu de cours de mise en forme de code. :/
 
Effectivement le type[0x10] c'est une grosse erreur, on l'avait mis de cette taille sans savoir la taille d'une description, et oublié de le modifier après.
 
Merci beaucoup pour ton aide, je verrais ce soir si ça passe. :)

n°1623589
Emmanuel D​elahaye
C is a sharp tool
Posté le 15-10-2007 à 08:31:50  profilanswer
 

Sebxoii a écrit :

Casse gueule mon code ?
 
C'est clair qu'il est pas formidable, mais je t'avouerais qu'on n'a jamais vraiment eu de cours de mise en forme de code. :/


On s'en fout de la présentation (quoique...). Ce qu'on essaye de te faire comprendre, c'est qu'il est fragile du fait qu'il n'y a aucun contrôle sur la taille des chaines etc.
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1623640
Taz
bisounours-codeur
Posté le 15-10-2007 à 10:16:01  profilanswer
 

Sebxoii a écrit :

Casse gueule mon code ? http://membres.lycos.fr/sebxoiii/Smileys/transpi.gif
 
C'est clair qu'il est pas formidable, mais je t'avouerais qu'on n'a jamais vraiment eu de cours de mise en forme de code. :/
 
Effectivement le type[0x10] c'est une grosse erreur, on l'avait mis de cette taille sans savoir la taille d'une description, et oublié de le modifier après.
 
Merci beaucoup pour ton aide, je verrais ce soir si ça passe. :)


Ne jamais utiliser strcpy ?
Faire attention avec l'utilisation de strncpy, utiliser une variante de strlcpy si on a.

n°1624036
Sebxoii
I've made a huge tiny mistake.
Posté le 15-10-2007 à 16:03:40  profilanswer
 

Emmanuel Delahaye a écrit :


On s'en fout de la présentation (quoique...). Ce qu'on essaye de te faire comprendre, c'est qu'il est fragile du fait qu'il n'y a aucun contrôle sur la taille des chaines etc.


Ah je pensais que la critique portait sur la répartition des fonctions/sous-fonctions que je fais un peu à l'occasion. Enfin merci pour les conseils. :)

Taz a écrit :


Ne jamais utiliser strcpy ?
Faire attention avec l'utilisation de strncpy, utiliser une variante de strlcpy si on a.


Je vais modifier ça à l'occasion.

 

edit : Merci tpierron, ça fonctionne nickel en modifiant la taille du buffer type. :)

 

Maintenant je vais essayer de rajouter quelques vérifications dès que j'écris dans un buffer. Je vais faire avec strncpy n'ayant pas strlcpy.

 

edit2 : Hmm en fait strncpy a l'air plutôt relou à utiliser, je viens de trouver la source de strlcpy, ça m'a l'air pas mal du tout. :)


Message édité par Sebxoii le 15-10-2007 à 16:21:07

Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C

  Serveur HTTP et Stack smashing detected

 

Sujets relatifs
[Symfony] ini_set() désactivé sur serveur webheberger une image sur un serveur diférent. risqué?
Serveur d'application .NETProbleme de transfert de fichier via winsock appli Serveur/Clients
suivi de version de programmes access reliés à une base serveurVérifier qu'une photo soit bien présente sur le serveur
Page d'accueil avec nom de serveurprobléme de base de données une fois pausé sur serveur
[ ASP ] Fonction include d'une page HTTP situé sur un autre serveur 
Plus de sujets relatifs à : Serveur HTTP et Stack smashing detected


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR