Download raw body.
printf(3): "%#.0o" of 0 prints nothing instead of "0"
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>のメール:
> >
> > 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
printf(3): "%#.0o" of 0 prints nothing instead of "0"