openlang

fleetcfg: искажённые имена хостов при 200 хостах и утечка, которая растёт при сбое malloc. Один баг или два?

Я поддерживаю небольшую утилиту на C, fleetcfg: она загружает Lua-конфиг, возвращающий список хостов, копирует их в HostList, а затем выводит. TCP-проба пока TODO.

Два наблюдения, и я не понимаю, связаны ли они:

  1. С реальным prod-конфигом на 200 хостов в нескольких строках итогового списка пустое имя или странные байты. С тестовым конфигом на 3 хоста такого не бывает. Подозреваю сторону Lua, может быть, то, что luaL_dofile оставляет на стеке. Вызов lua_gc я практически исключил, потому что вывод был правильным ещё до того, как я его добавил. Но это не подтверждено.

  2. С LD_PRELOAD-обёрткой, которая роняет malloc/realloc после N вызовов, сбой внутри hostlist_push выводит "out of memory" и завершается с кодом 1, как и задумано. Но valgrind сообщает "definitely lost", и этот блок растёт вместе с количеством хостов. Я предположил, что очистка Lua что-то пропускает, а остальное покрывает hostlist_free.

valgrind --leak-check=full ./fleetcfg $PWD/prod hosts.lua

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

Релевантные фрагменты:

l->items = realloc(l->items, ncap * sizeof(Host));
if (l->items == NULL)
    return -1;
const char *name = lua_tostring(L, -2);
...
lua_pop(L, 3);
...
hostlist_push(out, name, port)

С чего бы вы начали смотреть? Не могу обещать, что эти два бага независимы, и под отладчиком я пока ничего не запускал.

Переведено с английскогоПоказать оригинал

Разговор продолжается 5

Следуя доводам
  1. Ссылка на ответ

    Сделайте strdup для имени, у меня это убрало мусор. Lua может собрать строку, как только она снята со стека, а 200 хостов достаточно, чтобы запустить GC. Ещё l->items = realloc(l->items, ...) теряет старый блок, когда realloc не срабатывает. Используйте временный указатель.

    Переведено с английскогоПоказать оригинал
  2. Ссылка на ответ

    Немного не соглашусь. Я бы не стал винить luaL_dofile и стек. По-моему, дело просто в указателе.

    В мануале сказано, что строка, которую возвращает lua_tostring, валидна, только пока значение остаётся на стеке, а после этого Lua может её освободить: https://www.lua.org/manual/5.4/manual.html#lua_tolstring

    Вы делаете lua_pop(L, 3), а потом вызываете hostlist_push с name, так что указатель уже висячий. На 3 хостах GC не срабатывает, поэтому всё выглядит нормально. На 200 срабатывает.

    Фикс: strdup внутри hostlist_push, проверка на NULL и освобождение каждого имени в hostlist_free. Сделайте это до того, как гоняться за утечкой. После этого заново прогоните valgrind, потому что эти две проблемы могут быть не отдельными.

    Переведено с английскогоПоказать оригинал
  3. Ссылка на ответ

    О, интересно, что блок растёт вместе с числом хостов, хорошая зацепка! Вы печатали lua_gettop в начале каждой итерации цикла и после pop? И $PWD в этой строке с valgrind взят в кавычки, с учётом пробела?

    Переведено с английскогоПоказать оригинал

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

  1. Предупреждение про висячий указатель пропало, а указатель остался

    Передаю имя в библиотеку на C++ через обёртку, и сначала написал всё в одну строку, CString::new(name).unwrap().as_ptr(), и r…

    Ответы 2Последняя активность rustcpp
  2. Один и тот же вызов с sink, сначала копия, потом перенос

    Nim 2 решает, копировать или переносить, в каждой точке вызова. Не на уровне proc. Тот же аргумент, та же proc. Первый вызов…

    Ответы 2Последняя активность nim
  3. getline выдаёт мусор, плюс утечка

    Вот прям застрял на этом. Какой баг сначала? hosts.txt: hosts_bad.txt:

    Ответы 6Последняя активность c