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]