This is an automated email from the ASF dual-hosted git repository.

bryancall pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 4b6f790351 Fix incorrect errno short names on FreeBSD/macOS in 
bwf::Errno (#13240)
4b6f790351 is described below

commit 4b6f79035141c3904c889ff83f0b68dff5209f53
Author: Bryan Call <[email protected]>
AuthorDate: Wed Jun 17 08:23:16 2026 -0700

    Fix incorrect errno short names on FreeBSD/macOS in bwf::Errno (#13240)
    
    bwf::Errno used a single Linux-numbered table indexed by raw errno, so
    other platforms printed the wrong name (e.g. ETIMEDOUT=60 logged as
    ENOSTR on FreeBSD/macOS). Replace it with a per-platform table built
    from each OS's <errno.h>, add guarded tests, and #error on unsupported
    platforms.
---
 lib/swoc/src/bw_format.cc             | 155 +++++++++++++++++-----------------
 lib/swoc/unit_tests/test_bw_format.cc |  35 ++++++++
 2 files changed, 111 insertions(+), 79 deletions(-)

diff --git a/lib/swoc/src/bw_format.cc b/lib/swoc/src/bw_format.cc
index 5b87fc9909..28a2348d4d 100644
--- a/lib/swoc/src/bw_format.cc
+++ b/lib/swoc/src/bw_format.cc
@@ -743,83 +743,79 @@ FixedBufferWriter::operator>>(std::ostream &s) const {
 }
 
 namespace {
-// Hand rolled, might not be totally compliant everywhere, but probably close
-// enough. The long string will be locally accurate. Clang requires the double
-// braces. Why, Turing only knows.
-static const std::array<std::string_view, 134> ERRNO_SHORT_NAME = {
-  {
-   "SUCCESS", "EPERM",
-   "ENOENT", "ESRCH",
-   "EINTR", "EIO",
-   "ENXIO", "E2BIG ",
-   "ENOEXEC", "EBADF",
-   "ECHILD", "EAGAIN",
-   "ENOMEM", "EACCES",
-   "EFAULT", "ENOTBLK",
-   "EBUSY", "EEXIST",
-   "EXDEV", "ENODEV",
-   "ENOTDIR", "EISDIR",
-   "EINVAL", "ENFILE",
-   "EMFILE", "ENOTTY",
-   "ETXTBSY", "EFBIG",
-   "ENOSPC", "ESPIPE",
-   "EROFS", "EMLINK",
-   "EPIPE", "EDOM",
-   "ERANGE", "EDEADLK",
-   "ENAMETOOLONG", "ENOLCK",
-   "ENOSYS", "ENOTEMPTY",
-   "ELOOP", "EWOULDBLOCK",
-   "ENOMSG", "EIDRM",
-   "ECHRNG", "EL2NSYNC",
-   "EL3HLT", "EL3RST",
-   "ELNRNG", "EUNATCH",
-   "ENOCSI", "EL2HTL",
-   "EBADE", "EBADR",
-   "EXFULL", "ENOANO",
-   "EBADRQC", "EBADSLT",
-   "EDEADLOCK", "EBFONT",
-   "ENOSTR", "ENODATA",
-   "ETIME", "ENOSR",
-   "ENONET", "ENOPKG",
-   "EREMOTE", "ENOLINK",
-   "EADV", "ESRMNT",
-   "ECOMM", "EPROTO",
-   "EMULTIHOP", "EDOTDOT",
-   "EBADMSG", "EOVERFLOW",
-   "ENOTUNIQ", "EBADFD",
-   "EREMCHG", "ELIBACC",
-   "ELIBBAD", "ELIBSCN",
-   "ELIBMAX", "ELIBEXEC",
-   "EILSEQ", "ERESTART",
-   "ESTRPIPE", "EUSERS",
-   "ENOTSOCK", "EDESTADDRREQ",
-   "EMSGSIZE", "EPROTOTYPE",
-   "ENOPROTOOPT", "EPROTONOSUPPORT",
-   "ESOCKTNOSUPPORT", "EOPNOTSUPP",
-   "EPFNOSUPPORT", "EAFNOSUPPORT",
-   "EADDRINUSE", "EADDRNOTAVAIL",
-   "ENETDOWN", "ENETUNREACH",
-   "ENETRESET", "ECONNABORTED",
-   "ECONNRESET", "ENOBUFS",
-   "EISCONN", "ENOTCONN",
-   "ESHUTDOWN", "ETOOMANYREFS",
-   "ETIMEDOUT", "ECONNREFUSED",
-   "EHOSTDOWN", "EHOSTUNREACH",
-   "EALREADY", "EINPROGRESS",
-   "ESTALE", "EUCLEAN",
-   "ENOTNAM", "ENAVAIL",
-   "EISNAM", "EREMOTEIO",
-   "EDQUOT", "ENOMEDIUM",
-   "EMEDIUMTYPE", "ECANCELED",
-   "ENOKEY", "EKEYEXPIRED",
-   "EKEYREVOKED", "EKEYREJECTED",
-   "EOWNERDEAD", "ENOTRECOVERABLE",
-   "ERFKILL", "EHWPOISON",
-   }
-};
-static constexpr DiscreteRange<unsigned> ERRNO_RANGE{0, 
ERRNO_SHORT_NAME.size() - 1};
-// This provides convenient safe access to the errno short name array.
-auto errno_short_name = [](unsigned n) { return ERRNO_RANGE.contains(n) ? 
ERRNO_SHORT_NAME[n] : "Unknown"sv; };
+// errno value -> symbolic name, indexed directly by the errno number (O(1)).
+// The numbering differs between Linux and the BSD-derived platforms, and even
+// macOS and FreeBSD disagree on a few of their own high-numbered codes, so 
each
+// platform carries a table generated from its real <errno.h>. Gaps in the
+// numbering are empty and fall through to "Unknown"; a platform with no table
+// is a compile error. The long form (the 'l' spec, below) still uses 
strerror().
+// clang-format off
+#if defined(__linux__)
+static constexpr std::array<std::string_view, 134> ERRNO_NAMES = {{
+  /*   0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", 
"E2BIG",
+  /*   8 */ "ENOEXEC", "EBADF", "ECHILD", "EAGAIN", "ENOMEM", "EACCES", 
"EFAULT", "ENOTBLK",
+  /*  16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", 
"EINVAL", "ENFILE",
+  /*  24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", 
"EROFS", "EMLINK",
+  /*  32 */ "EPIPE", "EDOM", "ERANGE", "EDEADLK", "ENAMETOOLONG", "ENOLCK", 
"ENOSYS", "ENOTEMPTY",
+  /*  40 */ "ELOOP", "", "ENOMSG", "EIDRM", "ECHRNG", "EL2NSYNC", "EL3HLT", 
"EL3RST",
+  /*  48 */ "ELNRNG", "EUNATCH", "ENOCSI", "EL2HLT", "EBADE", "EBADR", 
"EXFULL", "ENOANO",
+  /*  56 */ "EBADRQC", "EBADSLT", "", "EBFONT", "ENOSTR", "ENODATA", "ETIME", 
"ENOSR",
+  /*  64 */ "ENONET", "ENOPKG", "EREMOTE", "ENOLINK", "EADV", "ESRMNT", 
"ECOMM", "EPROTO",
+  /*  72 */ "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW", "ENOTUNIQ", 
"EBADFD", "EREMCHG", "ELIBACC",
+  /*  80 */ "ELIBBAD", "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART", 
"ESTRPIPE", "EUSERS",
+  /*  88 */ "ENOTSOCK", "EDESTADDRREQ", "EMSGSIZE", "EPROTOTYPE", 
"ENOPROTOOPT", "EPROTONOSUPPORT", "ESOCKTNOSUPPORT", "EOPNOTSUPP",
+  /*  96 */ "EPFNOSUPPORT", "EAFNOSUPPORT", "EADDRINUSE", "EADDRNOTAVAIL", 
"ENETDOWN", "ENETUNREACH", "ENETRESET", "ECONNABORTED",
+  /* 104 */ "ECONNRESET", "ENOBUFS", "EISCONN", "ENOTCONN", "ESHUTDOWN", 
"ETOOMANYREFS", "ETIMEDOUT", "ECONNREFUSED",
+  /* 112 */ "EHOSTDOWN", "EHOSTUNREACH", "EALREADY", "EINPROGRESS", "ESTALE", 
"EUCLEAN", "ENOTNAM", "ENAVAIL",
+  /* 120 */ "EISNAM", "EREMOTEIO", "EDQUOT", "ENOMEDIUM", "EMEDIUMTYPE", 
"ECANCELED", "ENOKEY", "EKEYEXPIRED",
+  /* 128 */ "EKEYREVOKED", "EKEYREJECTED", "EOWNERDEAD", "ENOTRECOVERABLE", 
"ERFKILL", "EHWPOISON",
+}};
+#elif defined(__APPLE__)
+static constexpr std::array<std::string_view, 108> ERRNO_NAMES = {{
+  /*   0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", 
"E2BIG",
+  /*   8 */ "ENOEXEC", "EBADF", "ECHILD", "EDEADLK", "ENOMEM", "EACCES", 
"EFAULT", "ENOTBLK",
+  /*  16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", 
"EINVAL", "ENFILE",
+  /*  24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", 
"EROFS", "EMLINK",
+  /*  32 */ "EPIPE", "EDOM", "ERANGE", "EAGAIN", "EINPROGRESS", "EALREADY", 
"ENOTSOCK", "EDESTADDRREQ",
+  /*  40 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT", 
"ESOCKTNOSUPPORT", "ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT",
+  /*  48 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH", 
"ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
+  /*  56 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT", 
"ECONNREFUSED", "ELOOP", "ENAMETOOLONG",
+  /*  64 */ "EHOSTDOWN", "EHOSTUNREACH", "ENOTEMPTY", "EPROCLIM", "EUSERS", 
"EDQUOT", "ESTALE", "EREMOTE",
+  /*  72 */ "EBADRPC", "ERPCMISMATCH", "EPROGUNAVAIL", "EPROGMISMATCH", 
"EPROCUNAVAIL", "ENOLCK", "ENOSYS", "EFTYPE",
+  /*  80 */ "EAUTH", "ENEEDAUTH", "EPWROFF", "EDEVERR", "EOVERFLOW", 
"EBADEXEC", "EBADARCH", "ESHLIBVERS",
+  /*  88 */ "EBADMACHO", "ECANCELED", "EIDRM", "ENOMSG", "EILSEQ", "ENOATTR", 
"EBADMSG", "EMULTIHOP",
+  /*  96 */ "ENODATA", "ENOLINK", "ENOSR", "ENOSTR", "EPROTO", "ETIME", 
"EOPNOTSUPP", "ENOPOLICY",
+  /* 104 */ "ENOTRECOVERABLE", "EOWNERDEAD", "EQFULL", "ENOTCAPABLE",
+}};
+#elif defined(__FreeBSD__)
+static constexpr std::array<std::string_view, 98> ERRNO_NAMES = {{
+  /*   0 */ "SUCCESS", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", 
"E2BIG",
+  /*   8 */ "ENOEXEC", "EBADF", "ECHILD", "EDEADLK", "ENOMEM", "EACCES", 
"EFAULT", "ENOTBLK",
+  /*  16 */ "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", 
"EINVAL", "ENFILE",
+  /*  24 */ "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", 
"EROFS", "EMLINK",
+  /*  32 */ "EPIPE", "EDOM", "ERANGE", "EAGAIN", "EINPROGRESS", "EALREADY", 
"ENOTSOCK", "EDESTADDRREQ",
+  /*  40 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT", 
"ESOCKTNOSUPPORT", "EOPNOTSUPP", "EPFNOSUPPORT", "EAFNOSUPPORT",
+  /*  48 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH", 
"ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS",
+  /*  56 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT", 
"ECONNREFUSED", "ELOOP", "ENAMETOOLONG",
+  /*  64 */ "EHOSTDOWN", "EHOSTUNREACH", "ENOTEMPTY", "EPROCLIM", "EUSERS", 
"EDQUOT", "ESTALE", "EREMOTE",
+  /*  72 */ "EBADRPC", "ERPCMISMATCH", "EPROGUNAVAIL", "EPROGMISMATCH", 
"EPROCUNAVAIL", "ENOLCK", "ENOSYS", "EFTYPE",
+  /*  80 */ "EAUTH", "ENEEDAUTH", "EIDRM", "ENOMSG", "EOVERFLOW", "ECANCELED", 
"EILSEQ", "ENOATTR",
+  /*  88 */ "EDOOFUS", "EBADMSG", "EMULTIHOP", "ENOLINK", "EPROTO", 
"ENOTCAPABLE", "ECAPMODE", "ENOTRECOVERABLE",
+  /*  96 */ "EOWNERDEAD", "EINTEGRITY",
+}};
+#else
+#error "errno name table not defined for this platform"
+#endif
+// clang-format on
+
+std::string_view
+errno_short_name(int e)
+{
+  if (e >= 0 && e < static_cast<int>(ERRNO_NAMES.size()) && 
!ERRNO_NAMES[e].empty()) {
+    return ERRNO_NAMES[e];
+  }
+  return "Unknown"sv;
+}
 } // namespace
 
 BufferWriter &
@@ -910,8 +906,9 @@ bwformat(BufferWriter &w, bwf::Spec const &spec, 
std::error_code const &ec) {
   if (spec.has_numeric_type()) {                        // if numeric type, 
print just the numeric part.
     bwformat(w, spec, ec.value());
   } else {
-    if ((&ec.category() == G_CAT || &ec.category() == S_CAT) && 
swoc::ERRNO_RANGE.contains(ec.value())) {
-      bwformat(w, spec, swoc::ERRNO_SHORT_NAME[ec.value()]);
+    auto short_name = (&ec.category() == G_CAT || &ec.category() == S_CAT) ? 
errno_short_name(ec.value()) : "Unknown"sv;
+    if (short_name != "Unknown"sv) {
+      bwformat(w, spec, short_name);
     } else {
       w.write(ec.message());
     }
diff --git a/lib/swoc/unit_tests/test_bw_format.cc 
b/lib/swoc/unit_tests/test_bw_format.cc
index 2cfb99ad2f..caf69a0499 100644
--- a/lib/swoc/unit_tests/test_bw_format.cc
+++ b/lib/swoc/unit_tests/test_bw_format.cc
@@ -8,6 +8,9 @@
 #include <iostream>
 #include <variant>
 #include <cmath>
+#include <cerrno>
+#include <string>
+#include <system_error>
 
 #include <netinet/in.h>
 
@@ -545,6 +548,38 @@ TEST_CASE("bwstring std formats", "[libswoc][bwprint]") {
   w.clear().print("{::l}", swoc::bwf::Errno(13));
   REQUIRE(w.view() == "Permission denied [13]"sv);
 
+  // The symbolic short name must come from the running platform's <errno.h>,
+  // not a fixed Linux-numbered table. Regression guard for the FreeBSD/macOS
+  // mislabel where ETIMEDOUT (errno 60 on those platforms) printed as "ENOSTR"
+  // (apache/trafficserver#13203). Use the macros so each platform checks its
+  // own numbering.
+  w.clear().print("{:s:s}", swoc::bwf::Errno(EPERM));
+  REQUIRE(w.view() == "EPERM"sv);
+  w.clear().print("{:s:s}", swoc::bwf::Errno(ETIMEDOUT));
+  REQUIRE(w.view() == "ETIMEDOUT"sv);
+  w.clear().print("{:s:s}", swoc::bwf::Errno(ECONNREFUSED));
+  REQUIRE(w.view() == "ECONNREFUSED"sv);
+
+  // Direct guards for the codes that were missing from the tables (each one
+  // formatted as "Unknown" before this fix). They are platform-specific, so
+  // each only compiles where it is defined: ENOATTR on macOS/FreeBSD, EL2HLT
+  // on Linux.
+#ifdef ENOATTR
+  w.clear().print("{:s:s}", swoc::bwf::Errno(ENOATTR));
+  REQUIRE(w.view() == "ENOATTR"sv);
+#endif
+#ifdef EL2HLT
+  w.clear().print("{:s:s}", swoc::bwf::Errno(EL2HLT));
+  REQUIRE(w.view() == "EL2HLT"sv);
+#endif
+
+  // The std::error_code formatter routes through the same per-platform table,
+  // rendering "<name> [<value>]". Build the expectation from the macro so the
+  // numeric part matches the running platform's numbering.
+  std::string ec_expected = "ECONNREFUSED [" + std::to_string(ECONNREFUSED) + 
"]";
+  w.clear().print("{}", std::error_code(ECONNREFUSED, 
std::generic_category()));
+  REQUIRE(w.view() == ec_expected);
+
   time_t t = 1528484137;
   // default is GMT
   w.clear().print("{} is {}", t, swoc::bwf::Date(t));

Reply via email to