Programing

정의되지 않은 동작을 감지하는 C ++ 구현?

lottogame 2020. 12. 13. 08:32
반응형

정의되지 않은 동작을 감지하는 C ++ 구현?


C ++에서 엄청난 수의 작업이 수행되면 정의되지 않은 동작이 발생합니다. 여기서 사양은 프로그램의 동작이 무엇인지에 대해 완전히 음소거되고 모든 일이 발생하도록 허용합니다. 이 때문에 사람들이 디버그에서 컴파일되지만 릴리스 모드가 아닌 코드를 가지고 있거나, 겉보기에 관련없는 변경이 이루어질 때까지 작동하거나, 한 머신에서는 작동하지만 다른 머신에서는 작동하지 않는 등의 모든 종류의 경우가 있습니다.

내 질문은 C ++ 코드의 실행을보고 프로그램이 정의되지 않은 동작을 호출하는 모든 인스턴스에 플래그를 지정하는 유틸리티가 있는지 여부입니다. valgrind 및 확인 된 STL 구현과 같은 도구가 있다는 것은 좋지만 제가 생각하는 것만 큼 강력하지는 않습니다. 예를 들어 아직 할당 한 메모리를 폐기하고 STL 구현을 확인한 경우 valgrind는 거짓 부정을 가질 수 있습니다. 기본 클래스 포인터를 통해 삭제를 포착하지 않습니다.

이 도구가 있습니까? 아니면 아예 눕혀 두는 것이 유용할까요?

편집 : 일반적으로 C ++ 프로그램이 정의되지 않은 동작을 실행할 수 있는지 여부를 정적으로 확인하는 것은 결정할 수 없다는 것을 알고 있습니다. 그러나 C ++ 특정 실행 이 정의되지 않은 동작을 생성 했는지 여부를 확인할 수 있습니다. 이를 수행하는 한 가지 방법은 각 지점에서 코드에 정의되지 않은 동작이 있는지 여부를 결정하는 사양에 설정된 정의에 따라 코드를 단계별로 실행하는 C ++ 인터프리터를 만드는 것입니다. 이것은 특정 프로그램 실행에서 발생하지 않는 정의되지 않은 동작을 감지하지 않지만 실제로 프로그램에서 나타나는 정의되지 않은 동작을 찾습니다. 이것은 일반적으로 여전히 결정할 수없는 경우에도 TM이 일부 입력을 수락하는지 여부를 결정하는 것이 Turing이 인식 할 수있는 방법과 관련이 있습니다.

감사!


Dead Code찾아서 Undefined Behavior Bugs 찾기의 John RegehrSTACK 이라는 도구를 지적 하고 사이트에서 인용했습니다 ( 강조 : ).

최적화-불안정 코드 (줄여서 불안정한 코드)는 새로운 종류의 소프트웨어 버그입니다. 프로그램에서 정의되지 않은 동작으로 인해 컴파일러 최적화에 의해 예기치 않게 제거되는 코드입니다. Linux 커널 및 Postgres 데이터베이스 서버를 포함한 많은 시스템에 불안정한 코드가 있습니다. 불안정한 코드의 결과는 잘못된 기능에서 누락 된 보안 검사에 이르기까지 다양합니다.

STACK은 C / C ++ 프로그램에서 불안정한 코드를 감지 하는 정적 검사기입니다 . 널리 사용되는 시스템에 STACK을 적용하면 개발자 가 확인 하고 수정 한 160 개의 새로운 버그가 발견되었습니다 .

또한 C ++ 11 에서 constexpr 변수 및 함수 의 경우 정의되지 않은 동작 은 컴파일 타임에 포착되어야합니다 .

gcc ubsan 도 있습니다 .

최근 GCC (버전 4.9)는 C 및 C ++ 언어 용 런타임 검사기 인 Undefined Behavior Sanitizer (ubsan)를 얻었습니다. ubsan으로 프로그램을 확인하려면 프로그램을 -fsanitize = undefined 옵션으로 컴파일하고 연결하십시오. 그러한 계측 된 바이너리는 실행되어야합니다. ubsan이 문제를 감지하면 "runtime error :"메시지를 출력하고 대부분의 경우 프로그램을 계속 실행합니다.

