Quais funções da biblioteca padrão devem (devem) ser evitadas?

90

Eu li no Stack Overflow que algumas funções C são "obsoletas" ou "devem ser evitadas". Você pode me dar alguns exemplos desse tipo de função e o motivo?

Que alternativas existem para essas funções?

Podemos usá-los com segurança - alguma boa prática?

Andrei Ciobanu
fonte
3
Eu acredito que é uma coisa muito boa saber. Algumas funções de C realmente devem ser evitadas e usadas apenas para fins educacionais.
INS
@Felix, seria mais fácil editar uma única resposta (marcada como correta) no futuro. Realmente está no ar as respostas que provavelmente mudarão com o tempo. Quem sabe, talvez Jeff forneça um distintivo de 'zelador' para pessoas que mantiverem as respostas atualizadas nos próximos anos.
Tim Post
1
Postagem @Tim: Ok, vou deletar meus comentários.
Felix Kling,
3
Eu não usaria strncpy()como um substituto geral para strcpy(), e strncat()nunca usaria porque tem a interface menos intuitiva que se possa imaginar - VOCÊ sabe o que especifica o parâmetro de comprimento?
Jonathan Leffler
2
Usar strncpy e strncat é quase sempre um erro. Eles certamente não devem ser usados ​​no lugar de strcpy e strcat !! scanf e sprintf também são perfeitamente utilizáveis ​​se você souber como usá-los ...
R .. GitHub PARE DE AJUDAR ICE

Respostas:

58

Funções
obsoletas Inseguras
Um exemplo perfeito de tal função é gets () , porque não há como dizer o quão grande é o buffer de destino. Consequentemente, qualquer programa que leia a entrada usando gets () tem uma vulnerabilidade de estouro de buffer . Por razões semelhantes, deve-se usar strncpy () no lugar de strcpy () e strncat () no lugar de strcat () .

Ainda, mais alguns exemplos incluem a função tmpfile () e mktemp () devido a possíveis problemas de segurança com a substituição de arquivos temporários e que são substituídos pela função mkstemp () mais segura .

Não-reentrante
Outros exemplos incluem gethostbyaddr () e gethostbyname () que são não-reentrantes (e, portanto, não têm garantia de threadsafe) e foram substituídos pelo getaddrinfo () e freeaddrinfo () reentrantes .

Você pode estar notando um padrão aqui ... ou a falta de segurança (possivelmente por não incluir informações suficientes na assinatura para possivelmente implementá-la com segurança) ou a não reentrada são fontes comuns de reprovação.

Desatualizado, Não-portátil
Algumas outras funções simplesmente se tornaram obsoletas porque duplicam a funcionalidade e não são tão portáveis ​​quanto outras variantes. Por exemplo, bzero () está obsoleto em favor de memset () .

Thread Safety e Reentrance
Você perguntou, em sua postagem, sobre thread safety e reentrance. Existe uma pequena diferença. Uma função é reentrante se não usar nenhum estado mutável compartilhado. Portanto, por exemplo, se todas as informações necessárias forem passadas para a função e quaisquer buffers necessários também forem passados ​​para a função (em vez de serem compartilhados por todas as chamadas para a função), então ela é reentrante. Isso significa que threads diferentes, usando parâmetros independentes, não correm o risco de compartilhar o estado acidentalmente. A reentrada é uma garantia mais forte do que a segurança do thread. Uma função é segura para thread se puder ser usada por vários threads simultaneamente. Uma função é thread-safe se:

  • É reentrante (ou seja, não compartilha nenhum estado entre as chamadas), ou:
  • Não é reentrante, mas usa sincronização / bloqueio conforme necessário para o estado compartilhado.

Em geral, na Single UNIX Specification e IEEE 1003.1 (ou seja, "POSIX"), qualquer função que não seja garantida como reentrante não tem garantia de thread safe. Portanto, em outras palavras, apenas as funções que são garantidas como reentrantes podem ser usadas portavelmente em aplicativos multithread (sem bloqueio externo). Isso não significa, no entanto, que as implementações desses padrões não possam optar por tornar uma função não reentrante threadsafe. Por exemplo, o Linux frequentemente adiciona sincronização a funções não reentrantes para adicionar uma garantia (além daquela da Especificação Única do UNIX) de threadsafety.

