Повторный вход или нет с этим кодом NetBSD

Я изучаю "чтение кода", читая фрагменты исходного кода NetBSD.
(для тех, кто заинтересован, я читаю )

И я нашел эту функцию:

/* convert IP address to a string, but not into a single buffer
*/
char *
naddr_ntoa(naddr a)
{
#define NUM_BUFS 4
    static int bufno;
    static struct {
    char str[16];   /* xxx.xxx.xxx.xxx\0 */
    } bufs[NUM_BUFS];
    char *s;
    struct in_addr addr;

    addr.s_addr = a;
    strlcpy(bufs[bufno].str, inet_ntoa(addr), sizeof(bufs[bufno].str));
    s = bufs[bufno].str;
    bufno = (bufno+1) % NUM_BUFS;
    return s;
#undef NUM_BUFS
}

Он вводит 4 разных временных буфера, чтобы обернуть функцию inet_ntoa, так как inet_ntoa не является реентерабельным.
Но мне кажется, что эта функция naddr_ntoa также не повторяется:
статическая переменная bufno может управляться другими, поэтому временные буферы здесь работают не так, как ожидалось.

Так это потенциальная ошибка?

2 ответа

Да, это потенциальная ошибка. Если вы хотите подобную функцию, которая, скорее всего, реентерабельна, вы можете использовать, например, inet_ntop (который, кстати, также обрабатывает IPv6).

Этот код исходит от src/sbin/routed/trace.c и это не обычная библиотечная процедура, а просто пользовательский хак, используемый только в перенаправленной программе. addrname() Функция в том же файле использует один и тот же прием, по той же причине. Это даже не код NetBSD сам по себе, а скорее исходный код от SGI, который поддерживается Верноном Шрайвером (см. The Routed Page).

Это просто быстрый взлом, чтобы разрешить использование нескольких вызовов в одном выражении, например, когда результаты используются в одном вызове printf(): Например:

printf("addr1->%s, addr2->%s, addr3->%s, addr4->%s\n",
       naddr_ntoa(addr1), naddr_ntoa(addr2), naddr_ntoa(addr3), naddr_ntoa(addr4));

Есть несколько примеров аналогичного использования в перенаправленных исходных файлах (if.c, input.c, rdisc.c).

В этом коде нет ошибки. Направленная программа не является многопоточной. Reentrancy не рассматривается вообще в этом хаке. Этот трюк был разработан специально для очень конкретной цели, которая не имеет ничего общего с повторным входом. Автор (ы) чтения кода неправильно связывает этот трюк с повторным входом.

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

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

Другие вопросы по тегам