연타 정적 분석 하는 포함 많은 검사 정의 행동한다. 예를 들어 clangs -fsanitize 는 다음을 포함합니다 -fsanitize=undefined.

-fsanitize = undefined : 빠르고 호환 가능한 정의되지 않은 동작 검사기. 런타임 비용이 적고 주소 공간 레이아웃 또는 ABI에 영향을주지 않는 정의되지 않은 동작 검사를 활성화합니다. 여기에는 unsigned-integer-overflow를 제외하고 아래 나열된 모든 검사가 포함됩니다.

과에 대한 C 우리는 그의 글을 볼 수 있습니다 그것은 착취 정의되지 않은 동작에 대한 심각한을 할 시간입니다 말합니다 :

[..] 개인적으로는 KCC와 Frama-C라는 최고의 동적 정의되지 않은 동작 검사기를 통해 GCC 또는 LLVM을 습득하는 데 필요한 검프 션이 없다는 것을 고백합니다 . [...]

다음은 kcc에 대한 링크입니다 .

[...] 정의되지 않은 프로그램 (또는 의미가 누락 된 프로그램)을 실행하려고하면 프로그램이 중단됩니다. 메시지는 고정 된 위치를 알려주고 그 이유에 대한 힌트를 제공 할 수 있습니다. 출력을 해독하는 데 도움이 필요하거나 프로그램이 정의되지 않은 이유를 이해하는 데 도움이 필요하면 .kdump 파일을 보내주십시오. [...]

그리고 여기있다 FRAMA-C 링크 , 문서 는 C 인터프리터 등 FRAMA-C 제의 사용은 설명 및 부록 문서이다.


이것은 대단한 질문이지만 일반적으로 불가능하거나 적어도 매우 어려울 수 있다고 생각하는 이유에 대한 아이디어를 제공하겠습니다.

아마도 그러한 구현은 거의 C ++ 인터프리터 이거나 적어도 Lisp 또는 Java와 같은 것에 대한 컴파일러 일 것입니다. 배열 외부에서 산술을 수행하거나 이미 해제 된 항목을 역 참조하지 않도록하기 위해 각 포인터에 대한 추가 데이터를 유지해야합니다.

이제 다음 코드를 고려하십시오.

int *p = new int;
delete p;
int *q = new int;

if (p == q)
    *p = 17;

는 IS *p = 17정의되지 않은 동작은? 한편으로는 해제 된 p후에 역 참조 합니다. 반면에 역 참조 q괜찮고 p == q...

그러나 그것은 실제로 요점이 아닙니다. 요점은 if평가가 true로 평가 되는지 여부는 구현마다 다를 수있는 힙 구현의 세부 사항에 달려 있다는 것입니다. 따라서 *p = 17실제 정의되지 않은 동작으로 교체 하면 일반 컴파일러에서는 잘 작동하지만 가상의 "UB 검출기"에서는 잘 실행되는 프로그램이 있습니다. (일반적인 C ++ 구현은 LIFO가없는 목록을 사용하므로 포인터가 같을 가능성이 높습니다. 가상의 "UB 감지기"는 사용 후 사용 문제를 감지하기 위해 가비지 수집 언어처럼 작동 할 수 있습니다.)

다시 말해, 구현 정의 된 동작 의 존재는 모든 프로그램에서 작동하는 "UB 검출기"를 작성하는 것을 불가능하게 만듭니다.

즉, "uber-strict C ++ 컴파일러"를 만드는 프로젝트는 매우 흥미로울 것입니다. 시작하려면 알려주세요. :-)


사용 g++

-Wall -Werror -pedantic-error

(바람직하게는 적절한 -std논쟁과 함께) 꽤 많은 UB 사례를 선택합니다.


물건 -Wall을 얻을은 다음과 같습니다 :

-pedantic
엄격한 ISO C 및 ISO C ++에서 요구하는 모든 경고를 발행합니다. 금지 된 확장을 사용하는 모든 프로그램과 ISO C 및 ISO C ++를 따르지 않는 일부 다른 프로그램을 거부합니다. ISO C의 경우 사용 된 -std 옵션에 지정된 ISO C 표준 버전을 따릅니다.

