Hi Timo,Aki,

   I have been investigating why the IMAP hibernation feature
   (imap-hibernate) fails natively under FreeBSD 15.x, and I managed to trace
   down and fix the core architectural issues.

  The Root Cause Analysis

    1. Integer Overflow on Hibernation Entry: Under FreeBSD 15.x,
       running fstat() on a network/virtual socket descriptor
       returns NODEV (UINT64_MAX or 18446744073709551615) for
       both major() and minor() device numbers. When an IMAP process tries to
       hibernate, it serializes these values and sends them to
       the imap-hibernate service. However,
       both src/imap-hibernate/imap-hibernate-client.c and 
src/imap/imap-master-client.c utilize
       32-bit unsigned int storage for peer_dev_majorand peer_dev_minor,
       paired with str_to_uint(). This triggers an instant integer overflow
       parsing error on entry: imap-hibernate: Error: conn
       unix:imap-hibernate (uid=125): Failed to parse client input: Invalid
       peer_dev_major value: 18446744073709551615

    2. Strict Verification Bug on Unhibernation (Exit): When a client wakes
       up, imap_master_client_verify() in src/imap/imap-master-client.c checks
       if the current peer_st.st_dev matches the
       recorded master_input->peer_dev via the CMP_DEV_T macro. Under
       FreeBSD, since these are virtual network descriptors, the kernel's
       device mapping evaluation causes a mismatch check failure, triggering
       a false positive BUG panic loop during the restore assertion.

  The Complete 64-bit IPC Solution

   To resolve this fundamentally without masking data or dropping validation,
   we need to expand the internal serialization parsers
   to uint64_t using str_to_uint64() on both ends of the IPC boundary, and
   add an explicit exemption for FreeBSD's NODEV (-1) sockets during
   verification.

   Here are the patches required for a complete solution:

    1. Inside src/imap-hibernate/imap-hibernate-client.c

 --- src/imap-hibernate/imap-hibernate-client.c.orig     2026-06-25 19:49:30 UTC
 +++ src/imap-hibernate/imap-hibernate-client.c
 @@ -51,8 +51,7 @@ imap_hibernate_client_parse_input(const char *const *a
                                   const char **error_r)
  {
         const char *key, *value;
 -       unsigned int peer_dev_major = 0, peer_dev_minor = 0;
 -
 +       uint64_t peer_dev_major = 0, peer_dev_minor = 0;
         i_zero(state_r);
         if (args[0] == NULL) {
                 *error_r = "Missing username in input";
 @@ -98,13 +97,13 @@ imap_hibernate_client_parse_input(const char *const *a
                                 return -1;
                         }
                 } else if (strcmp(key, "peer_dev_major") == 0) {
 -                       if (str_to_uint(value, &peer_dev_major) < 0) {
 +                       if (str_to_uint64(value, &peer_dev_major) < 0) {
                                 *error_r = t_strdup_printf(
                                         "Invalid peer_dev_major value: %s", 
value);
                                 return -1;
                         }
                 } else if (strcmp(key, "peer_dev_minor") == 0) {
 -                       if (str_to_uint(value, &peer_dev_minor) < 0) {
 +                       if (str_to_uint64(value, &peer_dev_minor) < 0) {
                                 *error_r = t_strdup_printf(
                                         "Invalid peer_dev_minor value: %s", 
value);
                                 return -1;
 @@ -244,7 +243,7 @@ imap_hibernate_client_parse_input(const char *const *a
                 return -1;
         }
         if (peer_dev_major != 0 || peer_dev_minor != 0)
 -               state_r->peer_dev = makedev(peer_dev_major, peer_dev_minor);
 +               state_r->peer_dev = makedev((unsigned long)peer_dev_major, 
(unsigned long)peer_dev_minor);
         return 0;
  }

    2. Inside src/imap/imap-master-client.c

 --- src/imap/imap-master-client.c.orig  2026-05-12 10:48:57 UTC
 +++ src/imap/imap-master-client.c
 @@ -75,7 +75,7 @@ imap_master_client_parse_input(const char *const *args
                                const char **error_r)
  {
         const char *key, *value;
 -       unsigned int peer_dev_major = 0, peer_dev_minor = 0;
 +       uint64_t peer_dev_major = 0, peer_dev_minor = 0;

         i_zero(input_r);
         i_zero(stats_r);
 @@ -128,13 +128,13 @@ imap_master_client_parse_input(const char *const *args
                                 return -1;
                         }
                 } else if (strcmp(key, "peer_dev_major") == 0) {
 -                       if (str_to_uint(value, &peer_dev_major) < 0) {
 +                       if (str_to_uint64(value, &peer_dev_major) < 0) {
                                 *error_r = t_strdup_printf(
                                         "Invalid peer_dev_major value: %s", 
value);
                                 return -1;
                         }
                 } else if (strcmp(key, "peer_dev_minor") == 0) {
 -                       if (str_to_uint(value, &peer_dev_minor) < 0) {
 +                       if (str_to_uint64(value, &peer_dev_minor) < 0) {
                                 *error_r = t_strdup_printf(
                                         "Invalid peer_dev_minor value: %s", 
value);
                                 return -1;
 @@ -265,7 +265,7 @@ imap_master_client_parse_input(const char *const *args
         }
         if (peer_dev_major != 0 || peer_dev_minor != 0) {
                 master_input_r->peer_dev =
 -                       makedev(peer_dev_major, peer_dev_minor);
 +                       makedev((unsigned long)peer_dev_major, (unsigned 
long)peer_dev_minor);
         }
         return 0;
  }
 @@ -410,7 +410,8 @@ static int imap_master_client_verify(const struct imap
                 return -1;
         }
         if (peer_st.st_ino != master_input->peer_ino ||
 -           !CMP_DEV_T(peer_st.st_dev, master_input->peer_dev)) {
 +           (!CMP_DEV_T(peer_st.st_dev, master_input->peer_dev) &&
 +            major(peer_st.st_dev) != (unsigned long)-1)) {
                 *error_r = t_strdup_printf(
                         "BUG: Expected peer device=%lu,%lu inode=%s doesn't 
match "
                         "client fd's actual device=%lu,%lu inode=%s",

  Empirical Testing & Results

   To answer your question regarding whether incoming mail or flag
   modifications on SELECTED mailboxes are still properly noticed during
   background hibernation--yes, I have performed live production tests with
   these patches applied, and it works perfectly.

     o The Test Scenario: An active connection entered an authenticated
       session on the INBOX and executed an explicit IDLE sequence. It was
       successfully sent to sleep and offloaded to imap-hibernate (verified
       via doveadm who).

     o The Notification Event: A new message was transmitted to the mailbox
       externally via Gmail.

     o The Outcome: The internal event system seamlessly triggered the kernel
       notification layers. The imap-hibernateservice immediately picked up
       the modification event, successfully unhibernated the session, and
       seamlessly handed the socket descriptor back over to a
       standard imap worker process.

   The active client instantly pushed raw streaming updates to the live
   terminal without dropping connection parameters or printing any errors:

   Plaintext

 + idling
 * OK Still here
 * 4249 EXISTS
 * 1 RECENT
 * 4249 FETCH (FLAGS (\Recent $NotJunk NotJunk))

   Long-term stability tests show that idle sessions remain safely tucked
   away under imap-hibernate for hours until client-side teardowns or
   standard network NAT lease expirations occur, at which point the process
   terminates gracefully with a routine Disconnected: Connection closed log
   record.

   I hope this native 64-bit conversion can be merged into standard upstream
   branches to officially unbreak the feature for FreeBSD server deployment.

   Best regards,

   Jordan

     On 25 Jun 2026, at 19:34, Aki Tuomi <[email protected]> wrote:
     Did you also test that incoming mail, or changes to SELECTed mailboxes
     is noticed while hibernated?
_______________________________________________
dovecot mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to