From nobody Thu Aug 20 10:41:53 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hQg3q6YWxz6pZvV for ; Thu, 20 Aug 2026 10:41:59 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hQg3q3Slkz3yfJ for ; Thu, 20 Aug 2026 10:41:59 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1787222519; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=O68o+gVCnedj7Uq8RMbL31kavZ/Tjlhwf3RxMazZ0gA=; b=bX4Tioe7fRPjqteF0kY0+8JFLoVLbQOMCs445bX7qTB0Ewei/k7/Zm0ByFtjE8yjn5KM4I TY9oQhwtyrwxWICaWHqYWm0AjX0S+MS8Y7OAJg1xTjOD7Gd4TSi8JgP8EhajCaNyvM8zZz /L6KiLlca9nOoXy/dgFk5Rjmr0STyvSUfMg5GlHiaeis93O/QK4MIqPYe7l4J5khOEYtwo Ecacaa0ulDJHK3z3WP8HeziyNwWkXckt+nLlaAITAuOJEuXK1B3QE3tLAX7u8pXSJup25a NEjGe+WLPT/AUygjCyMutzK49o9SzgRD/K/x6Rw/sr3Izx5CeSggZY3Bocvq3w== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1787222519; a=rsa-sha256; cv=none; b=j7Q8Iz517Nj5gjh4gB7pTfL39FLWOEn6bBB08INrUHvsH3ov1PEi54j5khblBrSV5qPU1+ P9OkAr42gAwsBUiierSxwhTskRvdiWhYdpDj4q+kQUYA2cFf9T73v18RWK6Ratw7V2DunM dhDSGJXvVBkrUWQOvVGCWSfYgnb0v6AM+DbClGofHQiAbYprWS3Tac4T4nsIDgbR9QyxQM wYU0L7Qz9oAxa4oKxWnSEnoGIJw9ArsBAJe7Qs+83jTs+H0sdl+IjUnw9sY3SH3K9RN68b RZhXOocwDrR8szthCngPp9A1GK/Fl03l/XwHp1SHbj7/Ow41WLwDsZ15FL4i0g== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1787222519; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=O68o+gVCnedj7Uq8RMbL31kavZ/Tjlhwf3RxMazZ0gA=; b=qDtGJwMrt+CSTY02nPNZpugV6pGNg8UBMDVstogcApTyeNJq30EWJB98/XMkSCr6cJcp/5 S/pC9Hoe/gphb1pSYucW0/JvahTGs3l1cbBqQuPU2UfVKYJGJY11N7Af/NSZbd/qGlpHDS dd26eHcFO11b4LMUUZ0CUDkcZH1ab4xi13JfwYV2R4KVTlVWm4NqIoOtrxOSUmO0jyvu0X +Zq5cbAZrtmU9qCKd3gDLtyZi7NmytvUHcaG71NCpWqLHTkLVuHQK2KXZLIoyYg6O2lMM7 2+enXvkiK6CT2hp4NBJZWuMCleIy73mr/qI5Xl5AAU2e7e1nv/3BLt/CYk90DQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hQg3q2744z1CQH for ; Thu, 20 Aug 2026 10:41:59 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 42889 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 20 Aug 2026 10:41:53 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Brooks Davis Subject: git: 2f024a7cfddd - main - exterr: relax format restrictions List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: brooks X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 2f024a7cfddd5ce92df8437d89ba5bc3d55dacaf Auto-Submitted: auto-generated Date: Thu, 20 Aug 2026 10:41:53 +0000 Message-Id: <6a86d9f1.42889.62d0b0c6@gitrepo.freebsd.org> The branch main has been updated by brooks: URL: https://cgit.FreeBSD.org/src/commit/?id=2f024a7cfddd5ce92df8437d89ba5bc3d55dacaf commit 2f024a7cfddd5ce92df8437d89ba5bc3d55dacaf Author: Brooks Davis AuthorDate: 2026-08-20 10:37:44 +0000 Commit: Brooks Davis CommitDate: 2026-08-20 10:38:19 +0000 exterr: relax format restrictions Rather than passing the format string to printf and forcing the arguments to be (u)intmax_t, partially parse format strings and cast p1 and p2 to the correct type before running the individual format though printf. This restructure has a couple motivatations: - We can skip formats that make no sense (floating point, %n, etc.). - It is possible to special case the printing of pointers in the CHERI case. The first case is motivated by a suggestion from the audiance at one of Kirk's BSDCan talks on exterr to allow userspace to set exterr status. Allowing arbitrary format strings including %n creates a write-what-where gadget so we need to not do that. The second case is motivated by our experinces with CHERI and debugging mmap issues using a different textual error reporting framework. With CHERI, pointers are more than integer addresses and it's useful to include more details. Doing so will follow in a future commit. When the new code encounters an inappropriate format it includes a diagnostic and in most cases prints the format untouched. Reviewed by: kib Effort: CHERI upstreaming Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D58058 --- lib/libc/gen/uexterr_format.c | 167 +++++++++++++++++++++++++++++++++++++++++- share/man/man9/exterror.9 | 17 +++-- 2 files changed, 175 insertions(+), 9 deletions(-) diff --git a/lib/libc/gen/uexterr_format.c b/lib/libc/gen/uexterr_format.c index 99ae85715f43..e32776c728bd 100644 --- a/lib/libc/gen/uexterr_format.c +++ b/lib/libc/gen/uexterr_format.c @@ -131,6 +131,170 @@ exterr_verbose_init(void) } } +static void +uexterr_format_msg(const struct uexterror *ue, char *buf, size_t bufsz) +{ + char fmt[32]; /* XXX: how big? */ + const char *msg = ue->msg; + int nextarg = 1, psz; + size_t cindex, mindex; + +#define PCHAR(c) if (bufsz > 1) { *buf++ = c; bufsz--; } /* reserve last byte */ +#define PFMT(f, a) ({ \ + psz = snprintf(buf, bufsz, f, a); \ + if (psz > bufsz) \ + return; /* Out of space */ \ + buf += psz; \ + bufsz -= psz; \ + }) +#define ARG(_n) ({ \ + int n = (_n); \ + n == 1 ? ue->p1 : (n == 2 ? ue->p2 : (uint64_t)-1); \ + }) + + while (*msg != '\0') { + if (*msg != '%') { + PCHAR(*msg++); + continue; + } + + msg++; + /* + * Find the conversion, reject unsound or nonsensical + * ones, and then call snprintf to format the result + * using the correct argument type cast (potentially + * determined by the length modifier). + */ + /* Conversion list ordred by printf(3). */ + cindex = strcspn(msg, "bBdiouxXDOUeEfFgGaACcSspnm%"); + + /* Format too large, just complain */ + if (cindex >= sizeof(fmt)) { + PFMT("%s", ""); + goto format_handled; + } + + /* + * Note: msg points to one past the initial '%' and + * cindex is an index to the conversion in msg. + */ + memcpy(fmt, msg - 1, cindex + 2); + fmt[cindex + 2] = '\0'; + + switch (msg[cindex]) { + case 'b': + case 'B': + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + /* + * Treat longs as 64-bit in 32-bit ABIs because + * that's what the kernel will do (unless we're + * in some 32-bit only code). + * + * This isn't quite right for signed values + * from 32-bit kernels unless the programmer + * took care to sign extend them, but 32-bit + * kernels aren't long for the world... + */ + + /* Find the first length modifier */ + mindex = strcspn(fmt, "hjltwz"); + + switch (fmt[mindex]) { + case '\0': /* No length modifier */ + case 'h': /* h or hh modifier */ + PFMT(fmt, (unsigned)ARG(nextarg)); + break; + + case 'l': +#ifdef __ILP32__ + if (fmt[mindex + 1] != 'l') + fmt[mindex] = 'j'; +#endif + PFMT(fmt, (uintmax_t)ARG(nextarg)); + break;; + + case 't': + case 'z': +#ifdef __ILP32__ + fmt[mindex] = 'j'; + /* FALLTHROUGH */ +#endif + case 'j': + PFMT(fmt, (uintmax_t)ARG(nextarg)); + break;; + + case 'w': + if (fmt[mindex + 1] == 'f') + mindex++; + if (fmt[mindex + 1] == '6' && fmt[mindex + 2] == '4') + PFMT(fmt, (uint64_t)ARG(nextarg)); + else + PFMT(fmt, (unsigned)ARG(nextarg)); + break; + } + break; + + case 'C': + case 'c': + PFMT(fmt, (unsigned)ARG(nextarg)); + break; + + case 'p': + PFMT(fmt, (void *)ARG(nextarg)); + break; + + case '%': + PCHAR('%'); + break; + + /* + * %n is a write-what-where gadget + */ + case 'n': + PFMT(":%s", fmt); + break; + + /* + * Things we don't support + */ + /* Incomplete expression */ + case '\0': + /* Obsolete formats */ + case 'D': + case 'O': + case 'U': + /* Floating point */ + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + /* String */ + case 'S': + case 's': + /* errno */ + case 'm': + /* strcspn list out of sync with this switch. */ + default: + PFMT(":%s", fmt); + break; + } +format_handled: + nextarg++; + msg += cindex + 1; + } + *buf = '\0'; +#undef PCHAR +#undef PFMT +#undef ARG +} + int __uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz) { @@ -146,8 +310,7 @@ __uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz) has_msg = ue->msg[0] != '\0'; if (has_msg) { - snprintf(buf, bufsz, ue->msg, (uintmax_t)ue->p1, - (uintmax_t)ue->p2); + uexterr_format_msg(ue, buf, bufsz); } else { strlcpy(buf, "", bufsz); } diff --git a/share/man/man9/exterror.9 b/share/man/man9/exterror.9 index a7e63abab577..ff4033c6fc1d 100644 --- a/share/man/man9/exterror.9 +++ b/share/man/man9/exterror.9 @@ -6,7 +6,7 @@ .\" Konstantin Belousov under sponsorship .\" from the FreeBSD Foundation. .\" -.Dd August 3, 2026 +.Dd August 20, 2026 .Dt EXTERROR 9 .Os .Sh NAME @@ -101,12 +101,15 @@ The format string may include up to two printf-like format specifiers to insert the optional argument values in the user output, which is done in userspace. .Pp -The format specifier must be for an integer type, and include the -.Dq j -format modifier to accept only the types -.Vt intmax_t -or -.Vt uintmax_t . +The format specifier must be for an character, integer, or pointer type. +Note that userspace printing assumes all +.Dt long Ns -derived +types such as +.Dt size_t +are 64-bit and prints them accordingly. +Signed integer types should thus be cast to +.Dt int64_t +or similar to insure proper sign extension. .El .Pp The strings passed as the second argument are only retained