Page MenuHomeFreeBSD

D58058.id182522.diff
No OneTemporary

D58058.id182522.diff

diff --git a/lib/libc/gen/uexterr_format.c b/lib/libc/gen/uexterr_format.c
--- a/lib/libc/gen/uexterr_format.c
+++ b/lib/libc/gen/uexterr_format.c
@@ -61,6 +61,170 @@
}
}
+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", "<format-too-large>");
+ 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("<illegal-format>:%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("<unsupported-format>:%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)
{
@@ -76,8 +240,7 @@
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
--- a/share/man/man9/exterror.9
+++ b/share/man/man9/exterror.9
@@ -6,7 +6,7 @@
.\" Konstantin Belousov <kib@FreeBSD.org> under sponsorship
.\" from the FreeBSD Foundation.
.\"
-.Dd July 21, 2026
+.Dd July 22, 2026
.Dt EXTERROR 9
.Os
.Sh NAME
@@ -94,12 +94,15 @@
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

File Metadata

Mime Type
text/plain
Expires
Sat, Aug 1, 7:48 AM (13 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35816826
Default Alt Text
D58058.id182522.diff (4 KB)

Event Timeline