Разве нельзя сделать приложение на C ++ "Crash Proof"? - PullRequest
6 голосов
/ 23 декабря 2010

Скажем, у нас есть CK в C ++, который принимает некоторые двоичные данные (например, изображение) и что-то делает. Разве нельзя сделать этот SDK "защищенным от сбоев"? Под падением я в первую очередь имею в виду принудительное завершение ОС при нарушении доступа к памяти из-за неверного ввода, переданного пользователем (например, из-за ненадлежащим образом коротких ненужных данных).

У меня нет опыта работы с C ++, но когда я погуглил, я нашел несколько средств, которые звучали как решение (использовать вектор вместо массива, настроить компилятор так, чтобы выполнялась автоматическая проверка границ и т. Д.).

Когда я представил это разработчику, он сказал, что это все еще невозможно ... Не то, чтобы я ему не поверил, но если так, то как язык, как Java, справляется с этим? Я думал, что JVM выполняет проверку границ. Если так, то почему нельзя делать то же самое в C ++ вручную?

UPDATE
Под «доказательством сбоя» я не подразумеваю, что приложение не завершается. Я имею в виду, что он не должен внезапно завершаться без информации о том, что произошло (я имею в виду, что он сбросит ядро ​​и т. Д., Но нельзя ли вывести сообщение типа «Аргумент x был недействителен» и т. Д.?)

Ответы [ 8 ]

6 голосов
/ 23 декабря 2010

Вы можете проверить границы массива в C ++, std::vector::at делает это автоматически.

Это не делает ваше приложение защищенным от сбоев, вам все же разрешено преднамеренно стрелять себе в ногу, но ничто в C ++ не заставляет вас нажимать на курок.

4 голосов
/ 23 декабря 2010

Под падением я в первую очередь имею в виду принудительное завершение операционной системой при нарушении доступа к памяти из-за неверного ввода, переданного пользователем (например, ненормально коротких ненужных данных).

Это то, что обычно происходит. Если вы обращаетесь к какой-либо недопустимой памяти, обычно ОС прерывает вашу программу.

Однако вопрос, что является недопустимой памятью ... Вы можете свободно заполнять мусором всю память в куче и стеке, и это допустимо с точки зрения ОС, это не будет допустимо с вашей точки зрения, так как вы создали мусор .

В основном - вам нужно тщательно проверить входные данные и ретранслировать их. Ни одна ОС не сделает это за вас.

Если вы тщательно проверяете свои входные данные, вы, скорее всего, с ними справитесь.

4 голосов
/ 23 декабря 2010

Нет.Даже если предположить, что ваш код не содержит ошибок.С одной стороны, я просмотрел множество автоматически представленных отчетов о сбоях и могу заверить вас, что качество оборудования намного ниже того, чего ожидает большинство разработчиков.Битовые перевороты слишком распространены на обычных машинах и вызывают случайные AV.И даже если вы готовы обработать нарушения прав доступа, существуют определенные исключения, из-за которых у ОС нет иного выбора, кроме как прекратить процесс, например, невозможность зафиксировать страницу защиты стека .

3 голосов
/ 23 декабря 2010

Я в первую очередь имею в виду принудительное завершение ОС при доступе к памяти нарушение, из-за неправильного ввода передано пользователем

Не уверен, кто "пользователь".

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

То, что вы не можете легко сделать в C ++, это доказать , что ваша программа не будет аварийно завершать работу из-за неправильного ввода или работать еще хуже, создавая серьезные недостатки безопасности. Поэтому иногда [*] вы думаете, что ваш код защищен от любого ввода, но оказывается, что это не так. Ваш разработчик может иметь в виду это.

Если ваш код является функцией, которая принимает, например, указатель на данные изображения, то ничто не мешает вызывающей стороне передать вам недопустимое значение указателя:

char *image_data = malloc(1);
free(image_data);
image_processing_function(image_data);

Так что функция сама по себе не может быть «защищенной от сбоев», она требует, чтобы остальная часть программы не делала ничего, чтобы она вылетала. Ваш разработчик также может иметь это в виду, поэтому, возможно, вам следует попросить его уточнить.

Java решает эту конкретную проблему, делая невозможным создание недопустимой ссылки - вы не можете вручную освободить память в Java, поэтому, в частности, вы не можете сохранить ссылку на нее после этого. Он имеет дело с множеством других специфических проблем другими способами, так что ситуации, которые являются «неопределенным поведением» в C ++ и могут привести к сбою, будут делать что-то другое в Java (возможно, будет исключение).

[*] давайте посмотрим правде в глаза: на практике в крупных программных проектах "часто".

1 голос
/ 23 декабря 2010

Как правило, API-интерфейс C ++ не может быть защищен от сбоев , но для повышения его надежности можно использовать методы Вне моей головы (и ни в коем случае не исчерпывающий) для вашего конкретного примера:

  • Проверка работоспособности входных данных, где это возможно
  • Проверка ограничения буфера в коде обработки данных
  • Испытание краев и угловых корпусов
  • Нечеткое тестирование
  • Ввод проблемных входов в модульный тест для предотвращения регрессии
1 голос
/ 23 декабря 2010

Я думаю, что это тот случай, когда коды C ++ не являются управляемыми кодами.

Java, C # коды управляются, то есть они эффективно выполняются Интерпретатором, который может выполнять проверку границ и обнаруживать условия сбоя.

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

Суть в том, что сами коды C ++ не защищены от сбоев, но хороший дизайн и разработка могут сделать их такими.

0 голосов
/ 09 января 2011

Если «доказательство сбоя» означает только то, что вы хотите убедиться, что у вас достаточно информации для расследования сбоя после его возникновения, решение может быть простым. Большинство случаев, когда отладочная информация теряется во время сбоя, вызвана повреждением и / или потерей данных стека из-за недопустимой операции с памятью кодом, выполняющимся в одном из потоков. Если у вас есть несколько мест, где вы вызываете библиотеку или SDK, которым вы не доверяете, вы можете просто сохранить трассировку стека непосредственно перед выполнением вызова в эту библиотеку в некотором месте памяти, указанном глобальной переменной, которая будет включена в частичный или полный дамп памяти. генерируется системой при сбое вашего приложения. В Windows такая функциональность предоставляется API-интерфейсом CrtDbg. В Linux вы можете использовать API-интерфейс backtrace - просто найдите документ в show_stackframe (). Если вы потеряете информацию о вашем стеке, вы можете дать указание отладчику использовать это место в памяти как верхнюю часть стека после загрузки файла дампа. Ну, в конце концов, это не очень просто, но если вас преследуют дампы памяти без какой-либо подсказки, что может случиться, это может помочь. Другим приемом, часто используемым во встроенных приложениях, является буфер циклической памяти для детальной регистрации. Запись в буфер очень дешевая, поскольку она никогда не сохраняется, но вы можете понять, что происходит за миллисекунды до сбоя, посмотрев содержимое буфера в дампе памяти после сбоя.

0 голосов
/ 23 декабря 2010

На самом деле, использование проверки границ делает ваше приложение более вероятным сбоем !

Это хороший дизайн, потому что это означает, что если ваша программа работает, то вероятность того, что она будет работать, намного выше/ правильно /, вместо того, чтобы работать неправильно.

Тем не менее, строго говоря, данное приложение нельзя сделать «защищенным от сбоев», пока проблема остановки не будет решена.Удачи!

...