libformw: field_buffer() segfaults on a field from dup_field()/link_field()

Serhiy Storchaka <[email protected]> Mon, 3 Aug 2026 14:05:18 +0300
Newsgroups gmane.comp.lib.ncurses.bugs
Message-ID <[email protected]>
field_buffer() dereferences a NULL pointer for any field created by
dup_field() or link_field(), when linked against libformw.  The narrow
libform is not affected, and only the read side fails: set_field_buffer()
and post_form() work on such a field, and calling set_field_buffer() first
does not help.

ncurses 6.6.20251231 (Debian libncursesw6 6.6+20251231-1),
Linux 7.0.0 x86_64, gcc 15.2.0.

   #include <form.h>
   #include <locale.h>
   #include <stdio.h>

   int
   main(void)
   {
       setlocale(LC_ALL, "");
       initscr();
       FIELD *orig = new_field(1, 10, 0, 0, 0, 0);
       set_field_buffer(orig, 0, "hello");
       FIELD *copy = dup_field(orig, 1, 0);
       endwin();
       printf("orig: %s\n", field_buffer(orig, 0));
       printf("dup:  %s\n", field_buffer(copy, 0));   /* SIGSEGV here */
       return 0;
   }

   $ gcc $(pkg-config --cflags formw ncursesw) t.c -o t \
         $(pkg-config --libs formw ncursesw)
   $ ./t
   orig: hello
   Segmentation fault
   #0  field_buffer () from /usr/lib/x86_64-linux-gnu/libformw.so.6

new_field() allocates the per-field rendering state:

     fld_def.c:307:  New_Field->working = newpad(1, 
Buffer_Length(New_Field) + 1);
     fld_def.c:308:  New_Field->expanded = typeCalloc(char *, 1 + 
(unsigned)nbuf);

dup_field() and link_field() start from *_nc_Default_Field, where both are
NULL, and never allocate them.  A NULL 'working' is repaired by accident,
since set_field_buffer() reallocates it when wresize() fails
(frm_driver.c:4800-4804), but nothing repairs 'expanded', and field_buffer()
indexes it unconditionally:

     frm_driver.c:4900:  if (field->expanded[buffer] != NULL)

test/dup_field.c never calls field_buffer() on the duplicated field, 
which is
probably why this has gone unnoticed.

No patch, as I cannot tell whether you would rather allocate the state in
dup_field()/link_field() or have field_buffer() allocate it on demand.