Bug 237699

Summary: Hex printing with subr_prf cuts off zero-padded MSB
Product: Base System Reporter: contact
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed Not A Bug    
Severity: Affects Many People    
Priority: ---    
Version: CURRENT   
Hardware: Any   
OS: Any   

Description contact 2019-05-01 21:17:59 UTC
Hi,

Using a a print function that uses subr_prf.c with an argument of %#016qx has the first two padding 0s cut off. This only impacts zeros added by padding, as data does not get cut. It appears to be an issue in kvprintf in the section with the "number:" label. I'm trying to print an AVX register, using %#016qx%qx%qx%qx, but I think this would show up on any architecture on... a very large number of BSD versions?

I believe it has something to do with this part of the kvprintf "number:" section--I think 2 characters are being erroneously subtracted, but I haven't had an opportunity to really read through kvprintf and figure out how to fix this without accidentally harming anything else:

			if (!ladjust && padc == '0')
				dwidth = width - tmp;

I also noticed that FreeBSD uses subr_prf.c V8.3, though I know I've seen a V8.4 floating around...
Comment 1 contact 2019-05-01 21:52:24 UTC
Terribly sorry, I meant %#016qx%016qx%016qx%016qx.

It is only the format modifier with the # flag that is missing the upper most 0-padded byte/2 nibbles, and it should be very easy to replicate this.
Comment 2 Conrad Meyer freebsd_committer freebsd_triage 2019-05-02 00:55:07 UTC
For better or worse, our printf(9) includes the "0x" of '#' in the "width" specification:

https://github.com/freebsd/freebsd/blob/master/sys/kern/subr_prf.c#L894

			p = ksprintn(nbuf, num, base, &n, upper);
			tmp = 0;
			if (sharpflag && num != 0) {
				if (base == 8)
					tmp++;
				else if (base == 16)
					tmp += 2;
			}

That's where your two missing characters are going.
Comment 3 Conrad Meyer freebsd_committer freebsd_triage 2019-05-02 00:56:35 UTC
This seems to match printf(1) (and thus, probably matches our printf(3) too).

Also seems to match Linux.
Comment 4 Conrad Meyer freebsd_committer freebsd_triage 2019-05-02 00:59:29 UTC
I see.  Most printf implementations won't truncate the number to fit the specified width.  I.e., %#01x does not truncate 0x10 to 0x0.  So that is probably a bug in our printf(9).

Probably you want %#018x anyway, though, to account for "0x" and full 16 bytes of number in your fixed width print (or some large numbers will be different widths than others).
Comment 5 Conrad Meyer freebsd_committer freebsd_triage 2019-05-02 01:01:28 UTC
(In reply to Conrad Meyer from comment #4)
> So that is probably a bug in our printf(9).

Ah, nevermind.  You had specified it only affected padding zeros.  I think that's not-a-bug, then.
Comment 6 contact 2019-05-02 16:55:47 UTC
I see, thanks for looking into it.

Should the man page(s) for printf(3) and (9) be updated to clarify this? They just say that '#' prepends '0x' to non-zero results and don't mention that 2 nibbles get eaten if using 0-padding.
Comment 7 contact 2019-05-02 17:21:45 UTC
Sorry, I meant "2 nibbles of padding get eaten when using 0-padding" in my previous comment.

Something like "Note: when using the '#' flag with zero padding and x, the two uppermost padding zeroes will be replaced by the "0x" hexadecimal identifier. In these cases, specify two extra padding zeros (e.g. use %#018qx instead of %#016qx). Similarly, when using this flag with o, add one extra padding zero for the octal identifier (e.g. use %#08o instead of %#07o)."