From: Han Boetes Subject: leak in mg, interpreter.c:599 To: tech@openbsd.org Date: Sat, 21 Feb 2026 20:33:09 +0100 While playing around with valgrind and mg I noticed some leaks. First, I created a test init-file with the contents: (define myvar "hello") (define myvar "world") After which something is leaking in interpreter.c at line 599, see valgrind output below. Upon redefinition of a variable, the old varentry and its v_name string are not freed. Additionally, SLIST_INSERT_HEAD is called before strndup succeeds, meaning a failed strndup leaves a partially initialised entry in the live list. The following patch fixes both issues. With it applied, the interpreter.c leak in the valgrind output below disappears. --- a/interpreter.c +++ b/interpreter.c @@ -592,15 +592,20 @@      if (!SLIST_EMPTY(&varhead)) {          SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) { -            if (strcmp(vnamep, v1->v_name) == 0) +            if (strcmp(vnamep, v1->v_name) == 0) {                  SLIST_REMOVE(&varhead, v1, varentry, entry); +                free(v1->v_name); +                free(v1); +            }          }      }      if ((v1 = malloc(sizeof(struct varentry))) == NULL)          return (ABORT); -    SLIST_INSERT_HEAD(&varhead, v1, entry); -    if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL) +    if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL) { +        free(v1);          return(dobeep_msg("strndup error")); +    } +    SLIST_INSERT_HEAD(&varhead, v1, entry);      vnamep = v1->v_name;      v1->v_count = 0;      v1->v_vals = NULL; %   valgrind --leak-check=full ./mg -u ../test ==132660== Memcheck, a memory error detector ==132660== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al. ==132660== Using Valgrind-3.26.0 and LibVEX; rerun with -h for copyright info ==132660== Command: ./mg -u ../test ==132660== ==132660== ==132660== HEAP SUMMARY: ==132660==     in use at exit: 61,787 bytes in 191 blocks ==132660==   total heap usage: 1,176 allocs, 985 frees, 249,547 bytes allocated ==132660== ==132660== 8 bytes in 1 blocks are definitely lost in loss record 12 of 96 ==132660==    at 0x4840B26: malloc (vg_replace_malloc.c:447) ==132660==    by 0x4096AF: remap (extend.c:143) ==132660==    by 0x40A115: bindkey.constprop.0.isra.0 (extend.c:412) ==132660==    by 0x416E6E: ttykeymapinit (ttykbd.c:39) ==132660==    by 0x400CDA: main (main.c:165) ==132660== ==132660== 16 bytes in 1 blocks are definitely lost in loss record 16 of 96 ==132660==    at 0x4848683: calloc (vg_replace_malloc.c:1678) ==132660==    by 0x4098DD: remap (extend.c:125) ==132660==    by 0x40A115: bindkey.constprop.0.isra.0 (extend.c:412) ==132660==    by 0x416E96: ttykeymapinit (ttykbd.c:41) ==132660==    by 0x400CDA: main (main.c:165) ==132660== ==132660== 32 bytes in 1 blocks are definitely lost in loss record 58 of 96 ==132660==    at 0x4848683: calloc (vg_replace_malloc.c:1678) ==132660==    by 0x4098DD: remap (extend.c:125) ==132660==    by 0x40A115: bindkey.constprop.0.isra.0 (extend.c:412) ==132660==    by 0x416EBE: ttykeymapinit (ttykbd.c:43) ==132660==    by 0x400CDA: main (main.c:165) ==132660== ==132660== 40 bytes in 1 blocks are definitely lost in loss record 60 of 96 ==132660==    at 0x4848683: calloc (vg_replace_malloc.c:1678) ==132660==    by 0x4097A4: remap (extend.c:112) ==132660==    by 0x40A115: bindkey.constprop.0.isra.0 (extend.c:412) ==132660==    by 0x416F12: ttykeymapinit (ttykbd.c:49) ==132660==    by 0x400CDA: main (main.c:165) ==132660== ==132660== 112 bytes in 1 blocks are definitely lost in loss record 74 of 96 ==132660==    at 0x4840B26: malloc (vg_replace_malloc.c:447) ==132660==    by 0x4094E2: reallocmap (extend.c:274) ==132660==    by 0x40999F: remap (extend.c:139) ==132660==    by 0x40A0D3: bindkey.constprop.0.isra.0 (extend.c:402) ==132660==    by 0x416F62: ttykeymapinit (ttykbd.c:53) ==132660==    by 0x400CDA: main (main.c:165) ==132660== ==132660== 166 (160 direct, 6 indirect) bytes in 1 blocks are definitely lost in loss record 77 of 96 ==132660==    at 0x4840B26: malloc (vg_replace_malloc.c:447) ==132660==    by 0x40DCB9: founddef.constprop.0 (interpreter.c:599) ==132660==    by 0x40E4C9: parsdef (interpreter.c:345) ==132660==    by 0x40E4C9: parse.isra.0 (interpreter.c:297) ==132660==    by 0x40E81B: foundparen (interpreter.c:209) ==132660==    by 0x40AD66: load (extend.c:659) ==132660==    by 0x400D05: main (main.c:178) ==132660== ==132660== LEAK SUMMARY: ==132660==    definitely lost: 368 bytes in 6 blocks ==132660==    indirectly lost: 6 bytes in 1 blocks ==132660==      possibly lost: 0 bytes in 0 blocks ==132660==    still reachable: 61,413 bytes in 184 blocks ==132660==         suppressed: 0 bytes in 0 blocks ==132660== Reachable blocks (those to which a pointer was found) are not shown. ==132660== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==132660== ==132660== For lists of detected and suppressed errors, rerun with: -s ==132660== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)