From: 蟹澤翔太 Subject: Re: printf(3): "%#.0o" of 0 prints nothing instead of "0" To: Jan Klemkow Cc: Theo de Raadt , tech@openbsd.org Date: Thu, 17 Sep 2026 04:06:15 +0900 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 のメール: > > 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 >のメール: >>> >>> Please check for other clones of printf in the tree. There is one >>> in kernel, one in bootblocks, one in 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