% cat segfault.c
#include <stdio.h>
int main()
{
char *s = "hello world";
*s = 'H';
printf("%s\n", s);
}
% CFLAGS=-g make segfault
gcc -g segfault.c -o segfault
O erro vem de algo executando uma chamada waitpid
, geralmente o shell:
% ./segfault
zsh: bus error ./segfault
como aqui zsh
caiu de um waitpid
e, em seguida, percorreu algum caminho de código envolvendo WIFSIGNALED
. (o macOS emite um erro de barramento em vez de um segfault (uma rosa por qualquer outro nome) e o erro exato da string irá variar dependendo do shell.)
O OpenSSH portable (a partir do commit ed7bd5d93fe14c7bd90febd29b858ea985d14d45) faz várias chamadas WIFSIGNALED(status)
notavelmente em misc.c
, session.c
e sshd.c
; alguns desses return -1
que poderiam facilmente se transformar no status de saída 255
, embora eu precise adicionar depuração ou rastreio sshd
para ver exatamente como esse caso acontece ( ssh -v -v -v
não ajuda, nem o padrão sshd
logs).
Como kluge, você pode forçar um waitpid
a acontecer. Isso requer enganar um shell para não executar um exec
simples para substituir a si mesmo, o que ele fará como uma otimização, se puder:
% ssh localhost 'sh -c ./segfault'
% ssh localhost ':; ./segfault'
% ssh localhost 'sh -c ":; ./segfault"'
sh: line 1: 9068 Bus error: 10 ./segfault
%
:; ...
é complexo o suficiente para que sh
não fork
/ exec
e, em vez disso, termine waitpid
on segfault
e, em seguida, relate isso. Observe que o relatório de erros irá variar dependendo do sabor exato de sh
.
% ssh localhost '/usr/local/bin/sh -c ":; ./segfault"'
Bus Error
Agora, se sh
está causando o segfault (ou qualquer sinal inesperado), você precisará inspecionar o código de saída do SSH. Outra opção seria chamar um pequeno wrapper que faz o waitpid
sem truques de shell:
#include <sys/wait.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int status;
pid_t pid;
if (argc < 2) {
fprintf(stderr, "Usage: waiter command [args ..]\n");
exit(1);
}
pid = fork();
if (pid < 0) {
err(1, "could not fork");
} else if (pid == 0) { /* child */
argv++;
execvp(*argv, argv);
err(1, "could not exec");
} else { /* parent */
if (waitpid(pid, &status, 0) < 0)
err(1, "could not waitpid");
if (WIFEXITED(status)) {
exit(WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
warnx("child exited with signal %d", WTERMSIG(status));
exit(128 + WTERMSIG(status));
} else {
err(1, "unknown waitpid condition?? status=%d", status);
}
}
exit(1);
}
... embora este wrapper também possa segfault (ou qualquer sinal), especialmente se o servidor tiver problemas de hardware, memória ruim, etc.
% ssh localhost ./waiter ./segfault
waiter: child exited with signal 10
No entanto, o wrapper tem muito menos código do que um típicosh
% (~ 50 linhas versus ~ 10.000 para o shell bourne da herança) por isso deve ser menos provável que ele próprio cause uma condição de saída por sinal. (Você verificou o código de saída, certo?)