как я могу «защитить от сбоев» функцию обратного вызова, написанную на C, если передан неправильный тип параметра

#c #pointers #types #signals

#c #указатели #типы #сигналы

Вопрос:

У меня есть функция обратного вызова, написанная на C, которая выполняется на сервере и ДОЛЖНА быть защищена от сбоев. То есть, если ожидается целое число и передается указатель на символьную строку, я должен определить это внутри функции и предотвратить ошибки сегментации при попытке сделать что-то недопустимое для неправильного типа параметра.

Прототип функции:

 void callback_function(parameter_type a, const b);
  

и ‘a’ должен сообщить мне через enum, является ли ‘b’ целым числом или указателем на символьную строку.
Если программист вызывающей функции допускает ошибку и сообщает мне в ‘a’, что ‘b’ является целым числом, а ‘b’ на самом деле является указателем на символьную строку, то как мне определить это без сбоя кода функции обратного вызова. Это выполняется на сервере и должно продолжаться, если вызывающая функция допустила ошибку.

Код должен быть на C и быть переносимым, чтобы расширения библиотеки C были невозможны. Компилятор: gcc v4.8.2 Размер целого числа на платформе равен 4, как и длина символьного указателя. Целое число может иметь то же числовое значение, что и символьный указатель, и наоборот.

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

Если я напишу обработчик сигнала для обработки ошибки, как мне теперь «очистить» сигнал и возобновить выполнение в нормальном месте?

Я упоминал, что ‘b’ — это объединение, определенное как:

 union param_2 {
  char * c;
  int i;
} param_to_be_passed;
  

Я думаю, что это все.

Спасибо за ваши ответы.

Комментарии:

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

Ответ №1:

Это невозможно.

Невозможно «посмотреть» на указатель at и определить, допустимо ли отменить ссылку, за исключением NULL -проверки, конечно.

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

Комментарии:

1. Для этого есть несколько хаков POSIX. Я думаю, что я видел вызов write с указателем и размером 0 или, может быть, размером 1. Ядро проверит ячейку памяти на доступ для чтения и возврата EFAULT .

Ответ №2:

Вы ищете хак.

Какое бы предложение ни поступало, не используйте такие вещи в производстве.

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

Ответ №3:

Если вы пишете код для встроенного устройства, вы ожидаете, что все переменные будут находиться в оперативной памяти. Например, у вас может быть 128 КБ оперативной памяти от адресов 0x20000000 до 0x20020000 . Если вам был передан указатель на адрес памяти без этого диапазона, в отношении c , это был бы еще один способ определить, что что-то не так, в дополнение к проверке нулевого адреса.

 if((a == STRING) amp;amp; ((b.c == NULL) || (b.c < 0x20000000) || (b.c > 0x20020000)))
    return ERROR;
  

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

Ответ №4:

Если вызывающий абонент говорит, a что результат есть int , большого риска сбоя нет, потому что:

  • в вашем случае оба типа имеют одинаковую длину (имейте в виду, что это НЕ ГАРАНТИРУЕТ ПЕРЕНОСИМОСТЬ!)

  • В стандартах C говорится (ISO — раздел.6.3.2.3): » Любой тип указателя может быть преобразован в целочисленный тип. За исключением случаев, указанных ранее, результат определяется реализацией. Если результат не может быть представлен в целочисленном типе, поведение не определено.

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

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

Если вызывающий абонент говорит в «a», что результатом является указатель, но предоставляет int, гораздо сложнее избежать сбоя переносимым способом.

  • Стандартный ISO гласит: целое число может быть преобразовано в любой тип указателя. За исключением случаев, указанных ранее, результат определяется реализацией, может быть неправильно выровнен, может не указывать на объект ссылочного типа и может быть представлением ловушки.

  • На практике большинство этих ошибок попадают в исключения доступа к памяти на очень низком системном уровне. Поведение определяется реализацией, переносимого способа сделать это нет.

Ответ №5:

ПРИМЕЧАНИЕ: на самом деле это не попытка сделать функцию «защищенной от сбоев», потому что я подозреваю, что это невозможно.

Если вам разрешено изменять API, одним из вариантов может быть объединение объединения только с использованием api для доступа к типу.

 typedef enum Type { STRING, INT } Type;
typedef struct StringOrInt {
    Type type;
    union { int i; char* s } value;
} StringOrInt;

void soi_set_int(StringOrInt* v, int i) {
     v->type = INT;
     v->value.i = i;
}

void soi_set_string(StringOrInt* v, char* s) {
     v->type = STRING;
     v->value.s = s;
}

Type soi_get_type(StringOrInt cosnt* v) {
     return v->type;
}

int soi_get_int(StringOrInt const* v) {
     assert(v->type == INT);
     return v->value.i;
}

char* soi_get_string(StringOrInt const* v) {
     assert(v->type == STRING);
     return v->value.s;
}
  

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

Ответ №6:

Проверка типа во время выполнения на C фактически невозможна.

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

Единственное предложение, которое я могу сделать, это создать два отдельных обратных вызова, один из которых принимает an int , а другой a char * , и возложить на компилятор выполнение проверки типов во время компиляции.