#1148483 aptitude: Exit after 'Building view' "Uncaught exception: Unable to read from stdin: Invalid argument" in testing on arm64

Package:
aptitude
Source:
aptitude
Description:
terminal-based package manager
Submitter:
Derek Dongray
Date:
2026-09-30 16:13:01 UTC
Severity:
normal
#1148483#5
Date:
2026-09-19 23:22:14 UTC
From:
To:
Dear Maintainer,

Package: aptitude
Version: 0.8.13-7

System:
- Raspberry Pi arm64
- Linux 6.18.39+rpt-rpi-v8
- glibc 2.43
- ncurses 6.6

The aptitude curses interface exits immediately with:

Uncaught exception: Unable to read from stdin: Invalid argument

The failure is reproducible from:
- SSH sessions
- PuTTY
- Console terminal /dev/tty1
- script(1)

Non-interactive aptitude commands work:

aptitude search '~i'
aptitude update

The interface starts, builds the package view and displays package groups, then exits.

GDB catchpoint on the final exception shows:

cwidget::toplevel::input_thread::fatal_input_error::dispatch()
cwidget::toplevel::mainloop()

Relevant debugging performed:
- Tested aptitude 0.8.13-8.1 and 0.8.13-7
- Tested libcwidget4 0.5.18-6+b3 and 0.5.18-6+b2
- Tested apt/libapt-pkg 3.3.3 and 3.0.3
- Tested TERM=putty, xterm and vt100
- Tested via SSH and script(1)
- Python curses applications work correctly

strace of input operations shows repeated:

ppoll([{fd=0, events=POLLIN}], ...) = 0

and no failing read(), poll(), or ioctl() returning EINVAL was observed.

I also tried a fresh install of the current Debian image (trixie)

1. Copy Pi image (trixie) to SD, boot and login
2. Install aptitude
3. Check it works
4. Change all references from trixie to testing in /etc/apt/sources.list.d/debian.sources so it's using the testing repository
5. sudo apt update
6. Update aptitude from 0.8.13-7 to 0.8.13-8.1 which is the current version in testing (sudo apt install aptitude=0.8.13-8.1)
7. Check aptitude still works
8. Update libc6 from 2.41-12+rpt1+deb13u4 to 2.43-5, current version in testing (sudo apt install libc6=2.43-5)
9. aptitude is now broken and fails as above.

#1148483#10
Date:
2026-09-20 17:04:14 UTC
From:
To:
It took many hours to do

  ltrace -o ltrace2.txt -C -S -f -t -n1 aptitude

Here's the second that the error message appears in.

