On Sat, Jul 05, 2025 at 10:33:49PM +0200, Alejandro Colomar wrote: > seprintf() > ========== > > seprintf() is a function similar to stpcpy(3) in the sense that it > returns a pointer that is suitable for chaining to other copy > operations. > > It takes a pointer to the end of the buffer as a sentinel for when to > truncate, which unlike a size, doesn't need to be updated after every > call. This makes it much more ergonomic, avoiding manually calculating > the size after each copy, which is error prone. > > It also makes error handling much easier, by reporting truncation with > a null pointer, which is accepted and transparently passed down by > subsequent seprintf() calls. This results in only needing to report > errors once after a chain of seprintf() calls, unlike snprintf(3), which > requires checking after every call. > > p = buf; > e = buf + countof(buf); > p = seprintf(p, e, foo); > p = seprintf(p, e, bar); > if (p == NULL) > goto trunc; > > vs > > len = 0; > size = countof(buf); > len += snprintf(buf + len, size - len, foo); > if (len >= size) > goto trunc; > > len += snprintf(buf + len, size - len, bar); > if (len >= size) > goto trunc; > > And also better than scnprintf() calls: > > len = 0; > size = countof(buf); > len += scnprintf(buf + len, size - len, foo); > len += scnprintf(buf + len, size - len, bar); > if (len >= size) > goto trunc;
Oops, this error handling is incorrect, as scnprintf() doesn't report
truncation. I should have compared
p = buf;
e = buf + countof(buf);
p = seprintf(p, e, foo);
p = seprintf(p, e, bar);
vs
len = 0;
size = countof(buf);
len += scnprintf(buf + len, size - len, foo);
len += scnprintf(buf + len, size - len, bar);
>
> It seems aparent that it's a more elegant approach to string catenation.
>
> stprintf()
> ==========
>
> stprintf() is a helper that is needed for implementing seprintf()
> --although it could be open-coded within vseprintf(), of course--, but
> it's also useful by itself. It has the same interface properties as
> strscpy(): that is, it copies with truncation, and reports truncation
> with -E2BIG. It would be useful to replace some calls to snprintf(3)
> and scnprintf() which don't need chaining, and where it's simpler to
> pass a size.
>
> It is better than plain snprintf(3), because it results in simpler error
> detection (it doesn't need a check >=countof(buf), but rather <0).
>
> Cc: Kees Cook <[email protected]>
> Cc: Christopher Bazley <[email protected]>
> Signed-off-by: Alejandro Colomar <[email protected]>
> ---
> lib/vsprintf.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 109 insertions(+)
>
> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index 01699852f30c..a3efacadb5e5 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -2892,6 +2892,37 @@ int vsnprintf(char *buf, size_t size, const char
> *fmt_str, va_list args)
> }
> EXPORT_SYMBOL(vsnprintf);
>
> +/**
> + * vstprintf - Format a string and place it in a buffer
> + * @buf: The buffer to place the result into
> + * @size: The size of the buffer, including the trailing null space
> + * @fmt: The format string to use
> + * @args: Arguments for the format string
> + *
> + * The return value is the length of the new string.
> + * If the string is truncated, the function returns -E2BIG.
> + *
> + * If you're not already dealing with a va_list consider using stprintf().
> + *
> + * See the vsnprintf() documentation for format string extensions over C99.
> + */
> +int vstprintf(char *buf, size_t size, const char *fmt, va_list args)
> +{
> + int len;
> +
> + len = vsnprintf(buf, size, fmt, args);
> +
> + // It seems the kernel's vsnprintf() doesn't fail?
> + //if (unlikely(len < 0))
> + // return -E2BIG;
> +
> + if (unlikely(len >= size))
> + return -E2BIG;
> +
> + return len;
> +}
> +EXPORT_SYMBOL(vstprintf);
> +
> /**
> * vscnprintf - Format a string and place it in a buffer
> * @buf: The buffer to place the result into
> @@ -2923,6 +2954,36 @@ int vscnprintf(char *buf, size_t size, const char
> *fmt, va_list args)
> }
> EXPORT_SYMBOL(vscnprintf);
>
> +/**
> + * vseprintf - Format a string and place it in a buffer
> + * @p: The buffer to place the result into
> + * @end: A pointer to one past the last character in the buffer
> + * @fmt: The format string to use
> + * @args: Arguments for the format string
> + *
> + * The return value is a pointer to the trailing '\0'.
> + * If @p is NULL, the function returns NULL.
> + * If the string is truncated, the function returns NULL.
> + *
> + * If you're not already dealing with a va_list consider using seprintf().
> + *
> + * See the vsnprintf() documentation for format string extensions over C99.
> + */
> +char *vseprintf(char *p, const char end[0], const char *fmt, va_list args)
> +{
> + int len;
> +
> + if (unlikely(p == NULL))
> + return NULL;
> +
> + len = vstprintf(p, end - p, fmt, args);
> + if (unlikely(len < 0))
> + return NULL;
> +
> + return p + len;
> +}
> +EXPORT_SYMBOL(vseprintf);
> +
> /**
> * snprintf - Format a string and place it in a buffer
> * @buf: The buffer to place the result into
> @@ -2950,6 +3011,30 @@ int snprintf(char *buf, size_t size, const char *fmt,
> ...)
> }
> EXPORT_SYMBOL(snprintf);
>
> +/**
> + * stprintf - Format a string and place it in a buffer
> + * @buf: The buffer to place the result into
> + * @size: The size of the buffer, including the trailing null space
> + * @fmt: The format string to use
> + * @...: Arguments for the format string
> + *
> + * The return value is the length of the new string.
> + * If the string is truncated, the function returns -E2BIG.
> + */
> +
> +int stprintf(char *buf, size_t size, const char *fmt, ...)
> +{
> + va_list args;
> + int len;
> +
> + va_start(args, fmt);
> + len = vstprintf(buf, size, fmt, args);
> + va_end(args);
> +
> + return len;
> +}
> +EXPORT_SYMBOL(stprintf);
> +
> /**
> * scnprintf - Format a string and place it in a buffer
> * @buf: The buffer to place the result into
> @@ -2974,6 +3059,30 @@ int scnprintf(char *buf, size_t size, const char *fmt,
> ...)
> }
> EXPORT_SYMBOL(scnprintf);
>
> +/**
> + * seprintf - Format a string and place it in a buffer
> + * @p: The buffer to place the result into
> + * @end: A pointer to one past the last character in the buffer
> + * @fmt: The format string to use
> + * @...: Arguments for the format string
> + *
> + * The return value is a pointer to the trailing '\0'.
> + * If @buf is NULL, the function returns NULL.
> + * If the string is truncated, the function returns NULL.
> + */
> +
> +char *seprintf(char *p, const char end[0], const char *fmt, ...)
> +{
> + va_list args;
> +
> + va_start(args, fmt);
> + p = vseprintf(p, end, fmt, args);
> + va_end(args);
> +
> + return p;
> +}
> +EXPORT_SYMBOL(seprintf);
> +
> /**
> * vsprintf - Format a string and place it in a buffer
> * @buf: The buffer to place the result into
> --
> 2.50.0
>
--
<https://www.alejandro-colomar.es/>
signature.asc
Description: PGP signature
