Package: libncurses6 Version: 6.4-4 Severity: normal Tags: patch Dear Maintainer,
Found by valgrind: $ valgrind ./urlview text text.uv README ... ==1999505== Invalid read of size 1 ==1999505== at 0x4872850: waddnstr (in /usr/lib/x86_64-linux-gnu/libncursesw.so.6.4) ==1999505== by 0x10C26E: main (urlview.c:620) ==1999505== Address 0x4c78fbc is 0 bytes after a block of size 1,052 alloc'd ==1999505== at 0x484582F: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==1999505== by 0x10BD0D: main (urlview.c:610) this corresponds to https://git.sr.ht/~nabijaczleweli/urlview-ng/tree/44240e3b8ed0694f14f40f4b1169359abe452b4b/item/urlview.c#L566-621 which is a big chunk, sorry (execstatus allocated in EXECAPPENDONE(), waddnstr() invoked via mvaddnstr() at the end). but the code amounts to: #include <curses.h> #include <string.h> #include <stdlib.h> int main() { char * new = malloc(4); memcpy(new, "dupa", 4); initscr(); addnstr(new, 4); endwin(); } with $ cc n.c $(pkgconf --cflags --libs ncursesw) $ valgrind ./a.out > /dev/pts/5 ==2022105== Memcheck, a memory error detector ==2022105== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==2022105== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info ==2022105== Command: ./a.out ==2022105== ==2022105== Invalid read of size 1 ==2022105== at 0x4872850: waddnstr (in /usr/lib/x86_64-linux-gnu/libncursesw.so.6.4) ==2022105== by 0x1091AE: main (in /home/nabijaczleweli/uwu/a.out) ==2022105== Address 0x4ab9044 is 0 bytes after a block of size 4 alloc'd ==2022105== at 0x48407B4: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2022105== by 0x109181: main (in /home/nabijaczleweli/uwu/a.out) ==2022105== ==2022105== ==2022105== HEAP SUMMARY: ==2022105== in use at exit: 813,473 bytes in 228 blocks ==2022105== total heap usage: 238 allocs, 10 frees, 821,440 bytes allocated ==2022105== ==2022105== LEAK SUMMARY: ==2022105== definitely lost: 4 bytes in 1 blocks ==2022105== indirectly lost: 0 bytes in 0 blocks ==2022105== possibly lost: 0 bytes in 0 blocks ==2022105== still reachable: 813,469 bytes in 227 blocks ==2022105== suppressed: 0 bytes in 0 blocks ==2022105== Rerun with --leak-check=full to see details of leaked memory ==2022105== ==2022105== For lists of detected and suppressed errors, rerun with: -s ==2022105== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) I hope this sample is simple enough it's obvious it's correct. Looking at ncurses-6.4+20230625/ncurses/base/lib_addstr.c: NCURSES_EXPORT(int) waddnstr(WINDOW *win, const char *astr, int n) { const char *str = astr; int code = ERR; T((T_CALLED("waddnstr(%p,%s,%d)"), (void *) win, _nc_visbufn(astr, n), n)); if (win && (str != 0)) { TR(TRACE_VIRTPUT | TRACE_ATTRS, ("... current %s", _traceattr(WINDOW_ATTRS(win)))); code = OK; TR(TRACE_VIRTPUT, ("str is not null, length = %d", ((n > 0) ? n : (int) strlen(str)))); if (n < 0) n = INT_MAX; while ((*str != '\0') && (n-- > 0)) { NCURSES_CH_T ch; TR(TRACE_VIRTPUT, ("*str = %#o", UChar(*str))); SetChar(ch, UChar(*str++), A_NORMAL); if (_nc_waddch_nosync(win, ch) == ERR) { code = ERR; break; } } _nc_synchook(win); } TR(TRACE_VIRTPUT, ("waddnstr returns %d", code)); returnCode(code); } I see "while ((*str != '\0') && (n-- > 0)) {" is in the wrong order, and ought to be "(n-- > 0) && (*str != '\0')" ‒ only reading from str when it's not past the end. A cursory glance with \bn\b at the rest of the funxions in that file reveals: waddchnstr has "for (i = 0; i < n && ChCharOf(astr[i]) != '\0'; ++i) {" which is correct, waddnwstr has "while ((*str != L('\0')) && (n-- > 0)) {" which should also be "while ((n-- > 0) && (*str != L('\0'))) {". Rebuilding 6.4+20230625-2 with the changes described (obvious diff attached) fixes the issue $ LD_LIBRARY_PATH=~/uwu/ncurses-6.4+20230625/obj-wide/usr/lib/x86_64-linux-gnu/ valgrind ./a.out > /dev/pts/5 ==2162311== Memcheck, a memory error detector ==2162311== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==2162311== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info ==2162311== Command: ./a.out ==2162311== ==2162311== ==2162311== HEAP SUMMARY: ==2162311== in use at exit: 813,473 bytes in 228 blocks ==2162311== total heap usage: 239 allocs, 11 frees, 821,468 bytes allocated ==2162311== ==2162311== LEAK SUMMARY: ==2162311== definitely lost: 4 bytes in 1 blocks ==2162311== indirectly lost: 0 bytes in 0 blocks ==2162311== possibly lost: 0 bytes in 0 blocks ==2162311== still reachable: 813,469 bytes in 227 blocks ==2162311== suppressed: 0 bytes in 0 blocks ==2162311== Rerun with --leak-check=full to see details of leaked memory ==2162311== ==2162311== For lists of detected and suppressed errors, rerun with: -s ==2162311== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) but there are probably other funxions that take string_view^W(char*, int)s and need to be evaluated, and I don't know what those would be. Best, наб -- System Information: Debian Release: 12.2 APT prefers stable-updates APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable-debug'), (500, 'stable') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 6.1.0-9-amd64 (SMP w/24 CPU threads; PREEMPT) Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_FIRMWARE_WORKAROUND, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), LANGUAGE=en_GB:en Shell: /bin/sh linked to /usr/bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages libncurses6 depends on: ii libc6 2.36-9+deb12u3 ii libtinfo6 6.4-4 Versions of packages libncurses6 recommends: ii libgpm2 1.20.7-10+b1 libncurses6 suggests no packages. -- no debconf information
--- ncurses-6.4+20230625.orig/ncurses/base/lib_addstr.c
+++ ncurses-6.4+20230625/ncurses/base/lib_addstr.c
@@ -64,7 +64,7 @@ waddnstr(WINDOW *win, const char *astr,
((n > 0) ? n : (int) strlen(str))));
if (n < 0)
n = INT_MAX;
- while ((*str != '\0') && (n-- > 0)) {
+ while ((n-- > 0) && (*str != '\0')) {
NCURSES_CH_T ch;
TR(TRACE_VIRTPUT, ("*str = %#o", UChar(*str)));
SetChar(ch, UChar(*str++), A_NORMAL);
@@ -237,7 +237,7 @@ waddnwstr(WINDOW *win, const wchar_t *st
((n > 0) ? n : (int) wcslen(str))));
if (n < 0)
n = INT_MAX;
- while ((*str != L('\0')) && (n-- > 0)) {
+ while ((n-- > 0) && (*str != L('\0'))) {
NCURSES_CH_T ch;
TR(TRACE_VIRTPUT, ("*str[0] = %#lx", (unsigned long) *str));
SetChar(ch, *str++, A_NORMAL);
signature.asc
Description: PGP signature