Strings (e buffers de memória, em geral)
Você também perguntou se há alguma falha fundamental com strings / matrizes. Alguns podem argumentar que este é o caso, mas eu diria que não, não há nenhuma falha fundamental na linguagem. C e C ++ exigem que você passe o comprimento / capacidade de um array separadamente (não é uma propriedade ".length" como em algumas outras linguagens). Isso não é uma falha per se. Qualquer desenvolvedor C e C ++ pode escrever código correto simplesmente passando o comprimento como um parâmetro quando necessário. O problema é que várias APIs que exigiam essas informações não conseguiram especificá-las como um parâmetro. Ou presumiu que alguma constante MAX_BUFFER_SIZE seria usada. Essas APIs foram reprovadas e substituídas por APIs alternativas que permitem que os tamanhos de array / buffer / string sejam especificados.

Scanf (em resposta à sua última pergunta)
Pessoalmente, eu uso a biblioteca C ++ iostreams (std :: cin, std :: cout, os operadores << e >>, std :: getline, std :: istringstream, std :: ostringstream , etc.), então normalmente não lido com isso. Se eu fosse forçado a usar C puro, no entanto, pessoalmente apenas usaria fgetc () ou getchar () em combinação com strtol () , strtoul () , etc. e analisaria as coisas manualmente, já que não sou um grande fã de varargs ou strings de formato. Dito isso, até onde sei, não há problema com [f] scanf () , [f] printf (), etc., desde que você mesmo crie as strings de formato, nunca passe strings de formato arbitrárias ou permita que a entrada do usuário seja usada como strings de formato e use as macros de formatação definidas em <inttypes.h> quando apropriado. (Observe, snprintf () deve ser usado no lugar de sprintf () , mas isso tem a ver com a falha em especificar o tamanho do buffer de destino e não com o uso de strings de formato). Devo também apontar que, em C ++, boost :: format fornece formatação semelhante a printf sem varargs.

Michael Aaron Safyan
fonte
4
"Obsoleto" é uma palavra forte, que tem um significado específico quando se discute o padrão C ++. Nesse sentido, gets (), strcpy () etc. não estão obsoletos.
4
Contanto que você diferencie entre "reprovado pelo padrão C", "reprovado por Michael Aaron Safyan" e "reprovado por pessoa ou pessoas desconhecidas que esperançosamente sabem do que estão falando [carece de fontes]". A questão é sobre o estilo de codificação preferido, não sobre o padrão C, então os dois segundos são apropriados. Mas, como Neil, tive de pensar duas vezes antes de perceber que suas declarações não tinham a intenção de implicar o primeiro significado.
Steve Jessop,
11
strncpygeralmente deve ser evitado também. Ele não faz o que a maioria dos programadores presume que ele faz. Ele não garante o encerramento (levando a saturações do buffer) e preenche strings mais curtas (possivelmente degradando o desempenho em alguns casos).
Adrian McCarthy,
2
@Adrian: Eu concordo com você - nem, strncpy()nem o pior strncat()é um substituto sensato para as variantes n-less.
Jonathan Leffler
4
Essa resposta espalha dogmas absurdos sobre "substituir strcpy por strncpy - não sei por que, mas a Microsoft me diz para fazer isso." strncpy nunca teve a intenção de ser uma versão segura de strcpy! Na verdade, é muito mais inseguro. Consulte Por que strlcpy e strlcat são considerados inseguros? .
Lundin,
24

Mais uma vez as pessoas estão repetindo, como um mantra, a afirmação ridícula de que a versão "n" das funções str são versões seguras.

Se fosse para isso que eles se destinavam, eles sempre encerrariam as strings com um valor nulo.

As versões "n" das funções foram escritas para uso com campos de comprimento fixo (como entradas de diretório em sistemas de arquivos anteriores), onde o terminador nul só é necessário se a string não preencher o campo. Esta é também a razão pela qual as funções têm efeitos colaterais estranhos que são ineficientes se usados ​​apenas como substitutos - pegue strncpy () por exemplo:

Se a matriz apontada por s2 for uma string menor do que n bytes, bytes nulos serão acrescentados à cópia na matriz apontada por s1, até que n bytes em todos sejam gravados.

Como os buffers alocados para lidar com nomes de arquivos são normalmente de 4 kbytes, isso pode levar a uma grande deterioração no desempenho.

