NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
lib/60054: libedit: adding user defined functions with narrow name/desription leaks memory
>Number: 60054
>Category: lib
>Synopsis: libedit: adding user defined functions with narrow name/desription leaks memory
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue Mar 03 22:15:00 +0000 2026
>Originator: Kristofer Peterson
>Release: NetBSD-current
>Organization:
Tranception Limited
>Environment:
>Description:
Adding user defined functions in libedit via el_set(EL_ADDFN) converts the narrow character name and help/description strings to wide versions allocated on the heap which are then leaked when el_end() is called.
/bin/sh registers user defined functions with editline and will call el_init() and el_end() whenever editing and interactive modes are enabled and disabled, leaking memory as it goes.
Rather than simply converting the narrow help and description strings to wide character versions and then leaking them, this patch adds an internal function map_addfunc_n() which allocates a single buffer to hold the two widened strings as well as a next buffer pointer. A head buffer pointer is added to el_map_t to link these buffers in a singly linked list which is cleaned when map_end() is called.
>How-To-Repeat:
Create this shell script:
-------------------------
set -E
x=0
while [ $x -ne 10000 ]
do
set +i
set -i
x=$((x+1))
done
-------------------------
Run the shell script:
valgrind --leak-check=full --show-leak-kinds=all /bin/sh SCRIPT.SH
You should see leaked memory similar to the following:
...
==94995== LEAK SUMMARY:
==94995== definitely lost: 1,279,872 bytes in 19,998 blocks
==94995== indirectly lost: 0 bytes in 0 blocks
==94995== possibly lost: 0 bytes in 0 blocks
==94995== still reachable: 177,996 bytes in 237 blocks
==94995== suppressed: 104,160 bytes in 1 blocks
...
>Fix:
diff -ur a/src/lib/libedit/eln.c b/src/lib/libedit/eln.c
--- a/src/lib/libedit/eln.c 2025-12-14 13:07:40.000000000 -0500
+++ b/src/lib/libedit/eln.c 2026-03-03 16:33:23.888960000 -0500
@@ -212,23 +212,11 @@
/* XXX: do we need to change el_func_t too? */
case EL_ADDFN: { /* const char *, const char *, el_func_t */
- const char *args[2];
- el_func_t func;
- wchar_t **wargv;
+ const char *name = va_arg(ap, const char *);
+ const char *help = va_arg(ap, const char *);
+ el_func_t func = va_arg(ap, el_func_t);
- args[0] = va_arg(ap, const char *);
- args[1] = va_arg(ap, const char *);
- func = va_arg(ap, el_func_t);
-
- wargv = ct_decode_argv(2, args, &el->el_lgcyconv);
- if (!wargv) {
- ret = -1;
- goto out;
- }
- /* XXX: The two strdup's leak */
- ret = map_addfunc(el, wcsdup(wargv[0]), wcsdup(wargv[1]),
- func);
- el_free(wargv);
+ ret = map_addfunc_n(el, name, help, func);
break;
}
case EL_HIST: { /* hist_fun_t, const char * */
diff -ur a/src/lib/libedit/map.c b/src/lib/libedit/map.c
--- a/src/lib/libedit/map.c 2025-12-14 13:07:40.000000000 -0500
+++ b/src/lib/libedit/map.c 2026-03-03 16:47:20.423101000 -0500
@@ -57,6 +57,11 @@
#include "help.h"
#include "parse.h"
+typedef struct el_map_data_t {
+ struct el_map_data_t *next;
+ wchar_t data[];
+} el_map_data_t;
+
static void map_print_key(EditLine *, el_action_t *, const wchar_t *);
static void map_print_some_keys(EditLine *, el_action_t *, wint_t, wint_t);
static void map_print_all_keys(EditLine *);
@@ -934,6 +939,7 @@
* EL_NUM_FCNS);
el->el_map.nfunc = EL_NUM_FCNS;
el->el_map.wordchars = NULL;
+ el->el_map.data = NULL;
#ifdef VIDEFAULT
map_init_vi(el);
@@ -966,6 +972,12 @@
el->el_map.help = NULL;
el_free(el->el_map.func);
el->el_map.func = NULL;
+ for (el_map_data_t *curr = (el_map_data_t *)el->el_map.data; curr != NULL;) {
+ el_map_data_t * const next = curr->next;
+ el_free(curr);
+ curr = next;
+ }
+ el->el_map.data = NULL;
}
@@ -1424,6 +1436,40 @@
default:
EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
}
+ return 0;
+}
+
+
+/* map_addfunc():
+ * add a user defined function widening narrow name & help strings
+ */
+libedit_private int
+map_addfunc_n(EditLine *el, const char *name, const char *help,
+ el_func_t func)
+{
+ el_map_data_t *data;
+ wchar_t *name_w, *help_w;
+ size_t name_n, help_n;
+
+ if (name == NULL || help == NULL || func == NULL ||
+ (name_n = mbstowcs(NULL, name, 0)) == (size_t)-1 ||
+ (help_n = mbstowcs(NULL, help, 0)) == (size_t)-1 ||
+ (data = el_malloc(sizeof(*data) + sizeof(wchar_t) *
+ (name_n + help_n + 2))) == NULL)
+ return -1;
+
+ name_w = data->data;
+ help_w = data->data + name_n + 1;
+
+ if (mbstowcs(name_w, name, name_n + 1) > name_n ||
+ mbstowcs(help_w, help, help_n + 1) > help_n ||
+ map_addfunc(el, name_w, help_w, func) != 0) {
+ el_free(data);
+ return -1;
+ }
+
+ data->next = el->el_map.data;
+ el->el_map.data = data;
return 0;
}
diff -ur a/src/lib/libedit/map.h b/src/lib/libedit/map.h
--- a/src/lib/libedit/map.h 2025-12-14 13:07:40.000000000 -0500
+++ b/src/lib/libedit/map.h 2026-03-03 16:35:30.839826000 -0500
@@ -60,6 +60,7 @@
el_func_t *func; /* List of available functions */
size_t nfunc; /* The number of functions/help items */
wchar_t *wordchars; /* The word character separators */
+ void *data; /* Widened string data for functions */
} el_map_t;
#define MAP_EMACS 0
@@ -77,6 +78,8 @@
libedit_private int map_set_wordchars(EditLine *, wchar_t *);
libedit_private int map_get_wordchars(EditLine *, const wchar_t **);
libedit_private int map_addfunc(EditLine *, const wchar_t *, const wchar_t *,
+ el_func_t);
+libedit_private int map_addfunc_n(EditLine *, const char *, const char *,
el_func_t);
#endif /* _h_el_map */
Home |
Main Index |
Thread Index |
Old Index