Как предотвратить scanf порождение переполнения буфера в C?

Вы не делаете ничего плохого. Первый объект в Вашем отслеживании стека (число) является кодом ошибки, возвращенным COM-объектом. Второй объект является описанием, связанным с кодом ошибки, который в этом случае является "Произошедшим Исключением". pywintypes.com_error уже назвал эквивалент win32api. FormatMessage (errCode) для Вас. Мы посмотрим на второе число за минуту.

Между прочим, можно использовать утилиту "Error Lookup", которая прибывает в Visual Studio (C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\ErrLook.exe) как быстрый стартовый стол для проверки кодов ошибок COM. Та утилита также называет FormatMessage для Вас и отображает результат. Не все коды ошибок будут работать с этим механизмом, но многие будут. Это обычно - моя первая остановка.

Обработка ошибок и сообщающий в COM немного грязна. Я попытаюсь дать Вам некоторый фон.

Все вызовы метода COM возвратят цифровой код, названный HRESULT, который может указать на успешность или неуспешность. Все формы сообщения об ошибке в сборке COM вдобавок ко всему

коды обычно выражаются в шестнадцатеричном числе, хотя иногда Вы будете рассматривать их как большие 32-разрядные числа, как в Вашем отслеживании стека. Существуют все виды предопределенных кодов возврата для общих результатов и проблем, или объект может возвратить пользовательские цифровые коды для специальных ситуаций. Например, значение 0 (названный S_OK) универсально не означает "Ошибки", и 0x80000002 является E_OUTOFMEMORY. Иногда коды HRESULT возвращаются объектом, иногда инфраструктурой COM.

COM-объект А может также принять решение предоставить намного более богатую информацию об ошибке путем реализации интерфейса под названием IErrorInfo. Когда объект реализует IErrorInfo, он может обеспечить все виды детали о том, что произошло, такие как подробное пользовательское сообщение об ошибке и даже название справочного файла, который описывает проблему. В VB6 и VBA. эти Err объект позволяет Вам получать доступ ко всей этой информации (Err.Description, и т.д.).

Более того, поздно связанные COM-объекты (которые используют механизм под названием Автоматизация COM или IDispatch) добавляют некоторые слои, которые должны быть очищены прочь для получения информации. Excel обычно управляют через позднее связывание.

Теперь позволяют нам посмотреть на Вашу ситуацию снова. Что Вы получаете, поскольку первое число является довольно универсальным кодом ошибки: DISP_E_EXCEPTION. Примечание: можно обычно выяснять официальное название HRESULT путем поиска с помощью Google числа, хотя иногда необходимо будет использовать шестнадцатеричную версию для нахождения чего-либо полезным.

Ошибки, которые начинаются с DISP_, являются кодами ошибок IDISPATCH. Ошибка свободно означает, что "Было COM-исключение, брошенное объектом", с большей информацией, упакованной в другом месте (хотя я не вполне знаю где; я должен буду искать его).

Из того, что я понимаю pywintypes.com_error, последнее число в Вашем сообщении является фактическим кодом ошибки, который был возвращен объектом во время исключения. Это - фактический цифровой код, что Вы вышли бы из VBA's Err.Number.

, К сожалению, что второй код-2146788248 (0x800A9C68) находится в диапазоне, зарезервированном для определенных пользовательским приложением сообщений об ошибках (в VBA: VbObjectError + someCustomErrorNumber), таким образом, нет никакого централизованного значения. То же число может означать совершенно разные вещи для различных программ.

В этом случае, мы достигли тупика:

код ошибки является "пользовательским", и приложение должно зарегистрировать, каково это, за исключением того, что Excel не делает. Кроме того, Excel (или фактический источник ошибки), кажется, не предоставляет больше информацию через IErrorInfo.

Excel известен (мне, по крайней мере) для загадочных кодов ошибок от автоматизации и неясных ситуаций, которые вызывают их. Это особенно так для ошибок, что можно было рассмотреть "ошибки времени проектирования" ("необходимо было знать лучше, чем вызов метода, который не существует в объекте"). Вместо хорошего "Не мог считать свойство Name", Вы добираетесь" Ошибка времени выполнения '1004': Приложение определенная или определенная объектом ошибка " (который я просто полученный путем попытки получить доступ к свойству Name на Диапазоне, от VBA в Excel). Это не очень полезно.