Se você quiser versões "supostamente" seguras, obtenha - ou escreva suas próprias - rotinas strl (strlcpy, strlcat etc) que sempre terminam as strings e não têm efeitos colaterais. No entanto, observe que eles não são realmente seguros, pois podem truncar silenciosamente a string - raramente é o melhor curso de ação em qualquer programa do mundo real. Há ocasiões em que isso é OK, mas também há muitas circunstâncias em que pode levar a resultados catastróficos (por exemplo, impressão de receitas médicas).

Vareta de medição
fonte
1
Você está certo strncpy(), mas errado strncat(). strncat()não foi projetado para uso com campos de comprimento fixo - na verdade, foi projetado para strcat()limitar o número de caracteres concatenados. É muito fácil usar isso como um "seguro strcat()", mantendo o controle do espaço restante no buffer ao fazer várias concatenações, e ainda mais fácil de usá-lo como um "seguro strcpy()" (definindo o primeiro caractere do buffer de destino '\0'antes chamando). strncat() sempre termina a string de destino e não grava '\0's extras .
caf
2
@caf - sim, mas strncat () é totalmente inútil, pois toma como parâmetro o comprimento máximo a ser copiado - não o comprimento do buffer de destino. Para evitar o estouro de buffer, você precisa saber o comprimento da string de destino atual, e se você sabe por que usar strncat () - que tem que resolver o comprimento de dest novamente - em vez de apenas strlcat () a string de origem para o final da string dest.
Vareta de medição de
Isso ainda não muda o fato de que você insinua que strncat()nem sempre anula o destino e que ele foi projetado para uso com campos de comprimento fixo, ambos errados.
caf
2
@chrisharris: strncat()funcionará corretamente independentemente do comprimento da string de origem, enquanto strcat()não. O problema strlcat()aqui é que não é uma função C padrão.
David Thornley,
@caf - você está correto sobre strncat (). Na verdade, nunca usei porque - como indiquei acima - é totalmente inútil. Portanto, ainda deve ser evitado.
Vareta de medição de
19

Várias respostas aqui sugerem usar strncat()over strcat(); Eu sugeriria que strncat()(e strncpy()) também deve ser evitado. Tem problemas que dificultam o uso correto e levam a bugs:

  • o parâmetro de comprimento para strncat()está relacionado (mas não exatamente - veja o terceiro ponto) ao número máximo de caracteres que podem ser copiados para o destino ao invés do tamanho do buffer de destino. Isso torna strncat()mais difícil de usar do que deveria ser, principalmente se vários itens forem concatenados ao destino.
  • pode ser difícil determinar se o resultado foi truncado (o que pode ou não ser importante)
  • é fácil ter um erro por defeito. Como o padrão C99 observa, "Assim, o número máximo de caracteres que podem terminar na matriz apontada por s1é strlen(s1)+n+1" para uma chamada que se parece comstrncat( s1, s2, n)

strncpy()também tem um problema que pode resultar em bugs que você tenta usar de forma intuitiva - não garante que o destino seja terminado em nulo. Para garantir isso, você precisa ter certeza de lidar especificamente com esse caso de canto, deixando você mesmo um '\0'no último local do buffer (pelo menos em certas situações).

Eu sugiro usar algo como o OpenBSD strlcat()e strlcpy()(embora eu saiba que algumas pessoas não gostam dessas funções; acredito que são muito mais fáceis de usar com segurança do que strncat()/ strncpy()).

Aqui está um pouco do que Todd Miller e Theo de Raadt têm a dizer sobre os problemas com strncat()e strncpy():

Existem vários problemas encontrados quando strncpy()e strncat()são usados ​​como versões seguras de strcpy()e strcat(). Ambas as funções lidam com a terminação NUL e o parâmetro de comprimento de maneiras diferentes e não intuitivas que confundem até programadores experientes. Eles também não fornecem uma maneira fácil de detectar quando ocorre truncamento. ... De todos esses problemas, a confusão causada pelos parâmetros de comprimento e o problema relacionado de terminação NUL são os mais importantes. Quando auditamos a árvore de código do OpenBSD em busca de brechas de segurança em potencial, encontramos uso excessivo de strncpy()e strncat(). Embora nem tudo isso tenha resultado em brechas de segurança exploráveis, eles deixaram claro que as regras de uso strncpy()e strncat()operações de string seguras são amplamente mal interpretadas.

