web-dev-qa-db-fra.com

Pourquoi utiliser asprintf () au lieu de sprintf ()?

J'ai du mal à comprendre pourquoi vous auriez besoin d'asprintf. Ici, dans le manuel, il est dit

Les fonctions asprintf() et vasprintf() sont des analogues de sprintf(3) et vsprintf(3), sauf qu'elles allouent une chaîne suffisamment grande pour contenir la sortie, y compris le terminer l'octet nul et lui renvoyer un pointeur via le premier argument. Ce pointeur doit être passé à free(3) pour libérer le stockage alloué lorsqu'il n'est plus nécessaire.

Voici donc l'exemple que j'essaie de comprendre:

asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));

Quelle est la différence si le tampon alloue une chaîne suffisamment grande par rapport à dire char * = (chaîne)

47
Brandon Ling

Si vous utilisez sprintf() ou vsprintf(), vous devez d'abord allouer un tampon et vous devez vous assurer que le tampon est suffisamment grand pour contenir ce que sprintf écrit. Sinon, sprintf() remplacera volontiers la mémoire située au-delà de la fin du tampon.

char* x = malloc(5 * sizeof(char));
// writes "123456" +null but overruns the buffer
sprintf(x,"%s%s%s", "12", "34", "56");

... écrit le '6' et le null de fin au-delà de la fin de l'espace alloué à x, soit en corrompant une autre variable, soit en provoquant une erreur de segmentation.

Si vous êtes chanceux, il piétinera la mémoire entre les blocs alloués et ne fera aucun mal - cette fois. Cela conduit à des bugs intermittents - le type le plus difficile à diagnostiquer. Il est bon d'utiliser un outil comme ElectricFence qui accélère les dépassements.

Un utilisateur non malveillant qui fournit une entrée trop longue peut provoquer un comportement inattendu du programme. Un utilisateur malveillant pourrait exploiter cela comme un moyen d'obtenir son propre code exécutable dans le système.

Une garde contre cela consiste à utiliser snprintf(), qui tronque la chaîne à la longueur maximale que vous fournissez.

char *x = malloc(5 * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56"); // writes "1234" + null

La valeur de retour size est la longueur que aurait été écrite si l'espace était disponible - n'incluant pas le null final.

Dans ce cas, si size est supérieur ou égal à 5, alors vous savez que la troncature s'est produite - et si vous ne vouliez pas de troncature, vous pouvez allouer une nouvelle chaîne et réessayer snprintf() .

char *x = malloc(BUF_LEN * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56");
if (size >= BUF_LEN) {
    realloc(&x,(size + 1) * sizeof(char));
    snprintf(x, size + 1 , "%s%s%s", "12", "34", "56");
}

(c'est un algorithme assez naïf, mais il illustre le point)

asprintf() le fait en une seule étape pour vous - calcule la longueur de la chaîne, alloue cette quantité de mémoire et y écrit la chaîne.

char *x;
int size = asprintf(&x, "%s%s%s", "12", "34", "56");

Dans tous les cas, une fois que vous avez fini avec x, vous devez le libérer, ou vous perdez de la mémoire:

free(x);

asprintf() est un malloc() implicite, vous devez donc vérifier qu'il fonctionne, comme vous le feriez avec malloc() ou tout autre appel système.

if (size == -1 ) {
   /* deal with error in some way */
}

Notez que asprintf() fait partie des extensions GNU et BSD de libc - vous ne pouvez pas être sûr qu'il sera disponible dans tous les environnements C. sprintf() et snprintf() font partie des normes POSIX et C99.

109
slim

L'avantage est la sécurité.

De nombreux programmes ont permis aux exploits du système de se produire en faisant déborder les tampons fournis par le programmeur lorsqu'ils étaient remplis de données fournies par l'utilisateur.

Avoir asprintf allouer le tampon pour vous garantit que cela ne peut pas arriver.

Cependant, vous devez vérifier la valeur de retour de asprintf pour vous assurer que l'allocation de mémoire a bien réussi. Voir http://blogs.23.nu/ilja/2006/10/antville-12995/

20
Alnitak