FlawOpen

flawopen.com/Sécurité mémoire/strcpy vs strncpy

strncpy() est-elle plus sûre que strcpy() ?

CWE-787 : écriture hors limites ; CWE-170 : terminaison nulle incorrectePage de référence
Réponse courte

Un champ de largeur fixe ressemble à une rangée de cases : la dernière doit contenir un marqueur de fin si le code traite les octets comme une chaîne C. strncpy() écrit au plus n octets et évite un dépassement uniquement si n ne dépasse pas la taille réelle du tampon. Si la source remplit cette limite, aucun caractère nul n’est ajouté. Une fonction de chaîne appelée ensuite peut lire au-delà du tampon en cherchant ce marqueur manquant.

LES DEUX SONT INCORRECTES
char dst[16];

/* 1. Unbounded — classic overflow */
strcpy(dst, src);

/* 2. Bounded, but may not terminate.
   If strlen(src) >= 16, dst has no
   '\0' and every later strlen/printf
   reads out of bounds.            */
strncpy(dst, src, sizeof(dst));

/* 3. Off-by-one: room for 16 chars
   plus a terminator = 17 bytes,
   but dst only holds 16.          */
strncpy(dst, src, sizeof(dst));
dst[sizeof(dst)] = '\0';   /* OOB! */
CORRECT
char dst[16];

/* Preferred: snprintf always
   null-terminates (when size > 0),
   and tells you about truncation. */
int n = snprintf(dst, sizeof(dst),
                 "%s", src);
if (n < 0 || (size_t)n >= sizeof(dst)) {
    /* truncated — handle it */
    return ERR_TOO_LONG;
}

/* Or strlcpy where available —
   always terminates, returns the
   length it tried to create.      */
if (strlcpy(dst, src, sizeof(dst))
        >= sizeof(dst)) {
    return ERR_TOO_LONG;
}

À quoi strncpy était réellement destinée

Ce comportement n’est pas un défaut : strncpy() a été conçue pour des champs de largeur fixe remplis de nuls, pas comme copie généralement sûre de chaînes C. Si la chaîne source est plus courte que n, la fonction complète le tampon avec des octets nuls jusqu’à n - 1. Si aucun octet nul ne figure dans les n premiers octets de la source, elle copie exactement n octets sans terminer le tampon. C’est adapté à certains champs fixes, mais dangereux pour les API qui attendent une chaîne terminée par zéro.

Quand la source est plus courte que n, le remplissage écrit jusqu’à la largeur complète du champ. Pour un grand tampon et une source courte, cela peut coûter plus qu’une copie qui s’arrête au terminateur.

Toujours détecter la troncature

Éviter le dépassement ne suffit pas. Une troncature silencieuse est aussi un risque : un hôte, un chemin ou une valeur d’autorisation tronqué peut changer de sens. snprintf renvoie la longueur qu’il aurait voulu écrire ; comparez-la à la taille du tampon. La mauvaise interprétation de cette valeur comme nombre d’octets écrits a également compté dans Citrix Bleed.

Questions fréquentes

Et strcpy_s et les fonctions de l’Annex K ?

Elles terminent la chaîne et vérifient les limites, mais leur portabilité est inégale : bonne sous Windows, plus variable ailleurs. Pour plusieurs plateformes, snprintf est souvent plus disponible.

strlcpy fait-elle partie de la norme C ?

Non. Elle vient d’OpenBSD et existe sur BSD et macOS ; des versions récentes de glibc l’incluent aussi. Vérifiez sa disponibilité pour vos cibles.

En C++, dois-je utiliser ces fonctions ?

Préférez std::string et std::string_view, qui gèrent la longueur. Réservez les fonctions C aux frontières d’API exigeant un tampon brut.

Références

À voir aussi CWE vs CVE : quelle est la différence ? Mass assignment : risques et listes de champs autorisés Fuite d’état serverless : isoler les données par requête PHP == et les comparaisons de magic hash Rust : le bloc unsafe est-il sûr ?