Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

printf et %n.

15 réponses
Avatar
GurneyH
Bonjour,

Je me trouve en face d'un probl=E8me =E9trange.
Ce code compil=E9 avec MinGW :

#include <stdio.h>

int main(void)
{
int a;
char const *s =3D "Hello world";

printf("%s%n\n", s, &a);
printf("%d\n", a);

return 0;
}

M'affiche bien chez moi 11, qui est bien est le nombre de caract=E8res
d=E9j=E0 =E9crits.

Seulement une autre personne =E9galement sous MinGW compile ce code, et
il semble que le sp=E9cificateur %n est ignor=E9. : si on initialise la
variable a avec une valeur quelconque, on retrouve cette valeur apr=E8s
l'appel de printf.

Une id=E9e?...

Merci d'avance.

10 réponses

1 2
Avatar
-ed-
On 26 fév, 07:16, GurneyH wrote:
    int a;
    char const *s = "Hello world";

    printf("%s%nn", s, &a);



Ne fonctionne pas chez moi (MinGW).

Seulement une autre personne également sous MinGW compile ce code, et
il semble que le spécificateur %n est ignoré. : si  on initialise l a
variable a avec une valeur quelconque, on retrouve cette valeur après
l'appel de printf.



Il me semble que "%n" n'est définit que pour scanf(). Je vérifie ...
Avatar
-ed-
On 26 fév, 10:13, -ed- wrote:
On 26 fév, 07:16, GurneyH wrote:

>     int a;
>     char const *s = "Hello world";

>     printf("%s%nn", s, &a);

Ne fonctionne pas chez moi (MinGW).

> Seulement une autre personne également sous MinGW compile ce code, et
> il semble que le spécificateur %n est ignoré. : si  on initialise la
> variable a avec une valeur quelconque, on retrouve cette valeur après
> l'appel de printf.

Il me semble que "%n" n'est définit que pour scanf(). Je vérifie ...



Effectivement, c'est aussi pour [f]printf()

WG14/N1256 Committee Draft — September 7, 2007 ISO/IEC 9899:TC3
7.19.6.1

n The argument shall be a pointer to signed integer into which is
written the
number of characters written to the output stream so far by this call
to
fprintf. No argument is converted, but one is consumed. If the
conversion
specification includes any flags, a field width, or a precision, the
behavior is
undefined.

Mais curieusement, je confirme que ça ne fonctionne pas avec MinGW
sous Vista SP2.
Avatar
GurneyH
> On 26 fév, 10:13, -ed- wrote:

> On 26 fév, 07:16, GurneyH wrote:

> >     int a;
> >     char const *s = "Hello world";

> >     printf("%s%nn", s, &a);

> Ne fonctionne pas chez moi (MinGW).

> > Seulement une autre personne également sous MinGW compile ce code, et
> > il semble que le spécificateur %n est ignoré. : si  on initiali se la
> > variable a avec une valeur quelconque, on retrouve cette valeur apr ès
> > l'appel de printf.

> Il me semble que "%n" n'est définit que pour scanf(). Je vérifie .. .

Effectivement, c'est aussi pour [f]printf()

WG14/N1256 Committee Draft — September 7, 2007 ISO/IEC 9899:TC3
7.19.6.1

n The argument shall be a pointer to signed integer into which is
written the
number of characters written to the output stream so far by this call
to
fprintf. No argument is converted, but one is consumed. If the
conversion
specification includes any flags, a field width, or a precision, the
behavior is
undefined.

Mais curieusement, je confirme que ça ne fonctionne pas avec MinGW
sous Vista SP2.



J'avais vérifié que ce spécificateur existait bien pour printf.
J'ai testé sous Visual C++ Express 2008, et au premier essai, il lève
une assertion.
Après recherche, ce spécificateur est désactivé avec le compilateur de
Microsoft, pour des raisons de sécurité(bien compréhensible en fait.) ,
mais on peut l'activer avec la commande