A auditoria de segurança do OpenBSD descobriu que os bugs com essas funções eram "excessivos". Ao contrário gets(), essas funções podem ser usadas com segurança, mas na prática há muitos problemas porque a interface é confusa, não intuitiva e difícil de usar corretamente. Eu sei que a Microsoft também fez análises (embora eu não saiba o quanto de seus dados eles podem ter publicado) e, como resultado, baniu (ou pelo menos desencorajou fortemente - o 'ban' pode não ser absoluto) o uso de strncat()e strncpy()(entre outras funções).

Alguns links com mais informações:

Michael Burr
fonte
1
É muito melhor controlar o comprimento das cordas fora da própria corda. Assim, concatenar duas strings (-with-length) com segurança torna-se uma questão de cálculo simples (para obter o tamanho do buffer necessário), uma possível realocação e a memmove(). (Bem, você pode usar memcpy()no caso normal quando as cordas são independentes.)
Donal Fellows
1
Seu segundo ponto está totalmente errado - strncat() sempre termina a string de destino.
caf
1
Seu segundo ponto está errado strncat(). No entanto, é correto para strncpy(), que tem alguns outros problemas. strncat()é uma substituição razoável para strcat(), mas strncpy()não é uma substituição razoável para strcpy().
David Thornley,
2
caf e David estão 100% certos sobre minha afirmação de que strncat()nem sempre termina nulo. Eu estava confundindo um pouco os comportamentos de strncat()e strncpy()(outra razão pela qual são funções a serem evitadas - eles têm nomes que implicam em comportamentos semelhantes, mas na verdade se comportam de maneiras diferentes de maneiras importantes ...). Eu alterei minha resposta para corrigir isso e também adicionar informações adicionais.
Michael Burr,
1
Observe que char str[N] = ""; strncat(str, "long string", sizeof(str));é um estouro de buffer se N não for grande o suficiente. A strncat()função é muito fácil de ser mal utilizada; não deve ser usado. Se você pode usar strncat()com segurança, você poderia ter usado memmove()ou em memcpy()vez disso (e aqueles seriam mais eficientes).
Jonathan Leffler
9

Funções de biblioteca padrão que nunca devem ser usadas:

setjmp.h

  • setjmp(). Junto com longjmp(), essas funções são amplamente reconhecidas como incrivelmente perigosas de usar: elas levam à programação espaguete, vêm com várias formas de comportamento indefinido, podem causar efeitos colaterais indesejados no ambiente do programa, como afetar os valores armazenados na pilha. Referências: MISRA-C: 2012 regra 21.4, CERT C MSC22-C .
  • longjmp(). Veja setjmp().

stdio.h

  • gets(). A função foi removida da linguagem C (conforme C11), pois não era segura conforme o design. A função já foi marcada como obsoleta no C99. Use em seu fgets()lugar. Referências: ISO 9899: 2011 K.3.5.4.1, consulte também a nota 404.

stdlib.h

  • atoi()família de funções. Eles não têm tratamento de erros, mas invocam um comportamento indefinido sempre que ocorrem erros. Funções completamente supérfluas que podem ser substituídas pela strtol()família de funções. Referências: MISRA-C: 2012 regra 21.7.

string.h

  • strncat(). Possui uma interface estranha que costuma ser mal utilizada. É principalmente uma função supérflua. Veja também as observações para strncpy().
  • strncpy(). A intenção dessa função nunca foi ser uma versão mais segura do strcpy(). Seu único propósito sempre foi lidar com um formato de string antigo em sistemas Unix, e o fato de ter sido incluído na biblioteca padrão é um erro conhecido. Esta função é perigosa porque pode deixar a string sem terminação nula e os programadores costumam usá-la incorretamente. Referências: Por que strlcpy e strlcat são considerados inseguros? .

Funções de biblioteca padrão que devem ser usadas com cuidado:

assert.h

  • assert(). Vem com sobrecarga e geralmente não deve ser usado no código de produção. É melhor usar um manipulador de erros específico do aplicativo que exibe erros, mas não necessariamente fecha todo o programa.

sinal.h

stdarg.h

  • va_arg()família de funções. A presença de funções de comprimento variável em um programa C quase sempre é uma indicação de um projeto de programa ruim. Deve ser evitado, a menos que você tenha requisitos muito específicos.

