Как получить предупреждение об указателях на локальные переменные вне области видимости - PullRequest
0 голосов
/ 02 октября 2018

Рассмотрим следующий код:

#include <stdio.h>

void badidea(int**);

int main(void) {
        int* p;
        badidea(&p);
        printf("%d\n", *p); /* undefined behavior happens here: p points to x from badidea, which is now out of scope */
        return 0;
}

void badidea(int** p) {
        int x = 5;
        *p = &x;
}

Предполагается, что он будет печатать 5, но на самом деле он вызывает неопределенное поведение из-за разыменования указателя на локальный вне области видимости.переменная в main.Как я могу найти экземпляры этой проблемы в базе кода?Вот что я пробовал до сих пор:

  • Компиляция с gcc -Wall -Wextra -pedantic
  • Компиляция с clang -Weverything
  • Запуск с компиляцией с clang -fsanitize=undefined
  • Работает под valgrind

Ни одно из вышеперечисленных предупреждений не выдвигалось.

Ответы [ 3 ]

0 голосов
/ 02 октября 2018

Сначала компиляция с GCC 7.2 и без -fsanitize=address и , а затем работа в Valgrind приводит к следующему:

==25751== Conditional jump or move depends on uninitialised value(s)
==25751==    at 0x4E988DA: vfprintf (vfprintf.c:1642)
==25751==    by 0x4EA0F25: printf (printf.c:33)
==25751==    by 0x1086E5: main (in ./a.out)

, за которым следуют другие предупреждения.

0 голосов
/ 02 октября 2018

Наш CheckPointer инструмент может обнаружить это с помощью динамического анализа, наряду с множеством других ошибок доступа к памяти.Инструмент отслеживает все выделения, доступы и назначения, включающие явные или неявные указатели, и жалуется в самый ранний момент, когда такой доступ является недопустимым или неопределенным.

Сохранение примера кода OP как "глючного".c ", и запуск CheckPointer производит следующий вывод (некоторые строки удалены по педагогическим причинам):

C~GCC4 CheckPointer Version 1.2.1001
Copyright (C) 2011-2016 Semantic Designs, Inc; All Rights Reserved; SD Confidential Powered by DMS (R) Software Reengineering Toolkit
Parsing source file "E:/DMS/Domains/C/GCC4/Tools/CheckPointer/Example/Source/buggy.c" using encoding CP-1252 +CRLF $^J $^M $^e -1 +8 ...
  Grouping top level declarations ...
  Creating object meta data initializers ...
  Normalizing syntax tree ...
  Instrumenting syntax tree ...
  Ungrouping top level declarations ...
Writing target file "E:/DMS/Domains/C/GCC4/Tools/CheckPointer/Example/Target/buggy.c" using encoding CP-1252 +CRLF $^J $^M $^e -1 +8 ...
*** Compiling sources with memory access checking code gcc.exe -I"e:\DMS\Domains\C\GCC4\Tools\CheckPointer" -I.\Target -obuggy.exe Target\buggy.c Target\check-pointer-data-initializers.c "e:\DMS\Domains\C\GCC4\Tools\CheckPointer\check-pointer.c" "e:\DMS\Domains\C\GCC4\Tools\CheckPointer\check-pointer-splay-tree.c" "e:\DMS\Domains\C\GCC4\Tools\CheckPointer\check-pointer-wrappers.c"
*** Executing instrumented application
*** Error: CWE-465 Pointer Issue (subcategory CWE-476, CWE-587, CWE-824, or CWE-825)
       Dereference of dangling pointer.
in function: main, line: 8, file E:/DMS/Domains/C/GCC4/Tools/CheckPointer/Example/Source/buggy.c

Конкретный тип ошибки сообщается с использованием кодов, определенных стандартом Common Weakness Enumeration .

NIST предлагает тест "пыток" для ошибок Java и C, который называется Juliet .CheckPointer обнаружил 13257 ожидаемых ошибок доступа к памяти из 14 195 тестовых примеров Juliet, относящихся к языку C.908 тестовых случаев не были диагностированы, но они включают в себя те, которые содержат неопределенное поведение, не связанное с ошибками использования указателя (которые CheckPointer не предназначен для обнаружения), или ошибками использования указателя, которые не были обнаружены при фактическом выполнении (например, неинициализированная переменная содержала 0 вфактическое исполнение).[Мы изменили некоторые из этих примеров, чтобы фактическое выполнение не содержало 0 для таких переменных, а затем CheckPointer выдал сообщение об ошибке, как и ожидалось.]