#1148483#15
Date:
2026-09-22 12:36:09 UTC
From:
To:
It's in the cwidget::toplevel::input_thread::dispatch() method:


    class input_thread
    {
...
      class get_input_event : public event
      {
...
        void dispatch()
        {
          // NB: use the GLOBAL getch function to avoid weirdness
          // referencing the state of toplevel.
          wint_t wch = 0;
          int status;

          bool done = false;

...
          // Tracks the error corresponding to the last read; used to
          // report errors to the user if we're unable to read a
          // keystroke.
          int last_read_error = 0;
          while(!done)
            {
              // Abort if the world went away underneath us.
              //
              // We need to check up here since we might have just
              // finished invoking a key handler, which could have
              // suspended cwidget.
              if(get_suspend_count() != my_suspend_count)
                return;

              // I assume here that init() set nodelay.
              do
                {
                  errno = 0;
                  status = get_wch(&wch);
                  last_read_error = errno;

                  if(status == KEY_CODE_YES && wch == KEY_RESIZE)
                    // Don't confuse resize events with EOF.
                    read_anything = true;
                } while(status == KEY_CODE_YES && wch == KEY_RESIZE);

              if(status == ERR) // No more to read.
                {
                  // Ignore signals caught during read() (EINTR) and
                  // beep at the user if there's a problem decoding a
                  // multibyte character.
                  if(last_read_error == EINTR)
                    read_anything = true;
                  else if(last_read_error == EILSEQ)
                    {
                      read_anything = true;
                      beep();
                    }

                  threads::mutex::lock l(m);
                  b = true;
                  c.wake_all();
                  done = true;
                }
              else
                {
...
                }
            }

          // If nothing was read (where "stuff being read" includes
          // resize keystrokes and interrupted system calls), kill the
          // main loop off.
          if(!read_anything)
            throw fatal_input_exception(last_read_error);
        }
      };


The get_wch() call is setting errno to EINVAL(22) "invalid argument".


I cannot reproduce this problem on a Raspberry Pi with a simple test
program:

#define NCURSES_WIDECHAR 1
#include <curses.h>
#include <errno.h>

int main(void)
{
   wint_t wch = 0;

   WINDOW *w = initscr();
   mousemask(BUTTON1_PRESSED, NULL);

   errno = 0;
   int status = get_wch(&wch);
   int last_read_error = errno;

   endwin();

   printf("wch=%d status=%d errno=%d\n",
     wch, status, last_read_error);

   return 0;
}

#1148483#20
Date:
2026-09-28 21:54:11 UTC
From:
To:
The problem here is how errno is handled in cwidget (used by aptitude).

man errno:

In cwidget-0.5.18/src/cwidget/toplevel.cc:488 result of select is used
incorrectly. It's allowed to return any value different than -1 on success.
errno can be checked **only** if select returns -1.

Relevant part of code:

pthread_testcancel();
int result = select(1, &selectfds, NULL, NULL, &timeout);
pthread_testcancel();   // Workaround for Linux threads suckage.
                        // See pthread_cancel(3).

if(result != 1)
{
    if(errno != EINTR && errno != 0)
    {
        // Probably means that there was an error reading
        // standard input.  (could also be ENOMEM)
        post_event(new fatal_input_error(errno));
        break;
    }
}

If errno was not 0 before we are entering a loop it's game over.
fatal_input_error path is triggered and aptitude will die.

How it was found? First observation is that error is:

Let's run aptitude under strace: strace -f aptitude and search for
EINVAL.

Here is a result in my test env:

1209  madvise(0xaaab14a00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1210  <... madvise resumed>)            = -1 EINVAL (Invalid argument)
1211  <... madvise resumed>)            = -1 EINVAL (Invalid argument)
1212  <... madvise resumed>)            = -1 EINVAL (Invalid argument)
1209  readlinkat(AT_FDCWD, "/var", 0xffffd3d22040, 1023) = -1 EINVAL (Invalid argument)
1209  readlinkat(AT_FDCWD, "/var/lib", 0xffffd3d22040, 1023) = -1 EINVAL (Invalid argument)
1209  readlinkat(AT_FDCWD, "/var/lib/dpkg", 0xffffd3d22040, 1023) = -1 EINVAL (Invalid argument)
1209  readlinkat(AT_FDCWD, "/var/lib/dpkg/status", 0xffffd3d22040, 1023) = -1 EINVAL (Invalid argument)
1209  madvise(0xffffa4c5f000, 4677632, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffffa4363000, 9420800, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff92d39000, 25116672, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff9243d000, 9420800, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff90c49000, 25116672, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff90a48000, 2101248, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff90647000, 4198400, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff8fe46000, 8392704, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff8ee45000, 16781312, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff902ae000, 10072064, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xffff8e4aa000, 10072064, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab14c00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab14e00000, 10485760, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab15800000, 4194304, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab15c00000, 4194304, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab16000000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab16200000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab16400000, 6291456, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab16a00000, 10485760, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab17400000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab17600000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab17800000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab17a00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab17c00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab17e00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18000000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18200000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18400000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18600000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18800000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18a00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18c00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab18e00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19000000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19200000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19400000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19600000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19800000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19a00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19c00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab19e00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1a000000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1a200000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1a400000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1a600000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1a800000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1aa00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1ac00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1ae00000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1b000000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1b200000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1b400000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1b600000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)
1209  madvise(0xaaab1b800000, 2097152, MADV_HUGEPAGE) = -1 EINVAL (Invalid argument)

Why that happens? Looks like since libc 2.43 in arm64 version of Debian
libc tries to be smart about hugepages and set hints.

Here is a snippet from glibc malloc.c:

static inline void
madvise_thp (void *p, INTERNAL_SIZE_T size)
{
#ifdef MADV_HUGEPAGE

  /* Only use __madvise if the system is using 'madvise' mode and the size
     is at least a huge page, otherwise the call is wasteful. */
  if (mp_.thp_mode != thp_mode_madvise || size < mp_.thp_pagesize)
    return;

  /* Linux requires the input address to be page-aligned, and unaligned
     inputs happens only for initial data segment.  */
  if (__glibc_unlikely (!PTR_IS_ALIGNED (p, GLRO (dl_pagesize))))
    {
      void *q = PTR_ALIGN_UP (p, GLRO (dl_pagesize));
      size -= PTR_DIFF (q, p);
      p = q;
    }