stdio.h
Geralmente, toda essa biblioteca não é recomendada para código de produção , pois ela vem com vários casos de comportamento mal definido e segurança de tipo pobre.

  • fflush(). Perfeitamente bom para usar em fluxos de saída. Invoca um comportamento indefinido se usado para fluxos de entrada.
  • gets_s(). Versão segura gets()incluída na interface de verificação de limites C11. É preferível usar em fgets()vez disso, de acordo com a recomendação do padrão C. Referências: ISO 9899: 2011 K.3.5.4.1.
  • printf()família de funções. Funções pesadas de recursos que vêm com muito comportamento indefinido e segurança de tipo pobre. sprintf()também tem vulnerabilidades. Essas funções devem ser evitadas no código de produção. Referências: MISRA-C: 2012 regra 21.6.
  • scanf()família de funções. Veja as observações sobre printf(). Além disso, - scanf()é vulnerável a estouros de buffer se não for usado corretamente. fgets()é preferível usar quando possível. Referências: CERT C INT05-C , MISRA-C: 2012 regra 21.6.
  • tmpfile()família de funções. Vem com vários problemas de vulnerabilidade. Referências: CERT C FIO21-C .

stdlib.h

  • malloc()família de funções. Perfeitamente bom para usar em sistemas hospedados, embora esteja ciente dos problemas conhecidos no C90 e, portanto , não lance o resultado . A malloc()família de funções nunca deve ser usada em aplicativos independentes. Referências: MISRA-C: 2012 regra 21.3.

    Observe também que realloc()é perigoso se você substituir o ponteiro antigo com o resultado de realloc(). Caso a função falhe, você cria um vazamento.

  • system(). Vem com muita sobrecarga e, embora portátil, geralmente é melhor usar funções de API específicas do sistema. Vem com vários comportamentos mal definidos. Referências: CERT C ENV33-C .

string.h

  • strcat(). Veja as observações para strcpy().
  • strcpy(). Muito bem de usar, a menos que o tamanho dos dados a serem copiados seja desconhecido ou maior que o buffer de destino. Se nenhuma verificação do tamanho dos dados de entrada for feita, pode haver saturação de buffer. O que não é culpa dele strcpy()mesmo, mas do aplicativo de chamada - que strcpy()não é seguro é principalmente um mito criado pela Microsoft .
  • strtok(). Altera a string do chamador e usa variáveis ​​de estado internas, o que pode torná-lo inseguro em um ambiente multi-thread.
Lundin
fonte
Eu sugiro recomendar static_assert()no lugar de assert(), se a condição puder ser resolvida em tempo de compilação. Além disso, sprintf()quase sempre pode ser substituído, o snprintf()que é um pouco mais seguro.
user694733
1
Usar strtok()em uma função A significa que (a) a função não pode chamar qualquer outra função que também use strtok()enquanto A está usando, e (b) significa que nenhuma função que chama A pode estar usando strtok()quando ela chama A. Em outras palavras, usando strtok()envenena a cadeia de chamadas; ele não pode ser usado com segurança no código da biblioteca porque deve documentar que ele usa strtok()para impedir que outros usuários strtok()chamem o código da biblioteca.
Jonathan Leffler
O strncpyé a função apropriada para usar ao gravar um buffer de string preenchido com zero com dados retirados de uma string terminada com zero ou de um buffer preenchido com zero cujo tamanho é pelo menos tão grande quanto o destino. Buffers preenchidos com zeros não são terrivelmente comuns, mas também não são exatamente obscuros.
supercat
7

Algumas pessoas afirmam que strcpye strcatdeve ser evitado, em favor de strncpye strncat. Isso é um tanto subjetivo, na minha opinião.

Definitivamente, eles devem ser evitados ao lidar com a entrada do usuário - sem dúvida aqui.

No código "longe" do usuário, quando você só sabe os buffers são suficientemente longo, strcpye strcatpode ser um pouco mais eficiente, pois o cálculo da npara passar para seus primos pode ser supérfluo.

