애플리케이션 실행 로직의 오류를 감지하고 싶습니다. 예를 들어:
free()
반송 주소로 전화하는 것을 잊어버렸습니다.malloc()
- 반환된 파일 핸들이 닫히지 않았습니다.
open()
- 잘못된 플래그가 전달되었습니다.
open()
- 전달된 파일 핸들이 잘못되었습니다.
poll()
write()
쓰기용으로 열려 있지 않은 fd 호출open()
예를 들어 open("/etc/fstab", 4)에 잘못된 플래그를 전달합니다.close()
잘못된 fd 호출 중- ...
수백 개가 더 있는 것 같아요.
아마도 도구는 ftrace
또는 처럼 실행될 수 있지만 strace
오류 호출이 포함된 커널 로그로도 충분할 것입니다.
답변1
free()
반송 주소로 전화하는 것을 잊어버렸습니다.malloc()
글쎄, malloc
이것은 free
커널 호출이 아닙니다! 뭐죠 malloc()
(이건 libc 라이브러리 함수, 일반 사용자 프로세스 코드입니다!)
- 메모리 풀에 요청된 크기의 블록이 있는지 확인
- 그렇다면 사용된 것으로 표시되어 프로그램에 반환됩니다. 그렇지 않은 경우 호출
sbrk
(또는 이와 동등하게 익명 메모리 할당이 일반적임)은 커널에 특정 수의 새 가상 메모리 페이지를 요청하여 풀에 추가한 다음 프로그램의 요구 사항을 충족합니다.
free
이전에 에서 반환한 s 메모리 블록을 가져 malloc
오면 메모리 풀에서 사용되지 않은 것으로 표시합니다. (그렇지 않으면 정의되지 않은 동작이 발생하지만 대부분의 libc는 이 시점에서 중단됩니다.) 대부분의 구현은 free
메모리를 운영 체제에 반환하려고 시도하지도 않습니다!
이제 메모리 정리를 원할 경우 이러한 항목 과 호출을 모니터링하고 메모리 블록의 주소가 프로그램 어딘가에 여전히 "저장"되어 있는지 추적할 gcc -fsanitize
수 있는 도구(valgrind 등) 가 있습니다. 함수가 종료되면 해당 주소를 보유하는 포인터가 더 이상 존재하지 않으므로 메모리가 할당되었다는 사실을 아무도 기억하지 못합니다. 그것은 실제 버그입니다. 메모리를 즉시 해제하지 않거나 프로그램이 끝날 때까지 해제를 연기하는 것은 전혀 문제가 되지 않습니다. 요점 은 당신이 얻는 메모리의 수명이 잠재적으로 무한하다는 것입니다! (팁: 이러한 문제가 걱정되고 그렇게 하는 것이 옳다면 C를 작성하지 마십시오. 객체 수명을 적절하게 추적할 수 있는 언어로 작성하십시오. 이는 Rust 또는 C++와 같은 언어일 것입니다. 하지만 후자free
malloc
malloc
malloc
C++를 C의 확장으로 간주하지 않는 한. 내 C++ 코드에서 전혀 사용되지 않거나 new
더 나쁜 일부 대형 프로그램이 있습니다. malloc
스마트 포인터는 어깨에서 많은 함정을 제거합니다. 이는 C++에서도 매우 중요합니다.허용하다수동 메모리 제어를 수행할 수 있지만 최신 변형에서는 비용이 들지 않는 개체 수명 추적을 제공하여 그렇게 하지 않는 것이 좋습니다. )
반환된 파일 핸들이 닫히지 않았습니다.
open()
그건 문제가되지 않습니다! 메모리에 비해 프로그램이 끝날 때까지 파일을 열어 두는 것은 완벽하게 허용 가능하고 심지어 합리적입니다. 예를 들어 파일 잠금을 즉시 포기하면 작동하지 않습니다. 그리고 제어 인터페이스는 프로그램이 닫힐 때까지 열려 있어야 합니다.
다시 말하지만, 프로그램의 제어 흐름 내에서 수천 개의 파일이 열려 있고 닫는 것을 잊을 수 있다는 것이 걱정된다면 C로 작성하지 말고 파일 핸들에 수명이 있고 파일 핸들이 닫힐 수 있는 언어로 작성하세요. 폐쇄되다. 더 이상 필요하지 않은 경우 기본 파일 설명자입니다.
그냥: "열린 파일이 있는데 아직 닫히지 않은 파일이 있습니다"는 전혀 문제가 되지 않습니다.특히POSIX 시스템에서는 동시 파일 액세스가 정상이며 여러 면에서 잘 정의되어 있습니다.
잘못된 플래그가 전달되었습니다.
open()
오류 코드를 반환하는 것 외에 이를 어떻게 알 수 있나요?
내 말은, 라이브러리가 "쓰기+추가" 모드에서 파일을 열 수 있는지 확인하는 것이 정상이지만 열 수 없는 경우에는 문제가 되지 않는다는 것입니다.
시스템 호출이 이루어질 때마다 관찰하고 해당 인수와 인기 프로그램에서 사용되는 것과 같이 사용자 공간에 "반환"되는 내용을 얻으려면 ptrace
시스템 호출이 도움이 됩니다 . strace
다른 옵션에는 eBPF 프로브 또는 업로브 작성이 포함되며, 이는 매우 효율적이며 해당 이벤트의 로깅을 "지능적으로 필터링"하는 데 사용할 수 있습니다.
전달된 파일 핸들이 잘못되었습니다.
poll()
이전 질문과 마찬가지로 이는 파일 핸들을 폴링할 수 있는지 여부를 확인하는 프로그램일 수 있습니다. 모든 (의사) 파일 시스템에 해당되는 것은 아닙니다.
또한 poll
필요한 경우 실제로는 적어도 glibc에서 제공하는 래퍼 함수(기호)의 이름이며 "void"는 시스템 호출의 "void"와 다를 수 있습니다 poll
.
답변2
첫 번째, 할당된 메모리 블록 해제 실패는 malloc()
반드시 커널과 관련이 있는 것은 아닙니다. 메모리 할당은 C 라이브러리에 의해 처리됩니다. 월그라인드의메모리 체크이것들은 감지될 수 있습니다.
커널에서 반환된 오류를 추적하려면 다음 명령을 사용하여 프로그램을 실행할 수 있습니다 strace -Z
( strace
5.2부터 사용 가능). 이 명령은 오류를 반환하는 시스템 호출만 추적합니다. 예 EBADFD
를 들어 결과를 사후 처리해야 합니다.EINVAL
호출에 실패 free()
하거나 close()
반드시 오류가 되는 것은 아닙니다. 이러한 리소스는 어쨌든 프로그램이 종료될 때 해제되므로 어떤 경우에는 리소스를 명시적으로 포기하지 않는 것이 완벽하게 허용됩니다.
답변3
이러한 일반적인 코딩 오류를 찾는 데 사용할 수 있는 다양한 정적 코드 분석 도구가 있습니다. 모든 시나리오를 포괄하는지 말할 수는 없지만 SonarQube는 C를 지원하는 도구 중 하나입니다.https://www.sonarqube.org/features/multi-랭귀지/c
C 언어에는 수백 가지의 SonarQube 규칙이 있습니다:https://rules.sonarsource.com/c
정적 코드 분석이 충분하지 않은 경우 실행 중인 프로그램을 검사하기 위해 동적 분석이 필요할 수 있습니다.https://en.wikipedia.org/wiki/Dynamic_program_analytic