CheckPointer работает с GCC и MSVisualStudio.

==============================================

@ nm сделалколичество комментариев к различным ответам в этой теме.Он выпустил своего рода задачу, в которой продемонстрировал, что valgrind не может найти ошибку в следующем коде, похожую на OP, но более глубоко вложенную:

#include <stdio.h>

void badidea(int**);
void worseidea(int**);

int main(void) {
    int* p;
    badidea(&p);
//        printf("%d\n", *p); /* undefined behavior happens here: p points to x from badidea, which is now out of scope */
    worseidea(&p);
    return 0;
}

void worseidea(int **p) {
    int x = 42;
    printf("%d %d\n", **p, x); /* undefined behavior happens here: p points to x from badidea, which is now out of scope */
}

void badidea(int** p) {
    int x = 5;
    *p = &x;
}

Вот прогон Checkpointer, который диагностирует указательпроблема в коде nm:

C~GCC4 CheckPointer Version 1.2.1001
Copyright (C) 2011-2016 Semantic Designs, Inc; All Rights Reserved; SD Confidential
...
Parsing source file "C:/Users/idbaxter/AppData/Local/Temp/DMS/Domains/C/GCC4/Tools/CheckPointer/Example/Source/buggy.c" using encoding CP-1252 +CRLF $^J $^M $^e -1 +8 ...
  ...
Writing target file "C:/Users/idbaxter/AppData/Local/Temp/DMS/Domains/C/GCC4/Tools/CheckPointer/Example/Target/buggy.c" using encoding CP-1252 +CRLF $^J $^M $^e -1 +8 ...
*** Compiling sources with memory access checking code
gcc.exe -I"c:\DMS\Domains\C\GCC4\Tools\CheckPointer" -I.\Target -obuggy.exe Target\buggy.c Target\check-pointer-data-initializers.c "c:\DMS\Domains\C\GCC4\Tools\CheckPointer\check-
pointer.c" "c:\DMS\Domains\C\GCC4\Tools\CheckPointer\check-pointer-splay-tree.c" "c:\DMS\Domains\C\GCC4\Tools\CheckPointer\check-pointer-wrappers.c"
*** Executing instrumented application
*** Error: CWE-465 Pointer Issue (subcategory CWE-476, CWE-587, CWE-824, or CWE-825)
       Dereference of dangling pointer.
in function: worseidea, line: 16, file C:/Users/idbaxter/AppData/Local/Temp/DMS/Domains/C/GCC4/Tools/CheckPointer/Example/Source/buggy.c
called in function: main, line: 10, file: C:/Users/idbaxter/AppData/Local/Temp/DMS/Domains/C/GCC4/Tools/CheckPointer/Example/Source/buggy.c
0 голосов
/ 02 октября 2018

Я не думаю, что такой механизм существует в языке Си, в конце указатели - это просто переменные, содержащие адреса.Когда вы задаете им тип, он просто сообщает компилятору, какая переменная находится в адресном пространстве, указанном указателем. Таким образом, в теории указатель может хранить любые значения адреса, если он находится в определенном адресном пространстве.

И на самом деле это то, что делает язык Си действительно мощным.Особенно в механизмах передачи данных, потому что вы можете отправлять данные любого типа, даже определяемые пользователем структуры и т. Д., В любом порядке и легко получать / преобразовывать их на другом конце, не обращая внимания на endiannes и тому подобное.

Хотя в вашем случаенадеюсь, если вы знаете размер стека вашей программы и начальный адрес, вы можете проверить, находится ли содержимое адреса, на которое указывает указатель, в области, зарезервированной для стека, или нет.Таким образом, зная, указываете ли вы на локальную переменную или нет.

+ Если вам нужно указать на локальную переменную, вы можете определить ее как статическую, которая помещает переменную вне стека в ОЗУ.(Вы, вероятно, знаете это, но знаете, что некоторые не могут.)

...