-Winit-self (C, C ++, Objective-C 및 Objective-C ++ 만 해당)
자체적으로 초기화되는 초기화되지 않은 변수에 대해 경고합니다. 이 옵션은 -Wuninitialized 옵션과 함께 만 사용할 수 있으며 차례로 -O1 이상에서만 작동합니다.

-Wuninitialized
먼저 초기화하지 않고 자동 변수를 사용하거나 "setjmp"호출에 의해 변수가 제거 될 수있는 경우 경고합니다.

지정자 printfscanf패밀리 함수 로 수행 할 수있는 다양한 허용되지 않는 작업 .


Clang에는 다양한 형태의 정의되지 않은 동작을 포착 하는 일련의 새니 타이 저가 있습니다. 그들의 궁극적 인 목표는 모든 C ++ 핵심 언어의 정의되지 않은 동작을 포착 할 수 있도록하는 것이지만, 정의되지 않은 동작의 몇 가지 까다로운 형태에 대한 검사는 현재 누락되어 있습니다.

For a decent set of sanitizers, try:

clang++ -fsanitize=undefined,address

-fsanitize=address checks for use of bad pointers (not pointing to valid memory), and -fsanitize=undefined enables a set of lightweight UB checks (integer overflow, bad shifts, misaligned pointers, ...).

-fsanitize=memory (for detecting uninitialized memory reads) and -fsanitize=thread (for detecting data races) are also useful, but neither of these can be combined with -fsanitize=address nor with each other because all three have an invasive impact on the program's address space.


You might want to read about SAFECode.

This is a research project from the University of Illinois, the goal is stated on the front page (linked above):

The purpose of the SAFECode project is to enable program safety without garbage collection and with minimal run-time checks using static analysis when possible and run-time checks when necessary. SAFECode defines a code representation with minimal semantic restrictions designed to enable static enforcement of safety, using aggressive compiler techniques developed in this project.

What is really interesting to me is the elimination of the runtime checks whenever the program can be proved to be correct statically, for example:

int array[N];
for (i = 0; i != N; ++i) { array[i] = 0; }

Should not incur any more overhead than the regular version.

In a lighter fashion, Clang has some guarantees about undefined behavior too as far as I recall, but I cannot get my hands on it...


The clang compiler can detect some undefined behaviors and warn against them. Probably not as complete as you want, but it's definitely a good start.


Unfortunately I'm not aware of any such tool. Typically UB is defined as such precisely because it would be hard or impossible for a compiler to diagnose it in all cases.

In fact your best tool is probably compiler warnings: They often warn about UB type items (for example, non-virtual destructor in base classes, abusing the strict-aliasing rules, etc).

Code review can also help catch cases where UB is relied upon.

Then you have to rely on valgrind to capture the remaining cases.


Just as a side observation, according to the theory of computability, you cannot have a program that detects all possible undefined behaviours.

You can only have tools that use heuristics and detect some particular cases that follow certain patterns. Or you can in certain cases prove that a program behaves as you want. But you cannot detect undefined behaviour in general.

Edit

If a program does not terminate (hangs, loops forever) on a given input, then its output is undefined.

If you agree on this definition, then determining whether a program terminates is the well-known "Halting Problem", which has been proven to be undecidable, i.e. there exists no program (Turing Machine, C program, C++ program, Pascal program, in whatever language) that can solve this problem in general.

Simply put: there exists no program P that can take as input any program Q and input data I and print as output TRUE if Q(I) terminates, or else print FALSE if Q(I) does not terminate.

For more information you can look at http://en.wikipedia.org/wiki/Halting_problem.


Undefined behaviour is undefined. The best you can do is conform to the standard pedantically, as others have suggested, however, you can not test for what is undefined, because you don't know what it is. If you knew what it was and standards specified it, it would not be undefined.

However, if you for some reason, do actually rely on what the standard says is undefined, and it results in a particular result, then you may choose to define it, and write some unit tests to confirm that for your particular build, it is defined. It is much better, however, to simply avoid undefined behaviour whenever possible.


Take a look at PCLint its pretty decent at detecting a lot of bad things in C++.

Here's a subset of what it catches

참고URL : https://stackoverflow.com/questions/7237963/a-c-implementation-that-detects-undefined-behavior

반응형