runtime: add the ability to set exterrors in userspace

The UEXTERROR(3) macro is a partial analog to EXTERROR(9) that sets
the current user exterror state and errno.  The main difference is
that it returns no value and sets errno directly since that's the
typical pattern in libraries.

While here move the storage and constructor for single-threaded
program's uexterr to its own file.

Reviewed by:	kib
Effort:		CHERI upstreaming
Sponsored by:	Innovate UK
Differential Revision:	https://reviews.freebsd.org/D58059
This commit is contained in:
Brooks Davis committed 2026-08-20 11:39:00 +01:00
1 parent 2f024a7cfd
commit 95e33099c7
12 files changed
+304 -19

No files matched your search

+2 -1
View File
@@ -33,7 +33,8 @@ INCS= a.out.h ar.h assert.h bitstring.h byteswap.h \
stddef.h stdnoreturn.h stdio.h stdlib.h string.h stringlist.h \
strings.h sysexits.h tar.h termios.h tgmath.h \
time.h timeconv.h timers.h ttyent.h \
uchar.h ulimit.h unistd.h utime.h utmpx.h uuid.h varargs.h \
uchar.h uexterror.h ulimit.h unistd.h utime.h utmpx.h uuid.h \
varargs.h \
wchar.h wctype.h wordexp.h xlocale.h
.PATH: ${SRCTOP}/contrib/libc-vis
+65
View File
@@ -0,0 +1,65 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
* Copyright (c) 2026 Capabilities Limited
* All rights reserved.
*
* This software were developed by Konstantin Belousov <kib@FreeBSD.org>
* under sponsorship from the FreeBSD Foundation.
*
* This software was developed by Capabilities Limited with funding from
* Innovate UK and the Department for Science, Innovation and Technology
* for the adoption and diffusion of CHERI technology under project
* 10168042 (“CheriBSD feature extraction, maturity, and testing”).
*/
#ifndef _UEXTERROR_H_
#define _UEXTERROR_H_
#include <sys/types.h>
#include <sys/linker_set.h>
struct exterr_cat {
unsigned int cat;
const char *file;
};
#ifndef UEXTERR_CATEGORY
#error "Specify error category before including uexterror.h"
#endif
#ifndef NO_UEXTERR_STRINGS
static struct exterr_cat __dynamic_cat = { .file = UEXTERR_CATEGORY };
DATA_WSET(exterr_cats, __dynamic_cat);
#define _UEXTERR_CATEGORY \
(__dynamic_cat.cat | EXTERR_CAT_SRC_USER)
#else
#define _UEXTERR_CATEGORY (EXTERR_CAT_NONE | EXTERR_CAT_SRC_USER)
#endif
#ifdef NO_UEXTERR_STRINGS
#define SET_ERROR_MSG(mmsg) NULL
#else
#define SET_ERROR_MSG(mmsg) (mmsg)
#endif
#define _SET_ERROR2(eerror, mmsg, pp1, pp2) \
uexterr_set(eerror, _UEXTERR_CATEGORY, SET_ERROR_MSG(mmsg), \
(uint64ptr_t)(pp1), (uint64ptr_t)(pp2), __LINE__)
#define _SET_ERROR0(eerror, mmsg) _SET_ERROR2(eerror, mmsg, 0, 0)
#define _SET_ERROR1(eerror, mmsg, pp1) _SET_ERROR2(eerror, mmsg, pp1, 0)
#define _UEXTERROR_MACRO(eerror, mmsg, _1, _2, NAME, ...) \
NAME
#define UEXTERROR(...) \
_UEXTERROR_MACRO(__VA_ARGS__, _SET_ERROR2, _SET_ERROR1, \
_SET_ERROR0)(__VA_ARGS__)
__BEGIN_DECLS
void uexterr_set(int error, int category, const char *mmsg, uint64ptr_t pp1,
uint64ptr_t pp2, int line);
__END_DECLS
#endif
+3
View File
@@ -165,6 +165,8 @@ SRCS+= \
ualarm.c \
uexterr_format.c \
uexterr_gettext.c \
uexterr_init.c \
uexterr_set.c \
ulimit.c \
uname.c \
unvis-compat.c \
@@ -325,6 +327,7 @@ MAN+= alarm.3 \
ttyname.3 \
ualarm.3 \
ucontext.3 \
uexterr.3 \
uexterr_gettext.3 \
ulimit.3 \
uname.3 \
+2
View File
@@ -483,6 +483,7 @@ FBSD_1.9 {
posix_spawnattr_getprocdescp_np;
posix_spawnattr_setexecfd_np;
posix_spawnattr_setprocdescp_np;
uexterr_set;
};
FBSDprivate_1.0 {
@@ -613,4 +614,5 @@ FBSDprivate_1.0 {
__getcontextx_size;
__makecontext;
__uexterr_format;
__uexterr_set_ue;
};
+1
View File
@@ -43,6 +43,7 @@ interpos_func_t __libc_interposing[INTERPOS_MAX] = {
SLOT(spinunlock, __libc_spinunlock_stub),
SLOT(map_stacks_exec, __libc_map_stacks_exec),
SLOT(uexterr_gettext, __libc_uexterr_gettext),
SLOT(uexterr_set, __libc_uexterr_set),
};
#undef SLOT
-18
View File
@@ -8,27 +8,9 @@
* under sponsorship from the FreeBSD Foundation.
*/
#define _WANT_P_OSREL
#include <sys/param.h>
#include <sys/exterrvar.h>
#include <exterr.h>
#include <string.h>
#include "libc_private.h"
static struct uexterror uexterr = {
.ver = UEXTERROR_VER,
};
int __getosreldate(void);
static void uexterr_ctr(void) __attribute__((constructor));
static void
uexterr_ctr(void)
{
if (__getosreldate() >= P_OSREL_EXTERRCTL)
exterrctl(EXTERRCTL_ENABLE, 0, &uexterr);
}
int
__libc_uexterr_gettext(char *buf, size_t bufsz)
{
+26
View File
@@ -0,0 +1,26 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
* All rights reserved.
*
* This software were developed by Konstantin Belousov <kib@FreeBSD.org>
* under sponsorship from the FreeBSD Foundation.
*/
#define _WANT_P_OSREL
#include <sys/param.h>
#include <sys/exterrvar.h>
#include "libc_private.h"
struct uexterror uexterr = {
.ver = UEXTERROR_VER,
};
static void uexterr_ctr(void) __attribute__((constructor));
static void
uexterr_ctr(void)
{
if (__getosreldate() >= P_OSREL_EXTERRCTL)
exterrctl(EXTERRCTL_ENABLE, 0, &uexterr);
}
+50
View File
@@ -0,0 +1,50 @@
/*-
* Copyright (c) 2026 Capabilities Limited
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This software was developed by Capabilities Limited with funding from
* Innovate UK and the Department for Science, Innovation and Technology
* for the adoption and diffusion of CHERI technology under project
* 10168042 (“CheriBSD feature extraction, maturity, and testing”).
*/
#include <sys/exterrvar.h>
#include <errno.h>
#include <exterr.h>
#include <string.h>
#include "libc_private.h"
void
__uexterr_set_ue(struct uexterror *ue, int error, int category,
const char *mmsg, uint64ptr_t pp1, uint64ptr_t pp2, int line)
{
memset((char *)ue + offsetof(struct uexterror, error), 0,
sizeof(struct uexterror) - offsetof(struct uexterror, error));
ue->error = error;
ue->cat = category | EXTERR_CAT_SRC_USER;
ue->src_line = line;
ue->p1 = pp1;
ue->p2 = pp2;
if (mmsg != NULL)
strlcpy(ue->msg, mmsg, sizeof(ue->msg));
else
ue->msg[0] = '\0';
errno = error;
}
void
__libc_uexterr_set(int error, int category, const char *mmsg, uint64ptr_t pp1,
uint64ptr_t pp2, int line)
{
__uexterr_set_ue(&uexterr, error, category, mmsg, pp1, pp2, line);
}
void
uexterr_set(int error, int category, const char *mmsg, uint64ptr_t pp1,
uint64ptr_t pp2, int line)
{
((void (*)(int, int, const char *, uint64ptr_t, uint64ptr_t, int))
__libc_interposing[INTERPOS_uexterr_set])(error, category,
mmsg, pp1, pp2, line);
}
+135
View File
@@ -0,0 +1,135 @@
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
.\" Copyright (c) 2025 The FreeBSD Foundation
.\" Copyright (c) 2026 Capabilities Limited
.\"
.\" This documentation was written by
.\" Konstantin Belousov <[email protected]> under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" This software was developed by Capabilities Limited with funding from
.\" Innovate UK and the Department for Science, Innovation and Technology
.\" for the adoption and diffusion of CHERI technology under project
.\" 10168042 (“CheriBSD feature extraction, maturity, and testing”).
.\"
.Dd August 20, 2026
.Dt UEXTERROR 3
.Os
.Sh NAME
.Nm uexterror
.Nd provide extended error information from user-space code
.Sh SYNOPSIS
.Bd -literal -offset left -compact
#define UEXTERR_CATEGORY "path/to/this/file.c"
.Ed
.In uexterror.h
.Ft void
.Fn UEXTERROR "int error" "const char *msg" ...
.Sh DESCRIPTION
The
.Nm
framework allows user-space code to report errors using the
same
.Xr exterror 9
infrastructure the kernel uses to return additional information about an
error along with the standard
.Xr errno 3
error code, which is terse and often lacking context.
.Pp
The terseness is especially visible with commonly overloaded error codes like
.Er EINVAL
or
.Er EIO ,
which occur at many places for a given syscall, or even
outside the context of the current call.
Identifying the specific cause for the returned error using only the
.Va errno
value requires searching for all instances that the error is returned
in user-space and the kernel and trying to guess which is the most
likely code path to have returned the error.
.Nm
attaches additional data to the error itself
and records the error category and
the source code file line number.
The intent of
.Nm
is to make it easier for a user to identify the cause of the error.
.Sh USAGE
Before
.Nm
can be used in the given source .c file, the category of extended errors
must be defined.
This can be done by setting the
.Va UEXTERR_CATEGORY
macro to a string containing the name of the file relative to
.Pa src/sys
directory.
.Pp
A typical code fragment to report an error is to set errno
.D1 errno = EINVAL;
An extended error can augment the error code with additional information:
.D1 UEXTERROR(EINVAL, \[dq]Invalid length\[dq]));
The error data and metadata is saved in the current thread storage.
The metadata includes the category and the source file line number.
.Pp
Arguments to the
.Fn UEXTERROR
macro:
.Bl -dash
.It
The first argument to
.Fn UEXTERROR
is the errno error code.
.It
The second argument is a constant string with the unbound lifetime,
which should tersely provide enough human-readable details about
the error.
.It
The
.Fn UEXTERROR
macro can take two optional uintptr_t or 64-bit integer arguments,
whose meaning is specific to the subsystem.
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 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
Note that unlike its kernel counterpart
.Xr EXTERROR 9
the
.Nm
macro does not return a value and sets
.Xr errno 3
directly.
.Pp
The name of the file and strings passed as the second argument are
retained in the program or library text as long as the translation unit
was not compiled with the
.Cd NO_UEXTERR_STRINGS
macro defined.
.Sh SEE ALSO
.Xr errno 3 ,
.Xr err 3 ,
.Xr uexterr_gettext 3 ,
.Xr exterror 9
.Sh HISTORY
The
.Nm
facility was introduced in
.Fx 16.0 .
.Sh AUTHORS
This software and this manual page were developed by Capabilities
Limited with funding from Innovate UK and the Department for Science,
Innovation and Technology for the adoption and diffusion of CHERI
technology under project 10168042
.Pq Do CheriBSD feature extraction, maturity, and testing Dc .
+6
View File
@@ -253,6 +253,7 @@ enum {
INTERPOS_pdfork,
INTERPOS_uexterr_gettext,
INTERPOS_pdwait,
INTERPOS_uexterr_set,
INTERPOS_MAX
};
@@ -381,7 +382,12 @@ int __strerror_rl(int errnum, char *strerrbuf, size_t buflen,
struct _xlocale *locale);
struct uexterror;
extern struct uexterror uexterr;
int __uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz);
void __uexterr_set_ue(struct uexterror *ue, int error, int category,
const char *mmsg, uint64ptr_t pp1, uint64ptr_t pp2, int line);
int __libc_uexterr_gettext(char *buf, size_t bufsz);
void __libc_uexterr_set(int eerror, int category, const char *mmsg,
uint64ptr_t pp1, uint64ptr_t pp2, int line);
#endif /* _LIBC_PRIVATE_H_ */
+13
View File
@@ -643,6 +643,18 @@ __thr_uexterr_gettext(char *buf, size_t bufsz)
return (__uexterr_format(&curthread->uexterr, buf, bufsz));
}
static void
__thr_uexterr_set(int error, int category, const char *mmsg, uint64ptr_t pp1,
uint64ptr_t pp2, int line)
{
struct pthread *curthread;
curthread = _get_curthread();
__uexterr_set_ue(&curthread->uexterr, error, category, mmsg,
pp1, pp2, line);
}
void
__thr_interpose_libc(void)
{
@@ -700,6 +712,7 @@ __thr_interpose_libc(void)
SLOT(pdfork);
SLOT(uexterr_gettext);
SLOT(pdwait);
SLOT(uexterr_set);
#undef SLOT
*(__libc_interposing_slot(
INTERPOS__pthread_mutex_init_calloc_cb)) =
+1
View File
@@ -244,6 +244,7 @@ from the failed request back to the thread that create the request.
.Xr errno 3 ,
.Xr err 3 ,
.Xr uexterr_gettext 3
.Xr uexterr 3
.Sh HISTORY
The
.Nm