В этой статье я хочу поговорить о проверке проекта ReOpenLDAP. Данный проект был реализован для решения проблем, возникших при работе OpenLDAP в инфраструктуре ПАО «МегаФон», крупнейшего мобильного оператора России.
Сейчас ReOpenLDAP успешно работает в филиалах МегаФона по всей России, поэтому было бы интересно проверить столь высоконагруженный проект с помощью статического анализатора PVS-Studio.
Введение
ReOpenLDAP , также известный как «TelcoLDAP», является ответвлением проекта OpenLDAP , созданный российскими разработчиками для использования в телекоммуникационной отрасли, с множеством исправленных ошибок и рабочей репликацией в мультимастерной топологии.ReOpenLDAP — это реализация сервера с открытым протоколом.
ЛДАП на языке Си.
ReOpenLDAP обеспечивает высокий уровень производительности:
- Более 50 тысяч изменений LDAP в секунду
- Более 100 тысяч LDAP-запросов в секунду
идти к , что значительно усложняет анализ, но даже несмотря на это был обнаружен ряд ошибок.
Пожалуйста, зарегистрируйтесь, чтобы протестировать бета-версию PVS-Studio для Linux.
Написание этой статьи стало возможным благодаря началу работ по созданию PVS-Studio для Linux. Именно на Linux мы тестировали проект ReOpenLDAP. Однако существует угроза, что версия для Linux прекратит свое существование еще до выхода.Причина – малый практический интерес.
Когда на форуме возникает какое-то обсуждение, то создается впечатление, что самая большая проблема продукта PVS-Studio — это отсутствие версии для Linux. Но когда дело дошло до сбора контактов людей, желающих поучаствовать в тестировании Бета-версии, оказалось, что их очень мало.
Примечание: о поиске энтузиастов мы писали в статье « PVS-Studio заявляет о своей любви к Linux ".
Хочу отметить: мы не беспокоимся о тестировании PVS-Studio. Почему-то некоторые решили, что мы всё это запланировали для того, чтобы программисты бесплатно работали у нас тестировщиками.
Конечно, это совсем не так: мы способны организовать тестирование самостоятельно.
Однако если откликаются лишь несколько человек, то, возможно, стоит замедлить или даже прекратить работу в этом направлении.
И, к сожалению, респондентов действительно было очень мало.
Поэтому у нашего единорога просьба ко всем Linux-программистам.
Просим вас пополнить ряды бета-тестеров PVS-Studio для Linux. Это покажет, что к инструменту имеется практический интерес.
Напишу еще раз о том, как можно пополнить ряды энтузиастов.
Если вы хотите помочь нам проверить работу PVS-Studio для Linux, напишите нам.
Чтобы упростить обработку писем, в теме письма укажите, пожалуйста, строку «PVS-Studio for Linux, Beta».
Отправляйте письма по адресу: [email protected] .
Пожалуйста, пишите письма с корпоративных почтовых ящиков и кратко представьтесь.
Мы будем благодарны всем, кто откликнется, но в первую очередь учтем пожелания и рекомендации тех людей, которые со временем потенциально могут стать нашими клиентами.
Также прошу Вас в письме дать ответы на следующие вопросы:
- Под какой операционной системой вы планируете запускать анализатор?
- Какую среду разработки вы используете?
- Какой компилятор используется для сборки проекта?
- Какую систему сборки вы используете?
Заранее спасибо всем.
Результаты теста
Ошибка приоритета операции
Предупреждение PVS-Studio: В593 Рассмотрим выражение вида «A = B == C».Выражение рассчитывается следующим образом: «A = (B == C)».
mdb_dump.c 150
static int dumpit(.
)
{
.
while ((rc = mdb_cursor_get(.
) == MDB_SUCCESS)) {
.
}
.
}
Автор допустил ошибку с размещением закрывающей скобки в условии цикла пока , что привело к ошибке в приоритете операций.
В результате сначала выполняется сравнение, а затем результат этого сравнения записывается в переменную RC .
Этот код следует исправить следующим образом: while ((rc = mdb_cursor_get(.
)) == MDB_SUCCESS) { .
}
Работа с нулевым указателем
Предупреждение PVS-Studio: В595 Указатель «key» использовался до того, как он был проверен на соответствие nullptr. Проверить строки: 1324, 1327. mdb.c 1324 char *
mdb_dkey(MDB_val *key, char *buf)
{
.
unsigned char *c = key->mv_data; // <=
.
if (!key) // <=
return "";
.
}
В блоке если указатель проверяется ключ на НУЛЕВОЙ Поэтому автор допускает возможность того, что этот указатель может быть равен нулю, но указатель уже используется выше без проверки.
Чтобы избежать ошибки, следует проверить значение указателя ключ прежде чем использовать его.
Аналогичное предупреждение:
- V595 Указатель «ключ» использовался до его проверки на соответствие nullptr. Проверьте строки: 7282, 7291. mdb.c 7282
Подозрительный тернарный оператор
Предупреждение PVS-Studio: В583 Оператор '?:', независимо от его условного выражения, всегда возвращает одно и то же значение: «vlvResult».общий.
c 2119 static int
print_vlv(.
) { .
tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, ldif ? "vlvResult" : "vlvResult", buf, rc ); // <= } .
} Использование указанного тернарного оператора вернет одно и то же значение независимо от условия.
Судя по подобным местам в исходнике, есть опечатка, и этот код следует переписать так: .
tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, ldif ? "vlvResult: " : "vlvResult", buf, rc ); .
Возможна опечатка в названии поля.
Предупреждение PVS-Studio: В571 Повторяющаяся проверка.
Условие if (s-> state.r == 0) уже проверено в строке 147. rurwl.c 148 void rurw_r_unlock(.
) { .
if (s->state.r == 0) { // <= if (s->state.r == 0) // <= s->thr = 0; p->rurw_readers -= 1; } .
} Здесь одно и то же условие проверяется дважды.
Если обратить внимание на подобные места в исходном коде, например: void rurw_w_unlock(.
) { .
if (s->state.w == 0) { if (s->state.r == 0) s->thr = 0; p->rurw_writer = 0; } .
} то можно предположить, что одно из условий подразумевало проверку s-> state.w == 0. Однако это всего лишь предположение, но в любом случае разработчикам следует проверить этот участок кода и либо скорректировать одно из условий, либо удалить дубликат чека.
Похожее место:
- V571 Повторяющаяся проверка.
Условие 'def-> mrd_usage & 0x0100U' уже проверено в строке 319. mr.c 322
Перезапись параметра
Предупреждение PVS-Studio: В763 Параметр «rc» всегда перезаписывается в теле функции перед использованием.
tls_o.c 426 static char *
tlso_session_errmsg(.
, int rc, .
) { char err[256] = ""; const char *certerr=NULL; tlso_session *s = (tlso_session *)sess; rc = ERR_peek_error(); // <= .
} В этой функции значение параметра RC всегда перезаписывается перед использованием параметра.
Наверное стоит удалить RC из списка параметров.
Неверный спецификатор форматирования
Предупреждение PVS-Studio: В576 Неправильный формат. Рассмотрите возможность проверки четвертого фактического аргумента функции snprintf. Ожидается аргумент SIGNED типа memsize. конн.
с 309 struct Connection {
.
unsigned long c_connid; .
} .
static int conn_create(.
) { .
bv.bv_len = snprintf( buf, sizeof( buf ), "cn=Connection %ld", // <= c->c_connid ); .
} Спецификатор форматирования "%ld" не соответствует тому, что передано.
snprintf аргумент c-> c_connid .
Правильный спецификатор для беззнаковый длинный это «%lu».
Использование «%ld» вместо «%lu» приведет к получению неверных значений, если аргументы достаточно велики.
Похожие предупреждения:
- V576 Неверный формат. Рассмотрите возможность проверки третьего фактического аргумента функции fprintf. Ожидается аргумент целочисленного типа SIGNED. ure.c 1865 г.
- V576 Неверный формат. Рассмотрите возможность проверки третьего фактического аргумента функции fprintf. Ожидается аргумент SIGNED типа memsize. инструменты.
с 211
- V576 Неправильный формат. Рассмотрите возможность проверки четвертого фактического аргумента функции fprintf. Ожидается аргумент целочисленного типа UNSIGNED. mdb.c 1253
Указатель без ссылки
Предупреждение PVS-Studio: В528 Странно, что указатель на тип «char» сравнивается со значением «\0».Вероятно, имелось в виду: *ludp-> lud_filter != '\0'.
бэкэнд.c 1525 int
fe_acl_group(.
) { .
if ( ludp->lud_filter != NULL && ludp->lud_filter != '\0') // <= { .
} } Автор кода хотел выявить ситуацию, когда указатель равен нулю или строка пуста.
Но я забыл разыменовать указатель ludp-> lud_filter , поэтому указатель будет просто дважды проверен на равенство НУЛЕВОЙ .
Указатель должен быть разыменован: .
if ( ludp->lud_filter != NULL && *ludp->lud_filter != '\0') .
Подобные указатели без ссылок:
- V528 Странно, что указатель на тип 'char' сравнивается со значением '\0'.
Вероятно имелось в виду: *(* lsei)-> lsei_values[0] == '\0'.
синтаксис.
c 240
- V528 Странно, что указатель на тип 'char' сравнивается со значением '\0'.
Вероятно, имелось в виду: *(* lsei)-> lsei_values[1] != '\0'.
синтаксис.
c 241
Дополнительная проверка
Предупреждение PVS-Studio: В560 Часть условного выражения всегда истинна: !saveit. synprov.c 1510 static void
syncprov_matchops( Operation *op, opcookie *opc, int saveit )
{
.
if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
.
} else if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
.
}
.
}
В теме еще нет смысла проверять сохрани это быть равным нулю, поскольку это уже сделано в первом условии.
Это ухудшает читаемость.
И возможно это даже ошибка, так как должна была проверить что-то другое.
Но скорее всего код можно достаточно упростить: if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
.
} else if ( op->o_tag == LDAP_REQ_MODRDN ) { .
}
Опасное использование realloc
Предупреждение PVS-Studio: В701 Возможная утечка в realloc(): когда realloc() не удается выделить память, исходный указатель lud.lud_exts теряется.Рассмотрите возможность назначения функции realloc() временному указателю.
ldapurl.c 306 int
main( int argc, char *argv[])
{
.
lud.lud_exts = (char **)realloc( lud.lud_exts, sizeof( char * ) * ( nexts + 2 ) ); .
} Выражение формы foo = realloc(foo, .
) потенциально опасен.
Если память не может быть выделена перераспределить вернет нулевой указатель, перезаписав предыдущее значение указателя.
Чтобы этого не произошло, перед использованием рекомендуется сохранить значение указателя в дополнительной переменной.
перераспределить .
Перезапись значения
Предупреждение PVS-Studio: В519 Переменной ca.argv присваиваются значения дважды подряд. Возможно, это ошибка.
Проверьте строки: 7774, 7776. bconfig.c 7776 int
config_back_initialize( BackendInfo *bi )
{
.
ca.argv = argv; // <= argv[ 0 ] = "slapd"; ca.argv = argv; // <= ca.argc = 3; ca.fname = argv[0]; .
} Если этот код верен, то первое дополнительное присвоение следует удалить.
Заключение
ReOpenLDAP — проект, призванный обеспечить стабильную работу при высокой нагрузке.Поэтому разработчики ответственно подходят к тестированию и используют специализированные инструменты, такие как ThreadSanitizer И Валгринд .
Однако, как мы видим, этого не всегда достаточно, так как при использовании PVS-Studio был выявлен ряд ошибок, хотя их и немного.
Статический анализ помогает найти ошибки еще до этапа тестирования — на самых ранних этапах, тем самым экономя массу времени.
Поэтому анализаторами следует пользоваться регулярно, а не так, как мы, время от времени демонстрируя возможности нашего инструмента PVS-Studio. Я предлагаю скачать и попробуйте статический анализатор PVS-Studio на своем проекте.
Если вы хотите поделиться этой статьей с англоязычной аудиторией, воспользуйтесь ссылкой для перевода: Егор Бредихин.
Проверка кода LDAP-сервера ReOpenLDAP по просьбе наших читателей .
Вы прочитали статью и у вас есть вопросы? Часто о наших статьях задают одни и те же вопросы.
Ответы на них мы собрали здесь: Ответы на вопросы читателей статей о PVS-Studio версии 2015 .
Пожалуйста, проверьте список.
Теги: #разработка Linux #с открытым исходным кодом #C++ #статический анализ кода #Системное программирование #pvs-studio #статический анализ кода #ReOpenLDAP
-
Как Работает Облачный Биллинг?
19 Dec, 24 -
+10 К Интеллекту
19 Dec, 24