проблема не направляется на Python, или это - интерфейс к Excel. Сам Excel не объясняет, что произошло, даже к VBA.

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

Удачи!

75
задан Jonathan Leffler 26 October 2009 в 00:30
поделиться

5 ответов

В большинстве случаев комбинация fgets и sscanf выполняет свою работу. Другой вариант - написать собственный парсер, если ввод хорошо отформатирован. Также обратите внимание, что ваш второй пример требует небольшой модификации для безопасного использования:

#define LENGTH          42
#define str(x)          # x
#define xstr(x)         str(x)

/* ... */ 
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array); 

Вышеупомянутое отбрасывает входной поток вверх, но не включает символ новой строки ( \ n ). Вам нужно будет добавить getchar () , чтобы использовать это. Также проверьте, достигли ли вы конца потока:

if (!feof(stdin)) { ...

и все.

9
ответ дан 24 November 2019 в 11:42
поделиться

In their book The Practice of Programming (which is well worth reading), Kernighan and Pike discuss this problem, and they solve it by using snprintf() to create the string with the correct buffer size for passing to the scanf() family of functions. In effect:

int scanner(const char *data, char *buffer, size_t buflen)
{
    char format[32];
    if (buflen == 0)
        return 0;
    snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
    return sscanf(data, format, buffer);
}

Note, this still limits the input to the size provided as 'buffer'. If you need more space, then you have to do memory allocation, or use a non-standard library function that does the memory allocation for you.


Note that the POSIX 2008 (2013) version of the scanf() family of functions supports a format modifier m (an assignment-allocation character) for string inputs (%s, %c, %[). Instead of taking a char * argument, it takes a char ** argument, and it allocates the necessary space for the value it reads:

char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
    printf("String is: <<%s>>\n", buffer);
    free(buffer);
}

If the sscanf() function fails to satisfy all the conversion specifications, then all the memory it allocated for %ms-like conversions is freed before the function returns.

57
ответ дан 24 November 2019 в 11:42
поделиться

Если вы используете gcc, вы можете использовать спецификатор GNU-extension a , чтобы функция scanf () выделяла память для хранения ввода:

int main()
{
  char *str = NULL;

  scanf ("%as", &str);
  if (str) {
      printf("\"%s\"\n", str);
      free(str);
  }
  return 0;
}

Изменить: Как указал Джонатан, вам следует обратиться к справочным страницам scanf , поскольку спецификатор может быть другим (% m ), и вам может потребоваться включить определенные определения при компиляции.

30
ответ дан 24 November 2019 в 11:42
поделиться

Непосредственное использование scanf (3) и его вариантов создает ряд проблем. Обычно пользователи и неинтерактивные варианты использования определяются в терминах строк ввода. Редко можно встретить случай, когда, если не найдено достаточного количества объектов, проблему решит большее количество строк, но это режим по умолчанию для scanf. (Если пользователь не знал, что нужно ввести число в первой строке, вторая и третья строки, вероятно, не помогут.)

По крайней мере, если вы fgets (3) , вы знаете, сколько вводить строки, которые потребуются вашей программе, и у вас не будет переполнения буфера ...

s режим по умолчанию для scanf. (Если пользователь не знал, что нужно ввести число в первой строке, вторая и третья строки, вероятно, не помогут.)

По крайней мере, если вы fgets (3) , вы знаете, сколько вводить строки, которые потребуются вашей программе, и у вас не будет переполнения буфера ...

s режим по умолчанию для scanf. (Если пользователь не знал, что нужно ввести число в первой строке, вторая и третья строки, вероятно, не помогут.)

По крайней мере, если вы fgets (3) , вы знаете, сколько вводить строки, которые потребуются вашей программе, и у вас не будет переполнения буфера ...

4
ответ дан 24 November 2019 в 11:42
поделиться

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

Но это большая работа, поэтому большинство программистов на C просто отрубают ввод произвольной длины. Я полагаю, вы это уже знаете, но использование fgets () не позволит вам принимать произвольные объемы текста - вам все равно нужно будет установить лимит.

1
ответ дан 24 November 2019 в 11:42
поделиться
Другие вопросы по тегам:

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