Eli Bendersky
fonte
4
E se disponível, melhor ainda strlcate strlcpy, já que a versão 'n' não garante terminação NULL da string de destino.
Dan Andreatta,
Parece otimização prematura para mim. Mas IIRC, strncpy()escreverá exatamente nbytes, usando caracteres nul se necessário. Conforme Dan, usar uma versão segura é a melhor escolha da IMO.
Bastien Léonard,
@Dan, essas funções infelizmente não são padrão e, portanto, não pertencem a esta discussão
Eli Bendersky,
@Eli: mas o fato de que strncat()pode ser difícil de usar corretamente e com segurança (e deve ser evitado) está no assunto.
Michael Burr,
@Michael: Eu não diria que é difícil de usar corretamente e com segurança. Com cuidado, funciona muito bem
Eli Bendersky,
6

Evitar

  • strtok para programas multithread porque não é seguro para thread.
  • gets pois pode causar estouro de buffer
codadicto
fonte
2
São casos ligeiramente diferentes. Você pode usar strtok com segurança se souber que seu programa não é multithread ou se de alguma forma bloquear o acesso a ele, mas não pode usar get com segurança, então nunca deve usá-lo.
jcoder
9
O problema com strtok()vai um pouco além da segurança de thread - não é seguro nem mesmo em um único programa de thread a menos que você tenha certeza de que nenhuma função que seu código possa chamar enquanto usa strtok()não o use (ou eles bagunçarão o strtok()estado certo abaixo de você). Na verdade, a maioria dos compiladores que visam plataformas multi-threaded cuidam dos strtok()problemas potenciais de até onde vão os threads usando armazenamento local de thread para strtok()os dados estáticos de. Mas isso ainda não resolve o problema de outras funções usá-lo enquanto você está (no mesmo segmento).
Michael Burr,
De fato, vou calar a boca agora, pois não quero encorajar ninguém a usar strtok, pois ele sofre de muitos problemas. Eu só queria salientar que ele é diferente de get, pois é possível usá-lo com segurança, enquanto é impossível usar get com segurança.
jcoder
@Jimmy: Muitas vezes existem extensões de biblioteca fora do padrão, ou você pode escrever a sua própria. O grande problema com strtok()é que ele mantém precisamente um buffer para trabalhar, então a maioria das boas substituições exige manter um valor de buffer e transmiti-lo.
David Thornley
strcspnfaz a maior parte do que você precisa - encontre o próximo separador de token. Você pode reimplementar uma variante sensata de strtokcom ele.
bluss de
5

Provavelmente, vale a pena adicionar novamente que strncpy()não é a substituição de propósito geral para o strcpy()que seu nome pode sugerir. Ele é projetado para campos de comprimento fixo que não precisam de um terminador nul (ele foi originalmente projetado para uso com entradas de diretório UNIX, mas pode ser útil para coisas como campos de chave de criptografia).

É fácil, no entanto, usar strncat()como substituto para strcpy():

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

(O ifteste pode obviamente ser descartado no caso comum, onde você sabe que dest_sizeé definitivamente diferente de zero).

caf
fonte
5

Verifique também a lista de APIs proibidas da Microsoft . Essas são APIs (incluindo muitas já listadas aqui) que são banidas do código da Microsoft porque muitas vezes são mal utilizadas e levam a problemas de segurança.

Você pode não concordar com todos eles, mas vale a pena considerar todos eles. Eles adicionam uma API à lista quando seu uso indevido leva a uma série de bugs de segurança.

Adrian McCarthy
fonte
2

Quase todas as funções que lidam com strings terminadas em NUL são potencialmente inseguras. Se você está recebendo dados do mundo externo e os manipulando por meio das funções str * (), então você se preparou para a catástrofe

rep_movsd
fonte
2

Não se esqueça do sprintf - ele é a causa de muitos problemas. Isso é verdade porque a alternativa, snprintf, às vezes tem implementações diferentes que podem tornar seu código não portável.

  1. linux: http://linux.die.net/man/3/snprintf

  2. windows: http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

No caso 1 (linux), o valor de retorno é a quantidade de dados necessária para armazenar todo o buffer (se for menor que o tamanho do buffer fornecido, a saída foi truncada)

No caso 2 (windows) o valor de retorno é um número negativo caso a saída seja truncada.

Geralmente, você deve evitar funções que não são:

  1. buffer overflow safe (muitas funções já são mencionadas aqui)

  2. thread seguro / não reentrante (strtok, por exemplo)

No manual de cada função você deve procurar por palavras-chave como: seguro, sincronização, async, thread, buffer, bugs

