Index | Thread | Search

From:
蟹澤翔太 <zakinko@snowrabbit.org>
Subject:
Re: printf(3): "%#.0o" of 0 prints nothing instead of "0"
To:
Jan Klemkow <j.klemkow@wemelug.de>
Cc:
Theo de Raadt <deraadt@openbsd.org>, tech@openbsd.org
Date:
Thu, 17 Sep 2026 04:06:15 +0900

Download raw body.

Thread
Thank you for taking it, and for the regression test -- I had not
thought to add one, and the int.c line is exactly the case.

The diff looks right to me.  The three code hunks are the ones I sent
(libc on the 11th, the kernel one on the 14th) with the long lines
wrapped, and nothing else changed.  Thanks also for the pointer to
DR 151; I had not found it, and it settles the same question in fewer
words than my mail did.

Please go ahead.

> 2026/09/17 3:05、Jan Klemkow <j.klemkow@wemelug.de>のメール:
> 
> On Mon, Sep 14, 2026 at 07:50:18PM +0900, 蟹澤翔太 wrote:
>> Thanks for the quick reply.  Of the three you named, the kernel copy
>> has the same bug; the other two don't.
>> 
>> sys/kern/subr_prf.c (kprintf) is not shrunk in this respect -- it has
>> the same code as libc, the "if (_uquad != 0 || prec != 0)" test with
>> the octal leading-0 step inside it, so "%#.0o" of 0 prints nothing
>> there too.  Diff below.
>> 
>> sys/lib/libsa/printf.c (bootblocks) is shrunk past where it matters:
>> no precision and no # handling for octal, so "%#.0o" can't reach the
>> bug -- the flag is ignored and it prints the digits.
>> 
>> libexec/ld.so/util.c (_dl_printf) is the same: no precision, no #,
>> nothing to fix.
>> 
>> I only looked at those three, not the whole tree.
>> 
>> I built GENERIC.MP with the diff -- it compiles clean.  Pulling the
>> octal block out of subr_prf.c into a small harness, the stock code
>> prints [] for "%#.0o" of 0 and the patched code prints [0]; "%#.0o" of
>> 8 -> [010], "%.0o" of 0 -> [], and "%#o" of 0 -> [0] are unchanged
>> either way.  Same one-line change I made in libc, built on 7.9 and
>> checked the same way.
>> 
>>> 2026/09/11 10:13、Theo de Raadt <deraadt@openbsd.org <mailto:deraadt@openbsd.org>>のメール:
>>> 
>>> Please check for other clones of printf in the tree.  There is one
>>> in kernel, one in bootblocks, one in ld.so <http://ld.so/>.  There may be others.
>>> These have all been shrunk, but maybe it matters.  
> 
> Hi Showta,
> 
> Thanks for you diff.  Finally Defect Report #151 [0] gets fixed in
> OpenBSD.  I just warped the long lines and adds a regression test in
> the diff below.
> 
> ok?
> 
> [0]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_151.html
> 
> Thanks,
> jan
> 
> Index: lib/libc/stdio/vfprintf.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/stdio/vfprintf.c,v
> diff -u -p -r1.84 vfprintf.c
> --- lib/libc/stdio/vfprintf.c	8 Aug 2025 15:58:53 -0000	1.84
> +++ lib/libc/stdio/vfprintf.c	16 Sep 2026 17:26:07 -0000
> @@ -923,9 +923,13 @@ number:			if ((dprec = prec) >= 0)
> 			 * ``The result of converting a zero value with an
> 			 * explicit precision of zero is no characters.''
> 			 *	-- ANSI X3J11
> +			 *
> +			 * The # flag overrides that for octal: it asks for a
> +			 * leading zero, so "%#.0o" of 0 is "0", not "".
> 			 */
> 			cp = buf + BUF;
> -			if (_umax != 0 || prec != 0) {
> +			if (_umax != 0 || prec != 0 ||
> +			    (flags & ALT && base == OCT)) {
> 				/*
> 				 * Unsigned mod is hard, and unsigned mod
> 				 * by a constant is easier than that by
> Index: lib/libc/stdio/vfwprintf.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/stdio/vfwprintf.c,v
> diff -u -p -r1.25 vfwprintf.c
> --- lib/libc/stdio/vfwprintf.c	8 Aug 2025 15:58:53 -0000	1.25
> +++ lib/libc/stdio/vfwprintf.c	16 Sep 2026 17:26:07 -0000
> @@ -901,9 +901,13 @@ number:			if ((dprec = prec) >= 0)
> 			 * ``The result of converting a zero value with an
> 			 * explicit precision of zero is no characters.''
> 			 *	-- ANSI X3J11
> +			 *
> +			 * The # flag overrides that for octal: it asks for a
> +			 * leading zero, so "%#.0o" of 0 is "0", not "".
> 			 */
> 			cp = buf + BUF;
> -			if (_umax != 0 || prec != 0) {
> +			if (_umax != 0 || prec != 0 ||
> +			    (flags & ALT && base == OCT)) {
> 				/*
> 				 * Unsigned mod is hard, and unsigned mod
> 				 * by a constant is easier than that by
> Index: regress/lib/libc/printf/int.c
> ===================================================================
> RCS file: /cvs/src/regress/lib/libc/printf/int.c,v
> diff -u -p -r1.2 int.c
> --- regress/lib/libc/printf/int.c	14 Jul 2020 16:40:04 -0000	1.2
> +++ regress/lib/libc/printf/int.c	16 Sep 2026 17:26:07 -0000
> @@ -326,6 +326,7 @@ main(int argc, char *argv[])
> 
> 	tu("<%hho>", S_CHAR, 0, "<0>");
> 	tu("<%#hho>", S_CHAR, 0, "<0>");
> +	tu("<%#.0hho>", S_CHAR, 0, "<0>");
> 	tu("<%hho>", S_CHAR, UINT8_MAX, "<377>");
> 	tu("<%#hho>", S_CHAR, UINT8_MAX, "<0377>");
> 	tu("<%hho>", S_CHAR, -1, "<377>");
> Index: sys/kern/subr_prf.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/subr_prf.c,v
> diff -u -p -r1.106 subr_prf.c
> --- sys/kern/subr_prf.c	14 Aug 2022 01:58:28 -0000	1.106
> +++ sys/kern/subr_prf.c	16 Sep 2026 17:42:19 -0000
> @@ -960,7 +960,8 @@ number:			if ((dprec = prec) >= 0)
> 			 *	-- ANSI X3J11
> 			 */
> 			cp = buf + KPRINTF_BUFSIZE;
> -			if (_uquad != 0 || prec != 0) {
> +			if (_uquad != 0 || prec != 0 ||
> +			    (flags & ALT && base == OCT)) {
> 				/*
> 				 * Unsigned mod is hard, and unsigned mod
> 				 * by a constant is easier than that by