openlang

fleetcfg: garbled host names with 200 hosts, and a leak that grows when malloc fails. One bug or two?

I maintain a small C tool, fleetcfg, that loads a Lua config returning a list of hosts, copies them into a HostList, then prints them. The TCP probe is still a TODO.

Two observations, and I can't tell if they're related:

  1. With the real 200-host prod config, a few lines in the final listing show an empty name or odd bytes. The 3-host test config never does. I suspect the Lua side, maybe what luaL_dofile leaves on the stack. I've pretty much ruled out the lua_gc call, since the output was right before I added it. That is not confirmed.

  2. With an LD_PRELOAD wrapper that fails malloc/realloc after N calls, a failure inside hostlist_push prints "out of memory" and exits 1 as designed. But valgrind reports "definitely lost", and the block grows with the host count. I assumed the Lua cleanup misses something and that hostlist_free covers the rest.

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

My checkout path has a space in it, and the valgrind output looked odd to me.

Relevant pieces:

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)

Where would you start looking? I can't promise the two are separate, and I haven't run anything under a debugger yet.

The conversation continues 5

Follow the reasoning
  1. Permalink

    strdup the name, that fixed the garbage for me. Lua can collect the string once it's popped, and 200 hosts is enough to trigger a GC. Also, l->items = realloc(l->items, ...) leaks the old block when realloc fails. Use a temp pointer.

  2. Permalink

    Disagree a bit. I wouldn't blame luaL_dofile and the stack. I reckon it's just the pointer.

    The manual says the string lua_tostring returns is only valid while the value is still on the stack, and Lua can free it after that: https://www.lua.org/manual/5.4/manual.html#lua_tolstring

    You lua_pop(L, 3) and then call hostlist_push with name, so it's already dangling. 3 hosts never triggers a GC, so it looks fine. 200 does.

    Fix: strdup inside hostlist_push, check for NULL, and free every name in hostlist_free. Do that before chasing the leak. Re-run valgrind after, because the two might not be separate.

  3. Permalink

    Oh, interesting that the block grows with the host count, that's a nice clue! Have you printed lua_gettop at start of each loop iteration and after the pop? And is $PWD quoted in that valgrind line, with the space?

Other sides of the same question

  1. The dangling pointer warning went away, the pointer did not

    Passing a name into a C++ library through a wrapper, and the first version was one line, CString::new(name).unwrap().as_ptr()…

    Replies 2Latest activity rustcpp
  2. Same sink call, a copy on one line and a move on the next

    Nim 2 decides copy or move per call site. Not per proc. Same argument, same proc. The first call copies, because a is still n…

    Replies 2Latest activity nim
  3. getline rows garbage, plus a leak

    Pretty much stuck on this. Which bug first? hosts.txt: hosts_bad.txt:

    Replies 6Latest activity c