Какая была самая опасная ошибка программирования, которую вы допустили в C? - PullRequest
8 голосов
/ 12 ноября 2008

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

Ответы [ 26 ]

24 голосов
/ 12 ноября 2008
if (c = 1) // insert code here
20 голосов
/ 12 ноября 2008
if(a == true);
{
  //Do sth when it is true. But it is allways executed.
}

Редактировать : еще один вариант той же ошибки.

for(i=0; i<max_iterations;i++);
{
  //Do sth but unexpectedly only once
}
12 голосов
/ 12 ноября 2008

Это было давно, но некоторые вещи вы никогда не забудете; -).

  • забыть \0 в конце строки.
  • выделить n символов для строки из n символов.
  • забывая разрыв в операторе switch.
  • «креативное» использование макроса.
12 голосов
/ 12 ноября 2008

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

Префикс номера карты состоит из 6-значного BIN (идентификационного номера банка) и нескольких дополнительных цифр, которые банки используют по своему усмотрению, например, Банк имеет BIN для карты Visa Classic 456789 и резервирует 2 дополнительные цифры для обозначения субпродукта, например, 01 для студенческого билета, 02 для совместного бренда в местном универмаге и так далее. В этом случае префикс карты, который в основном является идентификатором продукта, становится длиной 8 цифр. Когда я кодировал эту часть, я решил, что 9 цифр «должно хватить всем». Я работал нормально 2 года, пока однажды банк не выпустил новые карточные продукты с префиксом из 10 цифр (понятия не имею, зачем им это нужно). Нетрудно представить, что произошло - маршрутизатор отключился, вся система остановилась, потому что она не может функционировать без транзакционного маршрутизатора, все банкоматы этого банка (один из крупнейших в стране) не функционировали в течение нескольких часов, пока проблема не была обнаружена и неподвижная.

Я не могу опубликовать код здесь, во-первых, потому что у меня его нет, а во-вторых, он защищен авторским правом компании, но нетрудно представить strcpy() без проверки размера целевого буфера.

Также как man strcpy говорит:

Если строка назначения strcpy () недостаточно велик (что есть, если программист был глуп или ленивый, и не смог проверить размер перед копированием) бывает. Переполнение фиксированной длины струны любимый взломщик техника.

Я был очень смущен. Это было хорошее время для совершения seppuku :)

Но я хорошо усвоил урок и не забудьте (обычно :)) проверить размер целевого буфера. Я бы не советовал изучать его трудным способом - просто выработайте привычку проверять целевой буфер перед strcpy() и strcat().

Редактировать: хорошее предложение от Healthcarel - используйте strncpy() вместо strcpy(). Это не добавляет конечный 0, но я обычно использую следующий макрос, чтобы обойти это:

#define STRNCPY(A,B,C) do {strncpy(A,B,C); A[C] = 0; } while (0)

6 голосов
/ 12 ноября 2008
for(int i = 0; i<10; ++i)
  //code here
  //code added later

Обратите внимание, что добавленный позже код отсутствует в цикле for.

5 голосов
/ 12 ноября 2008

Неинициализированные данные.

4 голосов
/ 12 ноября 2008

Самая опасная вещь, которую я когда-либо делал в C, пыталась написать код, который управлял моей собственной памятью. Фактически это означает, что самой опасной вещью, которую я когда-либо делал в C, было написание кода C . (Я слышал, что вы можете обойти это в наши дни. Бедро для здравомыслия. Используйте эти подходы, когда это уместно!)

  • Я не пишу алгоритмы подкачки - для меня это делают фанаты ОС.
  • Я не пишу схемы кэширования базы данных - для меня это делают фанаты баз данных.
  • Я не строю кэши процессора L2 - аппаратные фанаты делают это для меня.

И я не управляю памятью.

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

4 голосов
/ 12 ноября 2008

system () с некоторой пользовательской строкой в ​​аргументе. То же самое касается popen ().

Вместо этого используйте exec * ().

Конечно, это не уникально для C.

3 голосов
/ 12 ноября 2008

Я согласен с Пэтом Маком здесь (несмотря на его отрицательное голосование). Самое опасное, что вы можете сделать в C, - это просто использовать его для чего-то важного.

Например, разумный язык по умолчанию проверяет границы массивов и немедленно останавливает вашу программу (вызывает исключение или что-то в этом роде), если вы пытаетесь выйти за его пределы. Ада делает это. Ява делает это. Тонны других языков делают это. Не C. Есть целые индустрии хакерства, построенные вокруг этого недостатка в языке.

Один личный опыт с этим. Я работал с компанией, которая управляла сетью симуляторов полета, связанных вместе с отражающей (общей) аппаратурой памяти. У них была неприятная ошибка, которую они не могли отследить, поэтому два наших лучших инженера были отправлены туда, чтобы отследить их. Это заняло у них 2 месяца.

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

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

3 голосов
/ 12 ноября 2008

Я беру определение опасности как «мы можем отправить эту ошибку и обнаружить только спустя годы, когда уже поздно»:

char* c = malloc(...);
.
.
.
free(c);  
.
.
.
c[...] = ...; 

или

// char* s is an input string
char* c = malloc(strlen(s));
strcpy(c, s);

Но если вы пишете мультиплатформу (не ограничиваясь x86 / x64), это тоже замечательно:

char* c = ...;
int i = *((int*)c); // <-- alignment fault

И если ваш буфер получен из ненадежного источника .. в основном большая часть кода опасна.

Но, во всяком случае, в Си так легко выстрелить себе в ногу, что тема о выстреле в ноги может охватить тысячи страниц.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...