INS
fonte
2
_sprintf()foi criado pela Microsoft antes da snprintf()chegada do padrão , o IIRC. ´StringCbPrintf () ´ é bastante semelhante a snprintf()embora.
Bastien Léonard,
você pode usar de sprintfalguma forma com segurança em alguns casos: sprintf(buffer,"%10s",input);limita os nb bytes copiados a 10 (se bufferfor char buffer[11], é seguro, mesmo que os dados possam acabar truncados.
Jean-François Fabre
2

É muito difícil de usar scanfcom segurança. O bom uso de scanfpode evitar estouros de buffer, mas você ainda está vulnerável a um comportamento indefinido ao ler números que não se encaixam no tipo solicitado. Na maioria dos casos, fgetsseguido por auto-análise (usando sscanf, strchr, etc.) é a melhor opção.

Mas eu não diria "evite scanfo tempo todo". scanftem seus usos. Como exemplo, digamos que você queira ler a entrada do usuário em uma charmatriz de 10 bytes. Você deseja remover a nova linha final, se houver. Se o usuário inserir mais de 9 caracteres antes de uma nova linha, você deseja armazenar os primeiros 9 caracteres no buffer e descartar tudo até a próxima nova linha. Você pode fazer:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

Depois que você se acostuma com esse idioma, ele é mais curto e, de certa forma, mais limpo que:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}
Alok Singhal
fonte
0

Em todos os cenários de cópia / movimentação de string - strcat (), strncat (), strcpy (), strncpy (), etc. - as coisas vão muito melhor ( mais seguras ) se algumas heurísticas simples forem aplicadas:

   1. Sempre preencher NUL seu (s) buffer (es) antes de adicionar dados.
   2. Declare buffers de caracteres como [SIZE + 1], com uma constante de macro.

Por exemplo, dado:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

podemos usar códigos como:

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

com relativa segurança. O memset () deve aparecer antes de strncpy (), mesmo que tenhamos inicializado o Buffer em tempo de compilação, porque não sabemos que lixo o outro código colocou nele antes de nossa função ser chamada. O strncpy () truncará os dados copiados para "1234567890" e não os encerrará em NUL. No entanto, uma vez que já preenchemos o NUL de todo o buffer - sizeof (Buffer), em vez de BUFSIZE - é garantido que haverá um final "fora do escopo" finalizando o NUL de qualquer maneira, contanto que restrinjam nossas gravações usando o BUFSIZE constante, em vez de sizeof (Buffer).

Buffer e BUFSIZE também funcionam bem para snprintf ():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

Embora snprintf () grave especificamente apenas caracteres BUFIZE-1, seguidos de NUL, isso funciona com segurança. Portanto, "desperdiçamos" um byte NUL estranho no final do Buffer ... evitamos o estouro do buffer e as condições de string não terminadas, por um custo de memória bem pequeno.

Minha chamada em strcat () e strncat () é mais rígida: não os use. É difícil usar strcat () com segurança, e a API para strncat () é tão contra-intuitiva que o esforço necessário para usá-la corretamente anula qualquer benefício. Proponho a seguinte visita:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

É tentador criar um drop-in strcat (), mas não é uma boa ideia:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

porque target pode ser um ponteiro (portanto, sizeof () não retorna as informações de que precisamos). Não tenho uma boa solução "universal" para instâncias de strcat () em seu código.

Um problema que encontro frequentemente com programadores "cientes de strFunc ()" é uma tentativa de proteção contra estouros de buffer usando strlen (). Isso é bom se o conteúdo tiver a garantia de terminação NUL. Caso contrário, strlen () em si pode causar um erro de saturação de buffer (geralmente levando a uma violação de segmentação ou outra situação de despejo de núcleo), antes mesmo de você alcançar o código "problemático" que está tentando proteger.

TLR
fonte
-2

atoi não é thread-safe. Eu uso strtol em vez disso, por recomendação da página de manual.

Fred
fonte
5
Parece que se aplica a uma implementação específica. Não há nenhuma razão para strtol()que seja thread-safe e atoi()não seja.
David Thornley,
2
A recomendação de usar strtol não tem nada a ver com segurança de thread, mas com tratamento de erros. Não tenho certeza de qual página do manual você obteve essa informação - não consigo encontrar nenhuma recomendação abaixo man atoi(no entanto, deveria haver).
Lundin