A resposta de Gilles explica a condição da corrida. Eu só vou responder a esta parte:
Is there any way I can force this script to output always 0 lines (so the I/O redirection to tmp is always prepared first and so the data is always destroyed)? To be clear, I mean changing the system settings
IDK, se já existe uma ferramenta para isso, mas tenho uma ideia de como alguém pode ser implementado. (Mas note que isso não seria sempre 0 linhas, apenas um testador útil que pega facilmente raças simples como esta, e algumas raças mais complicadas. Veja @ Comentário de Gilles .) Não garante que um script seja seguro , mas pode ser uma ferramenta útil em testes, semelhante ao teste de um programa multi-thread em diferentes CPUs, incluindo CPUs não-x86 fracamente ordenadas como ARM.
Você executaria isso como racechecker bash foo.sh
Use os mesmos recursos de rastreamento / interceptação de chamadas do sistema que strace -f
e ltrace -f
usam para anexar a cada processo filho. (No Linux, essa é a mesma ptrace
chamada do sistema usada pelo GDB e outros depuradores para definir pontos de parada, etapa única, e modificar memória / registros de outro processo.)
Instrua as chamadas de sistema open
e openat
: quando qualquer processo executado sob essa ferramenta fizer uma uma chamada de sistema open(2)
(ou openat
) com O_RDONLY
, dormir por talvez 1/2 ou 1 segundo. Deixe que outras chamadas do sistema open
(especialmente as que incluem O_TRUNC
) sejam executadas sem atraso.
open()
s (e talvez read()
s ou gravações) está atrasada aumentaria o poder de detecção dessa ferramenta, mas é claro, sem testes por um período infinito de tempo com um simulador de atraso que acabará por cobrir todas as situações possíveis que você pode encontrar no mundo real, você não pode ter certeza de que seus scripts estão livres de corridas, a menos que você os leia atentamente e prove que eles não são.
Provavelmente, você precisará da lista de permissões (não demora open
) para arquivos em /usr/bin
e /usr/lib
, para que a inicialização do processo não demore para sempre. (A vinculação dinâmica em tempo de execução precisa de open()
multiple files (veja em strace -eopen /bin/true
ou /bin/ls
sometime), embora, se o próprio shell pai estiver fazendo o truncamento, isso será ok. Mas ainda será bom para essa ferramenta não torne os scripts excessivamente lentos).
Ou, talvez, coloque na lista de permissões todos os arquivos que o processo de chamada não tenha permissão para truncar em primeiro lugar. ou seja, o processo de rastreamento pode fazer uma chamada de sistema access(2)
antes de realmente suspender o processo que queria open()
um arquivo.
racechecker
teria que ser escrito em C, não no shell, mas poderia usar o código de strace
como ponto de partida e pode não precisar de muito trabalho para implementar.
Você talvez tenha a mesma funcionalidade com um sistema de arquivos FUSE . Provavelmente há um exemplo de FUSE de um sistema de arquivos puro de passagem, portanto, você poderia adicionar verificações à função open()
naquilo que faz com que ele durma para somente leitura, mas permita que o truncamento aconteça imediatamente.