Что самое опасное программировало ошибку, которую Вы сделали в C?

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

8
задан user7116 8 December 2008 в 00:32
поделиться

24 ответа

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

Префикс номера карты состоит из 6-разрядного МУСОРНОГО ВЕДРА (Идентификационный номер Банка) и дополнительные немного цифр, которые банки используют по собственному усмотрению, например, банк имеет МУСОРНОЕ ВЕДРО для карты Visa Classic 456789, и зарезервируйте 2 дополнительных цифры для указания на промежуточный результат, как 01 для карты студента, 02 для кобрендинговой карты с локальным универмагом и так далее. В этом префиксе карты случая, который является в основном идентификатором продукта, становится 8 цифрами долго. Когда я кодировал эту часть, я решил, что 9 цифр "должны быть достаточно для всех". Я работал хорошо в течение 2 лет, пока один дневной банк не делает новую карту продуктами с 10 цифрами длинный префикс (понятия не имейте, почему им был нужен он). Не слишком трудно для воображения, что произошло - маршрутизатор segfaulted, целая система остановленный, потому что это не может функционировать без маршрутизатора транзакции, все банкоматы того банка (один из самых больших в стране) стали не влияющими на эксплуатацию в течение нескольких часов, пока проблема не была найдена и решена.

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

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

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

Я был очень смущен. Это было хорошее время для фиксации сэппуку :)

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

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

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

12
ответ дан 5 December 2019 в 04:28
поделиться
if (importantvar = importantfunction() == VALID_CODE)

Это - когда я имел в виду это:

if ((important var = importantfunction()) == VALID_CODE)

Ведомый ко многим часам отладки проблем, когда я принял это, работал как последний.

0
ответ дан 5 December 2019 в 04:28
поделиться
#include <string>

Меня думающий C поддерживает строку исходно (использующий Metroworks codewarrior, приблизительно 8 лет назад).

Я сделал это для заключительного проекта приблизительно 15 000 строк кода. Я пользовался этой библиотекой, чтобы сделать все касающееся строк (добавление, разделение, и т.д.) Только, чтобы иметь TA, который не в состоянии скомпилировать любой бит моего присвоения (использующий GCC.)

Мало я узнавал, что metroworks создал их собственную строковую библиотеку. Я привел тот класс к сбою.

0
ответ дан 5 December 2019 в 04:28
поделиться

Я помню две грубых ошибки:

  1. возврат адреса автоматической переменной из функции, в которой это было создано;
  2. копирование строки к неинициализированному и освобожденному указателю для обугливания.
0
ответ дан 5 December 2019 в 04:28
поделиться

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

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

0
ответ дан 5 December 2019 в 04:28
поделиться

Упущение ограничений архитектуры и счастливо memcpy () луг в область I/O с отображенной памятью на микроконтроллере. Волшебный дым был выпущен от тестовой буровой установки.

1
ответ дан 5 December 2019 в 04:28
поделиться

Я имел дело с динамично выделенными 2D массивами и мной вместо свободного () 'строки луга N, я решил освободить столбцы M. Это было хорошо для меньших исходных данных, где N == M, но на больших исходных данных, я только свободный () 'd 50% того, что я выделил.

пожатие плеч

Век живи - век учись.

1
ответ дан 5 December 2019 в 04:28
поделиться

Будучи программистом Lisp, я привык располагать закрывающие фигурные скобки с отступом, как в:

(cond
    ((eq a foo)(bar ...
        ....
        ))
    )

и я нес это в программирование C:

if (a == foo){
    bar(...);
    ....
    }

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

1
ответ дан 5 December 2019 в 04:28
поделиться

Передача виртуального адреса к механизму DMA была худшей, не точно C связанный, но я предполагаю, что 99% связанного с DMA материала, записанного в C, таким образом, это - вид соответствия. Эта небольшая ошибка приводит к повреждению памяти, которое взяло меня 1,5 месяца для нахождения.

1
ответ дан 5 December 2019 в 04:28
поделиться

случай переключателя без повреждения.

1
ответ дан 5 December 2019 в 04:28
поделиться

Используя неограниченные строковые функции, такие как strcpy () или strcmp (), вместо безопасных версий как strncpy () и strncmp ().

1
ответ дан 5 December 2019 в 04:28
поделиться
while(a)
{ 
   // code - where 'a' never reaches 0 :( 
}
2
ответ дан 5 December 2019 в 04:28
поделиться

Когда указатель сначала выделяется, он не имеет пуанта.

Указатель является "неинициализированным"

Разыменовать операция на неверном указателе является серьезной ошибкой периода выполнения.

Если Вы будете удачливы, то разыменовать операция откажет или сразу остановится (Java ведет себя этот путь).

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

2
ответ дан 5 December 2019 в 04:28
поделиться

Я - в согласии с Кусочком Mac здесь (несмотря на его downvoting). Самая опасная вещь, которую можно сделать в C, состоит в том, чтобы просто использовать его для чего-то важного.

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

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

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

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

3
ответ дан 5 December 2019 в 04:28
поделиться

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

Используйте должностное лицо* () вместо этого.

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

4
ответ дан 5 December 2019 в 04:28
поделиться

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

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

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

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

3
ответ дан 5 December 2019 в 04:28
поделиться

Необходимо волноваться больше о небольших ошибках. Большие/захватывающие ошибки обычно документируются в книги (с причинами, почему они - плохие, альтернативные подходы и т.д.).

Это - мало ошибок дизайна/кодирования, которые добираются до Вас, потому что они имеют тенденцию складывать.

Таким образом, мой совет состоит в том, чтобы попытаться прочитать книги, которые Kernighan записал или создал в соавторстве ("Язык программирования C", "Практика Программирования" и т.д.), потому что они полны здравого смысла (характерный для опытных программистов C) совет и перечисляют принципы, которые очень полезны в предотвращении и маленькие и большие ошибки.

Они также перечисляют много потенциальных больших ошибок, таким образом, они отвечают на Ваш начальный вопрос.

3
ответ дан 5 December 2019 в 04:28
поделиться

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

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

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

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

4
ответ дан 5 December 2019 в 04:28
поделиться

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

5
ответ дан 5 December 2019 в 04:28
поделиться

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

  • забудьте \0 в конце строки.
  • выделите n символы для строки с n символами.
  • упущение перерыва в операторе переключения.
  • 'творческое' макро-использование.
12
ответ дан 5 December 2019 в 04:28
поделиться
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
}
20
ответ дан 5 December 2019 в 04:28
поделиться
for(int i = 0; i<10; ++i)
  //code here
  //code added later

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

6
ответ дан 5 December 2019 в 04:28
поделиться
if (c = 1) // insert code here
24
ответ дан 5 December 2019 в 04:28
поделиться

Две вещи приходят на ум. Тыс сначала была функцией во встроенном C (MCU), я пытался иметь некоторые ограничения на значение таймера как вводимый функция. таким образом, я записал

if(55000 < my_var < 65000)

Моя международная ассоциация развития должна была проверить как это:

if( (55000<my_var) < 65000)

Но это - equivilant или результат

if( (55000<my_var) || (my_var<65000))

и результат закончился, что, если тест был всегда верен.

Второй была ошибка указателя. (упрощенный представленный здесь)

get_data(BYTE **dataptr)
{ 
  ubyte* data = malloc(10);
  ... code ...
  *dataptr = &data[1];
}

 main()
 {
   BYTE *data
   get_data(&data);
   free(data);
 }

Таким образом приводя к потере 1 байта памяти в течение каждого раза get_data() функция была вызвана

2
ответ дан 5 December 2019 в 04:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: