web-dev-qa-db-fra.com

Passer trop d'arguments à printf

Tout programmeur C qui travaille depuis plus d'une semaine a rencontré des plantages résultant de l'appel de printf avec plus de spécificateurs de format que d'arguments réels, par exemple:

printf("Gonna %s and %s, %s!", "crash", "burn");

Cependant, y a-t-il des mauvaises choses similaires qui peuvent se produire lorsque vous passez trop d'arguments à printf?

printf("Gonna %s and %s!", "crash", "burn", "dude");

Ma connaissance de l'assemblage x86/x64 me porte à croire que cela est inoffensif, bien que je ne sois pas convaincu qu'il n'y ait pas de condition Edge qui me manque, et je n'ai aucune idée des autres architectures. Cette condition est-elle garantie inoffensive, ou y a-t-il un écueil potentiellement accidentel ici aussi?

33
JSBձոգչ

Vous connaissez probablement le prototype de la fonction printf comme quelque chose comme ça

int printf(const char *format, ...);

Une version plus complète serait en fait

int __cdecl printf(const char *format, ...);

Le __cdecl définit la "convention d'appel" qui, avec d'autres choses, décrit comment les arguments sont traités. Dans ce cas, cela signifie que les arguments sont poussés sur la pile et que la pile est nettoyée par la fonction effectuant l'appel.

Une alternative à _cdecl est __stdcall, il y en a d'autres. Avec __stdcall la convention est que les arguments sont poussés sur la pile et nettoyés par la fonction qui est appelée. Cependant, pour autant que je sache, il n'est pas possible pour un __stdcall fonction pour accepter un nombre variable d'arguments. Cela a du sens car il ne saurait pas combien de pile à nettoyer.

Le long et le court, c'est que dans le cas de __cdecl fonctionne de manière sécurisée pour passer autant d'arguments que vous le souhaitez, car le nettoyage est effectué dans le code faisant l'appel. Si vous deviez en quelque sorte passer trop d'arguments à un __stdcall fonction cela entraîne une corruption de la pile. Un exemple où cela pourrait se produire est si vous aviez le mauvais prototype.

Plus d'informations sur les conventions d'appel peuvent être trouvées sur Wikipedia ici .

15
torak

Projet de norme C en ligne (n1256) , section 7.19.6.1, paragraphe 2:

La fonction fprintf écrit la sortie dans le flux pointé par stream, sous le contrôle de la chaîne pointée par format qui spécifie comment les arguments suivants sont convertis pour la sortie. S'il n'y a pas suffisamment d'arguments pour le format, le comportement n'est pas défini. Si le format est épuisé alors que les arguments restent, les arguments en excès sont évalués (comme toujours) mais sont sinon ignorés. La fonction fprintf renvoie lorsque la fin de la chaîne de format est rencontrée.

Le comportement de toutes les autres fonctions *printf() est le même que les arguments excédentaires sauf pour vprintf() (évidemment).

33
John Bode

Tous les arguments seront poussés sur la pile et supprimés si le cadre de pile est supprimé. ce comportement est indépendant d'un processeur spécifique. (Je me souviens seulement d'un mainframe qui n'avait pas de pile, conçu dans les années 70) Donc, oui, le deuxième exemple n'échouera pas.

3
stacker

printf est conçu pour accepter n'importe quel nombre d'arguments. printf lit ensuite le spécificateur de format (premier argument) et extrait les arguments de la liste des arguments selon les besoins. C'est pourquoi trop peu d'arguments se bloquent: le code commence simplement à utiliser des arguments inexistants, à accéder à de la mémoire qui n'existe pas ou à quelque autre mauvaise chose. Mais avec trop d'arguments, les arguments supplémentaires seront simplement ignorés. Le spécificateur de format utilisera moins d'arguments que ce qui a été transmis.

3
Ned Batchelder