/* Enable or disable support of the %n format in printf, _printf_l,
wprintf, _wprintf_l-family functions. _set_printf_count_output(1);

et ça fonctionne sans problèmes ensuite.

Mystère, avec GCC.

Bon, je venais de découvrir %n, mais il ne devrait pas me manquer plus
que ça...

Merci, -ed- de t'être penché sur le problème.
Avatar
Pierre Maurette
GurneyH, le 26/02/2010 a écrit :

[...]

J'avais vérifié que ce spécificateur existait bien pour printf.
J'ai testé sous Visual C++ Express 2008, et au premier essai, il lève
une assertion.
Après recherche, ce spécificateur est désactivé avec le compilateur de
Microsoft, pour des raisons de sécurité(bien compréhensible en fait.),
mais on peut l'activer avec la commande

/* Enable or disable support of the %n format in printf, _printf_l,
wprintf, _wprintf_l-family functions. _set_printf_count_output(1);

et ça fonctionne sans problèmes ensuite.



Même compilo, mêmes résultats dans Windows 7.

Mystère, avec GCC.



Ça fonctionne, toujours dans Windows 7, avec le gcc 3.4.4 de cygwin. Ça
fonctionne également dans Ubuntu Karmic avec gcc 4.4.1.

Bon, je venais de découvrir %n, mais il ne devrait pas me manquer plus
que ça...



Je ne connaissais pas.

--
Pierre Maurette
Avatar
GurneyH
On 26 fév, 12:14, Pierre Maurette wrote:
GurneyH, le 26/02/2010 a écrit :

[...]

> J'avais vérifié que ce spécificateur existait bien pour printf.
> J'ai testé sous Visual C++ Express 2008, et au premier essai, il lè ve
> une assertion.
> Après recherche, ce spécificateur est désactivé avec le compila teur de
> Microsoft, pour des raisons de sécurité(bien compréhensible en fa it.),
> mais on peut l'activer avec la commande

> /* Enable or disable support of the %n format in printf, _printf_l,
> wprintf, _wprintf_l-family functions. _set_printf_count_output(1);

> et ça fonctionne sans problèmes ensuite.

Même compilo, mêmes résultats dans Windows 7.

> Mystère, avec GCC.

Ça fonctionne, toujours dans Windows 7, avec le gcc 3.4.4 de cygwin. Ça
fonctionne également dans Ubuntu Karmic avec gcc 4.4.1.

> Bon, je venais de découvrir %n, mais il ne devrait pas me manquer plu s
> que ça...

Je ne connaissais pas.

--
Pierre Maurette



Le comportement est assez étrange alors! Oo.

Enfin, comme dit, ce spécificateur ne m'a pas manqué jusque là, pas
plus qu'à vous alors...

J'aurais voulu avoir une explication au problème, mais finalement, je
ne pense pas que le jeu en vaille la chandelle...

Merci beaucoup, en tous cas, pour avoir pris un peu de votre temps
pour tester.

Nouveau sur comp.lang.c, je pense avoir d'autres questions plus
intéressantes à l'avenir.

Bonne journée, et encore merci à vous deux.
Avatar
Etienne Rousee
Le 26/02/2010 12:14, Pierre Maurette a écrit ...

Avec Windows XP SP2 et CodeBlocks (mingw32-gcc 3.4.5),
ça fonctionne aussi.
Et je ne connaissais pas non plus %n.

--

Etienne
Avatar
Antoine Leca
GurneyH écrivit :
int a;
char const *s = "Hello world";
printf("%s%nn", s, &a);


Ne fonctionne pas chez moi (MinGW).
Seulement une autre personne également sous MinGW compile ce code, et
il semble que le spécificateur %n est ignoré. : si on initialise la
variable a avec une valeur quelconque, on retrouve cette valeur après
l'appel de printf.






J'ai testé sous Visual C++ Express 2008, et au premier essai, il lève
une assertion.
Après recherche, ce spécificateur est désactivé avec le compilateur de
Microsoft, pour des raisons de sécurité(bien compréhensible en fait.),



Ah ! « Bien compréhensible » ? Bin moi j'ai toujours pas compris la
raison de sécurité qui fait interdire %n, mais qui laisse strcpy() et
tout plein d'autres fonctions...

mais on peut l'activer avec la commande

/* Enable or disable support of the %n format in printf, _printf_l,
wprintf, _wprintf_l-family functions. _set_printf_count_output(1);

et ça fonctionne sans problèmes ensuite.

Mystère, avec GCC.



Aucun mystère. MinGW, c'est GCC et la bibliothèque d'exécution de
Microsoft (Min veut dire Minimal, le minimum de choses à installer en
plus sur la bécane ; la bibliothèque d'exécution est déjà là, profitons-en).

Donc si sur ta bécane, la bibliothèque d'exécution refuse %n dans les
printf, elle le refusera tout autant avec le compilo de Microsoft
qu'avec le compilo GCC.

Le seul truc, c'est une différence de version entre les dites bibliothèques.
Le comportement que tu décris existe depuis ~2003 (VC7, à la suite des
déboires de Microsoft avec les débordements de tampon dans son propre
code), et ce comportement a été incorporé de force dans Vista (conçu
vers la même époque), y compris dans les versions « de compatibilité »,
donc dans celle qu'utilise MinGW (par défaut), qui correspond à VC2/4-6.

Mais les programmeurs ont gueulé (fort), et dans un souci de compromis,
Microsoft a inventé pour VS2008 (VC9) le truc de l'assertion bloquante,
et parallèlement la fonction qui débloque ; sauf que là, soit ils n'ont
pas encore eu le temps de rétro-porter ce bidule dans les MSVCRT de
compatibilité de Seven (très possible vues les dates), soit ils n'ont
pas l'intention de le faire histoire de forcer un peu la main pour que
les programmeurs « migrent » (encore plus probable que l'explication
précédente).


La solution pour toi, malheureusement, cela va être soit de devoir gérer
les différentes versions de MSVCRT (une grosse galère en perspective),
soit utiliser le printf de libmingwex (qui permet de gérer le %lld et
autres %zd de C99, mais qui oblige à distribuer une .dll supplémentaire;
en plus je n'ai pas été vérifié que %n est OK), soit devoir faire une
croix sur %n et se rabattre sur la valeur retournée par printf() et
découper les printf en petits morceaux (de loin la solution la plus
simple, même si c'est la moins jolie.)


De plus, cette enfilade a démontrée que pas mal de bons programmeurs C
ne connaissaient pas %n, ce qui devrait inciter à se méfier de cette
fonction (car cela signifie aussi que les programmeurs qui relisent le
code peuvent facilement se tromper.)


Antoine
Avatar
Manuel Pégourié-Gonnard
Antoine Leca scripsit :

GurneyH écrivit :
Après recherche, ce spécificateur est désactivé avec le compilateur de
Microsoft, pour des raisons de sécurité(bien compréhensible en fait.),



Ah ! « Bien compréhensible » ? Bin moi j'ai toujours pas compris la
raison de sécurité qui fait interdire %n, mais qui laisse strcpy() et
tout plein d'autres fonctions...



Bon, alors les problèmes de scrcpy() et consorts, je vois, par contre,
celui de %n c'est quoi en fait ? Désolé si la réponse est évidente, mais là
tout de suite elle m'échappe (et je sais pas cherche "%n" sous google).

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Antoine Leca
Manuel Pégourié-Gonnard écrivit :
Antoine Leca scripsit :

GurneyH écrivit :
Après recherche, ce spécificateur est désactivé avec le compilateur de
Microsoft, pour des raisons de sécurité(bien compréhensible en fait.),


Ah ! « Bien compréhensible » ? Bin moi j'ai toujours pas compris la
raison de sécurité qui fait interdire %n, mais qui laisse strcpy() et
tout plein d'autres fonctions...



Bon, alors les problèmes de scrcpy() et consorts, je vois, par contre,
celui de %n c'est quoi en fait ?



D'après ISO/CÉI TR 24731-1, le problème ce serait des attaques contre
des printf qui ne contrôleraient pas leur chaîne de format ; cas type :
printf(argv);

Au départ, c'est un truc pas sain (du même acabit que n'importe quel
débordement de tampon). Mais le cas-qui-tue, c'est si l'attaquant
profite de ce vecteur d'attaque et introduit des %n dans la chaîne de
format d'attaque : cela donne la possibilité d'écrire pas mal de choses
(et surtout des valeurs 0) à des adresses passées en paramètre à printf.
En gros, cela devient du même niveau que gets(), impossible à contrer.

Tiré de WG14 N1147 page 12 ; et sûrement issu d'une attaque subie par MS
ou des analyses de « circonstances aggravantes » de certains défauts.


Vu comme cela, cela paraît idiot : il « suffit » de s'imposer de NE PAS
utiliser des printf(chaîne_utilisateur,...), une politique de sécurité
raisonnable (c'est d'ailleurs la position implicite du comité C).
Le vrai souci, c'est la défense en profondeur : comme chacun sait, les
chaînes de format font partie des choses les plus faciles à repérer dans
un binaire C : donc un attaquant peut chercher à viser de telles
chaînes, changer le caractère qui suit un % par un n et le piège est
ouvert : par exemple, cela revient à modifier un code qui fait
printf("%x", &toto);
en
printf("%n", &toto);
soit
toto = 0;


Une circonstance aggravante, en terme de sûreté, c'est que la fonction «
écrire » qui est sous-tendue par %n n'est pas naturellement associée à
printf() (cf. l'enfilade pour s'en rendre compte) : en conséquence de
nombreuses analyses de sûreté risquent de passer à côté du piège
potentiel (et là aussi, vu la virulence des propos, j'ai comme dans
l'idée que cela a dû se passer en pratique au sud de Vancouver ;
cf. aussi les vulnérabilités WMF à répétition, qui sont basées sur le
même défaut fonctionnel de sûreté).


À mon sens, il s'agit quand même de discussions plutôt alambiquées ; et
toute solution qui continue à utiliser C mais cherche à le faire de
manière sûre me paraît mal embouchée par principe.
Mais bon, le code C que je vois personnellement n'a pas, et de TRÈS
loin, le niveau de sécurité de ces discussions-ci, et je n'ai jamais vu
un code C qui soit suffisamment gros pour que l'option de réécrire soit
réellement in-envisageable ; donc mon avis n'a pas vraiment d'utilité.


Antoine
Avatar
GurneyH
On 26 fév, 18:34, Antoine Leca wrote:
Manuel P gouri -Gonnard crivit :

> Antoine Leca scripsit :

>> GurneyH crivit :
>>> Apr s recherche, ce sp cificateur est d sactiv avec le compilateur de
>>> Microsoft, pour des raisons de s curit (bien compr hensible en fait.) ,
>> Ah ! Bien compr hensible ? Bin moi j'ai toujours pas compris la
>> raison de s curit qui fait interdire %n, mais qui laisse strcpy() et
>> tout plein d'autres fonctions...

> Bon, alors les probl mes de scrcpy() et consorts, je vois, par contre,
> celui de %n c'est quoi en fait ?

D'apr s ISO/C I TR 24731-1, le probl me ce serait des attaques contre
des printf qui ne contr leraient pas leur cha ne de format ; cas type :
        printf(argv);

Au d part, c'est un truc pas sain (du m me acabit que n'importe quel
d bordement de tampon). Mais le cas-qui-tue, c'est si l'attaquant
profite de ce vecteur d'attaque et introduit des %n dans la cha ne de
format d'attaque : cela donne la possibilit d' crire pas mal de choses
(et surtout des valeurs 0) des adresses pass es en param tre printf.
En gros, cela devient du m me niveau que gets(), impossible contrer.

Tir de WG14 N1147 page 12 ; et s rement issu d'une attaque subie par MS
ou des analyses de circonstances aggravantes de certains d fauts.

Vu comme cela, cela para t idiot : il suffit de s'imposer de NE PAS
utiliser des printf(cha ne_utilisateur,...), une politique de s curit
raisonnable (c'est d'ailleurs la position implicite du comit C).
Le vrai souci, c'est la d fense en profondeur : comme chacun sait, les
cha nes de format font partie des choses les plus faciles rep rer dans
un binaire C : donc un attaquant peut chercher viser de telles
cha nes, changer le caract re qui suit un % par un n et le pi ge est
ouvert : par exemple, cela revient modifier un code qui fait
        printf("%x", &toto);
en
        printf("%n", &toto);
soit
        toto = 0;

Une circonstance aggravante, en terme de s ret , c'est que la fonction
crire qui est sous-tendue par %n n'est pas naturellement associ e
printf() (cf. l'enfilade pour s'en rendre compte) : en cons quence de
nombreuses analyses de s ret risquent de passer c t du pi ge
potentiel (et l aussi, vu la virulence des propos, j'ai comme dans
l'id e que cela a d se passer en pratique au sud de Vancouver ;
cf. aussi les vuln rabilit s WMF r p tition, qui sont bas es sur le
m me d faut fonctionnel de s ret ).

mon sens, il s'agit quand m me de discussions plut t alambiqu es ; et
toute solution qui continue utiliser C mais cherche le faire de
mani re s re me para t mal embouch e par principe.
Mais bon, le code C que je vois personnellement n'a pas, et de TR S
loin, le niveau de s curit de ces discussions-ci, et je n'ai jamais vu
un code C qui soit suffisamment gros pour que l'option de r crire soit
r ellement in-envisageable ; donc mon avis n'a pas vraiment d'utilit .

Antoine



Merci pour ces explications, c'est exactement ce que je cherchais.
Je n'avais pas fait le lien entre MinGW et les bibliothèques
d'exécution de Microsoft. C'est plus clair maintenant.

GurneyH
1 2