Há um comando shell unix ( udevadm info -q path -n /dev/ttyUSB2
) que eu quero chamar de um programa C. Com provavelmente cerca de uma semana de luta, eu poderia reimplementá-lo, mas não quero fazer isso.
É uma boa prática amplamente aceita que eu chame apenas popen("my_command", "r");
, ou isso introduzirá problemas de segurança inaceitáveis e encaminhará problemas de compatibilidade? Parece errado fazer algo assim, mas não sei por que isso seria ruim.
udevadm
executável malicioso no mesmo diretório do seu programa e usá-lo para escalar privilégios? Não sei muito sobre segurança unix, mas o seu programa será executadosetuid root
?PATH
, então não - ele teria que ser executado./udevadm
. O Windows IIRC executaria executáveis no mesmo diretório.Respostas:
Não é particularmente ruim, mas existem algumas ressalvas.
LANG
etc.?;rm -rf /
(por exemplo) (observe que algumas APIs permitem especificar args com mais segurança do que apenas fornecê-los na linha de comando)Geralmente, sou feliz em executar binários quando estou em um ambiente conhecido que posso prever, quando a saída binária é fácil de analisar (se necessário - você pode precisar apenas de um código de saída) e não preciso fazer isso também frequentemente.
Como você observou, a outra questão é quanto trabalho é replicar o que o binário faz? Ele usa uma biblioteca que você também pode aproveitar?
fonte
Forking a binary results in a lot more load and requires more resources than executing library calls (generally speaking).
Se isso estivesse no Windows, eu concordo. No entanto, o OP especifica para o Unix que o uso de bifurcação é baixo o suficiente para ser difícil dizer se o carregamento dinâmico de uma biblioteca é pior do que o bifurcação.exec
que dofork
. Carregando seções, vinculando objetos compartilhados, executando o código init para cada um. E depois ter que converter todos os dados em texto para serem analisados do outro lado, tanto para entradas quanto para saídas.udevadm info -q path -n /dev/ttyUSB2
não vai funcionar de qualquer maneira no Windows, então toda a discussão sobre bifurcação é um pouco teórica.É preciso muito cuidado para evitar vulnerabilidades de injeção depois de introduzir um vetor em potencial. Está na vanguarda de sua mente agora, mas mais tarde você pode precisar selecionar
ttyUSB0-3
, então essa lista será usada em outros lugares, para que ela seja levada em consideração para seguir o princípio de responsabilidade única; então, o cliente terá que informar um dispositivo arbitrário na lista, e o desenvolvedor que fizer essa alteração não fará idéia de que a lista eventualmente será usada de maneira insegura.Em outras palavras, codifique como se o desenvolvedor mais descuidado que você conhece está fazendo uma alteração insegura em uma parte aparentemente não relacionada do código.
Segundo, a saída das ferramentas CLI geralmente não são consideradas interfaces estáveis, a menos que a documentação as marque especificamente como tal. Talvez você esteja bem contando com eles para um script executado e que possa solucionar problemas sozinho, mas não para algo implantado em um cliente.
Terceiro, se você deseja uma maneira fácil de extrair um valor do seu software, é provável que alguém o queira também. Procure uma biblioteca que já faça o que você deseja.
libudev
já estava instalado no meu sistema:Há outras funcionalidades úteis nessa biblioteca. Meu palpite é que, se você precisar disso, algumas das funções relacionadas também podem ser úteis.
fonte
ttyUSB2
e usasprintf(buf, "udevadm info -q path -n /dev/%s", argv[1])
. Agora isso parece bastante seguro, mas e seargv[1]
for"nul || echo OOPS"
?No seu caso específico, onde você deseja invocar
udevadm
, eu suspeitaria que você poderia entrarudev
como uma biblioteca e fazer as chamadas de função apropriadas como alternativa?por exemplo, você pode dar uma olhada no que o udevadm está fazendo ao chamar no modo "info": https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.c e fazer chamadas equivalentes quanto aos que o udevadm está fazendo.
Isso evitaria muitas das desvantagens listadas na excelente resposta de Brian Agnew - por exemplo, não depender do binário existente em um determinado caminho, evitando as despesas de bifurcação, etc.
fonte
Sua pergunta parecia exigir uma resposta da floresta, e as respostas aqui parecem respostas de árvores, então pensei em fornecer uma resposta da floresta.
É muito raramente como os programas em C são gravados. É sempre como os scripts de shell são escritos e, às vezes, como os programas Python, perl ou Ruby são escritos.
As pessoas normalmente escrevem em C para facilitar o uso das bibliotecas do sistema e o acesso direto de baixo nível às chamadas do sistema OS, bem como a velocidade. E C é uma linguagem difícil de escrever, portanto, se as pessoas não precisam dessas coisas, elas não usam C. Também se espera que os programas em C tenham dependências apenas de bibliotecas compartilhadas e arquivos de configuração.
Descascar para um subprocesso não é particularmente rápido e não requer acesso refinado e controlado a instalações de sistema de baixo nível, e introduz uma dependência possivelmente surpreendente de um executável externo, portanto, é incomum ver em programas C.
Existem algumas preocupações adicionais. As preocupações de segurança e portabilidade mencionadas pelas pessoas são completamente válidas. Eles são igualmente válidos para scripts de shell, é claro, mas as pessoas esperam esses tipos de problemas nos scripts de shell. Porém, normalmente não se espera que os programas C tenham essa classe de preocupação com segurança, o que a torna mais perigosa.
Mas, na minha opinião, as maiores preocupações têm a ver com a maneira como ele
popen
irá interagir com o restante do seu programa.popen
precisa criar um processo filho, ler sua saída e coletar seu status de saída. Enquanto isso, o stderr desse processo será conectado ao mesmo stderr do seu programa, o que pode causar resultados confusos, e o stdin será o mesmo do seu programa, o que pode causar outros problemas interessantes. Você pode resolver isso incluindo</dev/null 2>/dev/null
a string para a qual você passa, umapopen
vez que é interpretada pelo shell.E
popen
cria um processo filho. Se você fizer alguma coisa com os processos de manipulação ou bifurcação deSIGCHLD
sinais, poderá receber sinais estranhos . Suas chamadas parawait
podem interagir de maneira estranhapopen
e possivelmente criar condições de corrida estranhas.As preocupações de segurança e portabilidade estão aí, é claro. Como eles são para scripts de shell ou qualquer coisa que inicie outros executáveis no sistema. E você deve ter cuidado para que as pessoas que usam o seu programa não consigam colocar metacaracteres de shell na string que você passa,
popen
porque essa string é fornecida diretamentesh
comsh -c <string from popen as a single argument>
.Mas não acho que sejam por isso que é estranho ver um programa em C usando
popen
. O motivo é estranho, porque C é tipicamente uma linguagem de baixo nível epopen
não é de baixo nível. E porque o uso depopen
restrições de design coloca em seu programa, porque ele irá interagir estranhamente com a entrada e saída padrão do seu programa e dificulta o gerenciamento de processos ou a manipulação de sinais. E porque normalmente não se espera que os programas em C tenham dependências em executáveis externos.fonte
Seu programa pode estar sujeito a hackers, etc. Uma maneira de se proteger desse tipo de atividade é criar uma cópia do ambiente atual da máquina e executar o programa usando um sistema chamado chroot.
Do ponto de vista do seu programa, ele é executado em um ambiente normal, do ponto de vista da segurança, se alguém interrompe seu programa, ele só tem acesso aos elementos que você forneceu quando fez a cópia.
Essa configuração é chamada de prisão chroot para obter mais detalhes, consulte prisão chroot .
É normalmente usado para configurar servidores acessíveis ao público etc.
fonte