summaryrefslogtreecommitdiff
path: root/yhttpd/DOCKER.md
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-07 15:07:09 +0300
committerPaul Buetow <paul@buetow.org>2026-07-07 15:07:09 +0300
commiteb46afd5d10b0d2c95a90eade231d519415bdd80 (patch)
treebb933dfbf69be582098190115f7405ce8b61e47b /yhttpd/DOCKER.md
parent1f28ff5ac4a8a466113296d9510fe91f8cd13bc3 (diff)
yhttpd: fix malformed Content-Length crash and unchecked accept
Two residual stability bugs in yhttpd's own sock.cpp (ychat never had them -- yhttpd's read_http request parser is structurally different from ychat's), found while auditing for ychat engine-fix backports: 1. sock::read_http malformed Content-Length crash (a3908e1-class). read_http matched the header on the 15-char prefix "Content-Length:" (no space required) but then assumed the canonical "Content-Length: <value>" form and substr'd from index 16. Two reachable crash cases for any unauthenticated client: - bare "Content-Length:" (15 chars): substr(16, len-16) had pos > size -> std::out_of_range throw -> uncaught -> process crash. - "Content-Length:\n" (16 chars, no value): the substring was empty so the do/while digit scan read past the buffer (OOB read) until a stray '\n' in adjacent memory. Now guarded: require the space separator + a value before substr, and bound the scan to the substring length. Verified in Docker: both malformed cases close gracefully, a valid Content-Length: 0 POST still returns 200, server stays up. 2. sock::start unchecked accept(). The accept() return was used unchecked; on failure (fd == -1, e.g. EMFILE/ENFILE under fd exhaustion, EINTR) FD_SET(-1, &active_fd_set) is UB (bit-op on a negative index) and the later _create_container(-1) would read/write fd -1 (EBADF). Now bails with ACCPERR and continues on any accept error (the accept-bail half of ychat's 1c36abe, which the original yhttpd port only carried the size_t->socklen_t init of). yhttpd is not deployed to the cluster (no Helm chart/ArgoCD app); this is a build-and-verify-in-Docker project, so no deploy step. ycurses shares no socket/template engine with ychat (it is a standalone curses library demo) so nothing applies there.
Diffstat (limited to 'yhttpd/DOCKER.md')
-rw-r--r--yhttpd/DOCKER.md19
1 files changed, 19 insertions, 0 deletions
diff --git a/yhttpd/DOCKER.md b/yhttpd/DOCKER.md
index bba0284..7375189 100644
--- a/yhttpd/DOCKER.md
+++ b/yhttpd/DOCKER.md
@@ -39,6 +39,25 @@ yhttpd shares ychat's buggy files, so most ychat fixes apply. Ported / fixed:
- **`tool::shell_command` (CGI):** replaced `popen("/bin/sh -c …")` with
`fork/execve` of the file directly (no shell), so URL-derived metacharacters
can't inject when `httpd.enablecgi=true` (dormant/off by default).
+- **`sock::read_http` malformed `Content-Length:` crash (a3908e1-class):**
+ `read_http` matched the header on the 15-char prefix `Content-Length:`
+ (no space required) but then assumed the canonical `Content-Length:
+ <value>` form and `substr`'d from index 16. Two reachable crash cases
+ leaked through for any unauthenticated client: a bare `Content-Length:`
+ (15 chars) made `substr(16, len-16)` throw `std::out_of_range` (pos > size)
+ > uncaught > process crash; `Content-Length:\n` (16 chars, no value) left
+ the substring empty so the `do/while` digit scan read past the buffer (OOB
+ read) until a stray `\n` in adjacent memory. Now guarded: require the
+ space separator + a value before `substr`, and bound the scan to the
+ substring length. (ychat's a3908e1 fixed the same malformed-input-crash
+ class in its own structurally different request parser.)
+- **`sock::start` unchecked `accept`:** the `accept()` return was used
+ unchecked; on failure (`fd == -1`, e.g. EMFILE/ENFILE under fd exhaustion,
+ EINTR) `FD_SET(-1, &active_fd_set)` is UB (bit-op on a negative index) and
+ the later `_create_container(-1)` would read/write fd -1 (EBADF). Now bails
+ with `ACCPERR` and `continue`s on any `accept` error (the accept-bail half
+ of ychat's 1c36abe, which the original yhttpd port only carried the
+ `size_t`→`socklen_t` init of).
- **`tool::replace` and the pervasive `unsigned` string positions:** yhttpd
declared many find/substr/replace positions as `unsigned` (32-bit) but
`std::string::find` returns `size_t` (64-bit) `npos`. Truncating `npos` to