2010-03-22 2 views
7

아주 이상한 사고를 추적하려고합니다. 너무 이상한 점은 누군가가 발견했는데 설명 할 수없는 해결 방법입니다. 하지exec가 exec'ed 프로그램의 동작을 어떻게 바꿀 수 있습니까?

#include <stdio.h> 
#include <unistd.h> 
#include <string.h> 
#include <errno.h> 

int main(int argc, char *argv[]) 
{ 
    if (argc == 1) 
    { 
     fprintf(stderr, "Usage: %s prog [args ...]\n", argv[0]); 
     return 1; 
    } 

    execvp(argv[1], argv + 1); 

    fprintf(stderr, "execv failed: %s\n", strerror(errno)); 

    // If exec returns because the program is not found or we 
    // don't have the appropriate permission 
    return 255; 
} 

당신이 볼 수 있듯이,이 모든 프로그램은 다른 프로그램 자체를 대체하는 execvp을 사용할 수 있습니다 :

해결 방법은 내가 '주자'라고 부르는 것이 작은 프로그램입니다.

프로그램 충돌이 직접 명령 줄에서 호출됩니다

/path/to/prog args # this crashes 

하지만 간접적으로 내 주자 심을 통해 호출 될 때 잘 작동 : 내 인생

/path/to/runner /path/to/prog args # works successfully 

, 여분의 exec가 실행중인 프로그램의 동작을 어떻게 바꿀 수 있는지를 알아낼 수 있습니다 (프로그램이 환경을 변경하지 않는 것을 볼 수 있듯이).

충돌에 대한 배경 정보입니다. 크래시 자체가 C++ 런타임에서 발생합니다. 특히 프로그램에서 throw을 실행하면 충돌하는 버전에서 일치하는 catch가 있다고 잘못 인식하고 (terminate) 호출합니다. 러너를 통해 프로그램을 호출 할 때 예외가 제대로 포착됩니다.

제 질문은 추가 exec가 exec'ed 프로그램의 동작을 변경하는 이유는 무엇입니까?

+0

잘 모르겠다.하지만 execvp가 작업 디렉토리를 변경합니까? 어떤 논쟁을하고 있습니까? –

+0

@MartinYork - AFAIK,'execvp'는 작업 디렉토리를 변경하지 않습니다 ('chdir'에 대한 호출이 필요하고 러너는 그렇게하지 않습니다). 구체적인 주장은 부적합하다. 설명 된 동작은 프로그램에 전달 된 특정 인수와 독립적입니다. –

+1

'execvp()'대신에'execv()'를 사용하면 똑같은 일을합니까? – caf

답변

3

런너가로드 한 .so 파일로 인해 runee가 올바르게 작동 할 수 있습니다. 각 바이너리를 ldd로 시도하고 라이브러리가 다른 버전/위치를로드하고 있는지 확인하십시오.

+1

이슈는'ld-linux.so.2'가 특정 공유 객체를 주 바이너리 나 그 이후의 주소 공간에 매핑하는지 여부입니다 (실제 버그는 다른 곳에 있지만 상황에 따라 버그가 나타납니다. 하위 바이너리 주소). –

0

어둠 속에서 촬영 한 것처럼 : double-exec는 RAM의 환경 변수 순서를 변경할 수 있습니다.

환경은 포인터가있는 메모리 구조입니다. 커널은 그 구조를 새로운 프로세스의 주소 공간에 복사합니다. RAM에있는 요소의 실제 순서는 복사 중에 변경 될 수 있습니다 (환경 변수는 의미 론적으로 정렬되지 않지만 RAM의 주소에는 순서가 있음). 두 개의 exec()를 사용하면 순서가 두 번 수정 될 수 있습니다.

RAM에있는 문자열의 순서가 변경되면 버그가 발각된다는 점은 다소 괴상하지만 이상한 일이 발생했습니다.

+0

제안 해 주셔서 감사하지만 그럴 것 같지 않습니다. 원시 환경 블록을 버렸고 둘 다 동일한 순서를가집니다. –

0

당신이 argv [0]에서 뭔가 다른 것을 쉘이 전달하는지 궁금합니다. 위에서 쓴 내용을 볼 수는 없지만 argv [0]을 프로그램의 첫 번째 인수로 설정하는 것이 가능합니다. 반면 쉘은이를 호출 된 이름 (예 : 전체 또는 짧은 경로)으로 설정합니다)

+0

@MarkR - 귀하의 제안에 감사드립니다. 나는 argv [0]이 경로를 포함하지 않도록 러너를 수정했다. 불행히도, 나는 여전히 같은 행동을보고 있습니다. –

1

아마도 호출 된 프로그램에 메모리 누수가 있습니다. valgrind 또는 다른 메모리 검사 도구로 실행 해보십시오. 메모리 오류가 발생하면 그 밖의 모든 동작은 정의되지 않은 동작입니다 (모든 것이 발생할 수 있습니다).

+0

그 밖의 경우 종료 블록에 정상적으로 도달 할 수있는 경우 Valgrind는 종료되는 버전 (또는 해당 문제에 대해 종료되지 않는 버전)에서 오류를 감지하지 않습니다. –

0

'working'과 'crashing'버전 (오픈 파일 디스크립터와 시그널 핸들러)을 비교할 수있는 두 가지가 exec에 의해 전달되는 것처럼 보입니다.

나는 그들이 어떻게 문제가되는지/다를 수는 없지만, 그것들을 삭제할 가치가있을 수 있습니다.