J'ai du mal à comprendre pourquoi vous auriez besoin d'asprintf. Ici, dans le manuel, il est dit
Les fonctions
asprintf()
etvasprintf()
sont des analogues desprintf(3)
etvsprintf(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)
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.
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/