FlawOpen

flawopen.com/Segurança de memória/strcpy vs strncpy

strncpy() é mais segura que strcpy()?

CWE-787: gravação fora dos limites; CWE-170: terminação nula incorretaPágina de referência
Resposta curta

Um campo de largura fixa é como uma fileira de espaços rotulados: o último precisa guardar um marcador de fim se o código tratar os bytes como uma string C. strncpy() grava no máximo n bytes, evitando ultrapassar o destino apenas quando n não excede o tamanho real do buffer. Se a origem preencher esse limite, não haverá terminador nulo. Uma função de string posterior poderá ler além do destino à procura do marcador.

AMBAS ESTÃO ERRADAS
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! */
CORRETO
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;
}

Para que strncpy foi projetada

Esse comportamento não é defeito: strncpy() foi projetada para sequências de caracteres preenchidas com nulos e de largura fixa, não como cópia geralmente segura de strings C. Se a string de origem for menor que n, a função preenche o destino com bytes nulos até n - 1. Se não houver byte nulo nos primeiros n bytes da origem, copia exatamente n bytes e não termina o destino. Isso pode servir para campos de largura fixa; é perigoso quando o resultado vai para APIs que esperam uma string terminada em nulo.

Quando a origem é menor que n, o preenchimento obrigatório grava até o fim do campo. Em destinos grandes com origens curtas, isso pode fazer mais trabalho do que uma cópia que para no terminador.

Sempre detecte truncamento

Evitar o estouro é apenas metade do trabalho. O truncamento silencioso também cria risco: um host, caminho ou valor de autorização truncado pode mudar de significado. snprintf retorna o tamanho que tentaria escrever; compare esse valor com o tamanho do buffer. Essa semântica de retorno, quando interpretada incorretamente como bytes gravados, também foi relevante no caso Citrix Bleed.

Perguntas frequentes

E strcpy_s e as funções Annex K?

Elas terminam a string e verificam limites, mas a portabilidade varia: têm bom suporte no Windows e implementação irregular em outros ambientes. Para vários sistemas, snprintf costuma ser a opção mais disponível.

strlcpy faz parte do padrão C?

Não. Ela surgiu no OpenBSD e está disponível em BSD e macOS; versões recentes do glibc também a incluem. Verifique os sistemas-alvo em vez de presumir disponibilidade.

Em C++, devo usar alguma dessas funções?

Prefira std::string e std::string_view, que gerenciam o tamanho. Use funções C apenas em limites de API que exigem um buffer bruto.

Referências

Veja também CWE vs. CVE: qual é a diferença? Mass assignment: riscos e listas seguras de campos Vazamento de estado serverless: isole dados por requisição PHP == e comparações de magic hash O bloco unsafe do Rust ainda é seguro?