[Bug 296316] [libfetch] Fix POST upload truncation with fetchReqHTTP()

From: <bugzilla-noreply_at_freebsd.org>
Date: Sat, 27 Jun 2026 10:56:27 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=296316

            Bug ID: 296316
           Summary: [libfetch] Fix POST upload truncation with
                    fetchReqHTTP()
           Product: Base System
           Version: 15.1-RELEASE
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: bin
          Assignee: bugs@FreeBSD.org
          Reporter: decke@FreeBSD.org

Created attachment 272175
  --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=272175&action=edit
Fix for the described problem

I noticed a regression in 15.1 that causes fetchReqHTTP() to get stuck when
uploading a file via HTTPS POST request. It worked fine with FreeBSD 15.0. A
small utility that I use was able to trigger that very reliable:

https://codeberg.org/decke/mrpt-upload/src/branch/main/mrpt-upload.c

I used Claude to analyze the truss output and generate a patch that works for
me:


fetch_writev() in lib/libfetch/common.c uses a non-blocking socket with
a poll-and-write loop. The inner poll loop is gated on pfd.revents == 0,
but pfd.revents is never reset after a successful write. On subsequent
iterations the poll is skipped and SSL_write() is called immediately
without checking socket readiness. When the send buffer is full and
SSL_write() returns -1 with errno == EAGAIN, the error is not retried
— the function returns -1, silently truncating the request body.

For unencrypted connections this is less likely to trigger because
writev() operates on all iovec entries at once. For SSL, SSL_write()
operates on a single iovec per call, making short writes and a
subsequent EAGAIN much more likely with large request bodies.

The result observed with fetchReqHTTP() over HTTPS is that the server
receives a truncated POST body, returns an error response, closes its
end of the connection, and fetch_writev() then hangs polling for a
response that never arrives — until fetchTimeout fires.

Fix: In the wlen < 0 branch of fetch_writev(), handle EAGAIN by
resetting pfd.revents = 0 and continuing the loop, re-engaging the poll
before retrying SSL_write().

-- 
You are receiving this mail because:
You are the assignee for the bug.