From: Alejandro Colomar <alx@kernel.org>
To: linux-mm@kvack.org, linux-hardening@vger.kernel.org
Cc: Kees Cook <kees@kernel.org>,
Christopher Bazley <chris.bazley.wg14@gmail.com>,
shadow <~hallyn/shadow@lists.sr.ht>,
linux-kernel@vger.kernel.org,
Andrew Morton <akpm@linux-foundation.org>,
kasan-dev@googlegroups.com, Dmitry Vyukov <dvyukov@google.com>,
Alexander Potapenko <glider@google.com>,
Marco Elver <elver@google.com>, Christoph Lameter <cl@linux.com>,
David Rientjes <rientjes@google.com>,
Vlastimil Babka <vbabka@suse.cz>,
Roman Gushchin <roman.gushchin@linux.dev>,
Harry Yoo <harry.yoo@oracle.com>
Subject: Re: [RFC v1 1/3] vsprintf: Add [v]seprintf(), [v]stprintf()
Date: Sat, 5 Jul 2025 22:40:49 +0200 [thread overview]
Message-ID: <nx6vj5qqcgkts4pmefzux3ee4kuumwyjh6vlwsdltf56ayq33e@kyf25zkic2rk> (raw)
In-Reply-To: <2d20eaf1752efefcc23d0e7c5c2311dd5ae252af.1751747518.git.alx@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 7259 bytes --]
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 <kees@kernel.org>
> Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
> 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/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next prev parent reply other threads:[~2025-07-05 20:40 UTC|newest]
Thread overview: 98+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-05 20:33 [RFC v1 0/3] Add and use seprintf() instead of less ergonomic APIs Alejandro Colomar
2025-07-05 20:33 ` [RFC v1 1/3] vsprintf: Add [v]seprintf(), [v]stprintf() Alejandro Colomar
2025-07-05 20:40 ` Alejandro Colomar [this message]
2025-07-07 9:47 ` Alexander Potapenko
2025-07-07 14:59 ` Alejandro Colomar
2025-07-05 20:33 ` [RFC v1 2/3] stacktrace, stackdepot: Add seprintf()-like variants of functions Alejandro Colomar
2025-07-05 20:33 ` [RFC v1 3/3] mm: Use seprintf() instead of less ergonomic APIs Alejandro Colomar
2025-07-05 21:54 ` Alejandro Colomar
2025-07-06 17:37 ` [RFC v2 0/5] Add and use " Alejandro Colomar
2025-07-06 17:37 ` [RFC v2 1/5] vsprintf: Add [v]seprintf(), [v]stprintf() Alejandro Colomar
2025-07-06 17:37 ` [RFC v2 2/5] stacktrace, stackdepot: Add seprintf()-like variants of functions Alejandro Colomar
2025-07-06 17:37 ` [RFC v2 3/5] mm: Use seprintf() instead of less ergonomic APIs Alejandro Colomar
2025-07-06 17:37 ` [RFC v2 4/5] array_size.h: Add ENDOF() Alejandro Colomar
2025-07-06 17:37 ` [RFC v2 5/5] mm: Fix benign off-by-one bugs Alejandro Colomar
2025-07-07 5:06 ` [RFC v3 0/7] Add and use seprintf() instead of less ergonomic APIs Alejandro Colomar
2025-07-07 5:06 ` [RFC v3 1/7] vsprintf: Add [v]seprintf(), [v]stprintf() Alejandro Colomar
2025-07-07 5:06 ` [RFC v3 2/7] stacktrace, stackdepot: Add seprintf()-like variants of functions Alejandro Colomar
2025-07-07 5:06 ` [RFC v3 3/7] mm: Use seprintf() instead of less ergonomic APIs Alejandro Colomar
2025-07-07 7:44 ` Marco Elver
2025-07-07 14:39 ` Alejandro Colomar
2025-07-07 14:58 ` Marco Elver
2025-07-07 18:51 ` Alejandro Colomar
2025-07-07 19:08 ` Marco Elver
2025-07-07 20:53 ` Alejandro Colomar
2025-07-07 19:17 ` Linus Torvalds
2025-07-07 19:35 ` Al Viro
2025-07-07 20:46 ` Linus Torvalds
2025-07-07 20:29 ` Alejandro Colomar
2025-07-07 20:49 ` Linus Torvalds
2025-07-07 21:05 ` Alejandro Colomar
2025-07-07 21:26 ` Alejandro Colomar
2025-07-07 22:17 ` Linus Torvalds
2025-07-08 2:20 ` Alejandro Colomar
2025-07-12 20:58 ` Christopher Bazley
2025-07-14 7:57 ` Christopher Bazley
2025-07-07 5:06 ` [RFC v3 4/7] array_size.h: Add ENDOF() Alejandro Colomar
2025-07-07 5:06 ` [RFC v3 5/7] mm: Fix benign off-by-one bugs Alejandro Colomar
2025-07-07 7:46 ` Marco Elver
2025-07-07 7:53 ` Michal Hocko
2025-07-07 14:42 ` Alejandro Colomar
2025-07-07 15:12 ` Michal Hocko
2025-07-07 15:29 ` Alejandro Colomar
2025-07-07 5:06 ` [RFC v3 6/7] sprintf: Add [V]STPRINTF() Alejandro Colomar
2025-07-07 5:06 ` [RFC v3 7/7] mm: Use [V]STPRINTF() to avoid specifying the array size Alejandro Colomar
2025-07-07 5:11 ` [RFC v3 0/7] Add and use seprintf() instead of less ergonomic APIs Alejandro Colomar
2025-07-10 2:47 ` [RFC v4 0/7] Add and use sprintf_end() " Alejandro Colomar
2025-07-10 2:47 ` alx-0049r2 - add seprintf() Alejandro Colomar
2025-07-10 2:48 ` [RFC v4 1/7] vsprintf: Add [v]sprintf_end() Alejandro Colomar
2025-07-10 2:48 ` [RFC v4 2/7] stacktrace, stackdepot: Add sprintf_end()-like variants of functions Alejandro Colomar
2025-07-10 2:48 ` [RFC v4 3/7] mm: Use sprintf_end() instead of less ergonomic APIs Alejandro Colomar
2025-07-10 2:48 ` [RFC v4 4/7] array_size.h: Add ENDOF() Alejandro Colomar
2025-07-10 2:48 ` [RFC v4 5/7] mm: Fix benign off-by-one bugs Alejandro Colomar
2025-07-10 2:48 ` [RFC v4 6/7] sprintf: Add [V]SPRINTF_END() Alejandro Colomar
2025-07-10 15:52 ` Linus Torvalds
2025-07-10 18:30 ` Alejandro Colomar
2025-07-10 21:21 ` Alejandro Colomar
2025-07-10 22:08 ` Linus Torvalds
2025-07-10 2:49 ` [RFC v4 7/7] mm: Use [V]SPRINTF_END() to avoid specifying the array size Alejandro Colomar
2025-07-10 21:30 ` [RFC v5 0/7] Add and use sprintf_{end,array}() instead of less ergonomic APIs Alejandro Colomar
2025-07-10 21:30 ` [RFC v5 1/7] vsprintf: Add [v]sprintf_end() Alejandro Colomar
2025-07-10 21:30 ` [RFC v5 2/7] stacktrace, stackdepot: Add sprintf_end()-like variants of functions Alejandro Colomar
2025-07-10 21:30 ` [RFC v5 3/7] mm: Use sprintf_end() instead of less ergonomic APIs Alejandro Colomar
2025-07-10 21:31 ` [RFC v5 4/7] array_size.h: Add ENDOF() Alejandro Colomar
2025-07-10 21:31 ` [RFC v5 5/7] mm: Fix benign off-by-one bugs Alejandro Colomar
2025-07-10 21:31 ` [RFC v5 6/7] sprintf: Add [v]sprintf_array() Alejandro Colomar
2025-07-10 21:58 ` Linus Torvalds
2025-07-10 23:23 ` Alejandro Colomar
2025-07-10 23:24 ` Alejandro Colomar
2025-07-11 0:19 ` Alejandro Colomar
2025-07-11 17:43 ` David Laight
2025-07-11 19:17 ` Alejandro Colomar
2025-07-11 19:21 ` Alejandro Colomar
2025-07-11 6:05 ` Martin Uecker
2025-07-11 6:19 ` Martin Uecker
2025-07-11 17:45 ` David Laight
2025-07-11 17:58 ` Linus Torvalds
2025-07-11 19:24 ` Matthew Wilcox
2025-07-15 5:19 ` Kees Cook
2025-07-15 6:24 ` Martin Uecker
2025-07-17 23:44 ` Kees Cook
2025-07-15 7:08 ` Alejandro Colomar
2025-07-17 23:47 ` Kees Cook
2025-07-18 0:56 ` Alejandro Colomar
2025-07-11 18:01 ` Martin Uecker
2025-07-10 21:31 ` [RFC v5 7/7] mm: Use [v]sprintf_array() to avoid specifying the array size Alejandro Colomar
2025-07-11 1:56 ` [RFC v6 0/8] Add and use sprintf_{end,trunc,array}() instead of less ergonomic APIs Alejandro Colomar
2025-07-11 1:56 ` [RFC v6 1/8] vsprintf: Add [v]sprintf_trunc() Alejandro Colomar
2025-07-11 1:56 ` [RFC v6 2/8] vsprintf: Add [v]sprintf_end() Alejandro Colomar
2025-07-11 1:56 ` [RFC v6 3/8] sprintf: Add [v]sprintf_array() Alejandro Colomar
2025-07-11 1:56 ` [RFC v6 4/8] stacktrace, stackdepot: Add sprintf_end()-like variants of functions Alejandro Colomar
2025-07-11 1:57 ` [RFC v6 5/8] mm: Use sprintf_end() instead of less ergonomic APIs Alejandro Colomar
2025-07-11 1:57 ` [RFC v6 6/8] array_size.h: Add ENDOF() Alejandro Colomar
2025-07-11 1:57 ` [RFC v6 7/8] mm: Fix benign off-by-one bugs Alejandro Colomar
2025-07-11 1:57 ` [RFC v6 8/8] mm: Use [v]sprintf_array() to avoid specifying the array size Alejandro Colomar
2025-07-08 6:43 ` [RFC v1 0/3] Add and use seprintf() instead of less ergonomic APIs Rasmus Villemoes
2025-07-08 11:36 ` Alejandro Colomar
2025-07-08 13:51 ` Rasmus Villemoes
2025-07-08 16:14 ` Alejandro Colomar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=nx6vj5qqcgkts4pmefzux3ee4kuumwyjh6vlwsdltf56ayq33e@kyf25zkic2rk \
--to=alx@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=chris.bazley.wg14@gmail.com \
--cc=cl@linux.com \
--cc=dvyukov@google.com \
--cc=elver@google.com \
--cc=glider@google.com \
--cc=harry.yoo@oracle.com \
--cc=kasan-dev@googlegroups.com \
--cc=kees@kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=rientjes@google.com \
--cc=roman.gushchin@linux.dev \
--cc=vbabka@suse.cz \
--cc=~hallyn/shadow@lists.sr.ht \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).