Bug 191674 - Implementation for "%tu" printf(3) qualifier incorrect on several 32-bit architectures (arm/x86/mips, i.e. when sizeof(ptrdiff_t) != sizeof(*intmax_t))
Summary: Implementation for "%tu" printf(3) qualifier incorrect on several 32-bit arch...
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: CURRENT
Hardware: i386 Any
: --- Affects Some People
Assignee: freebsd-bugs mailing list
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-07-06 23:45 UTC by Enji Cooper
Modified: 2017-02-14 04:49 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Enji Cooper freebsd_committer 2014-07-06 23:45:07 UTC
One of the testcases in tools/regression/lib/libc/stdio/test-printbasic.t tests out %tu with -1 and it fails because the testcase is correctly expecting UINT32_MAX, not UINT64_MAX. According to printf(3):

         "
         t                 ptrdiff_t      (see note)            ptrdiff_t *

         Note: the t modifier, when applied to a o, u, x, or X conversion,
         indicates that the argument is of an unsigned type equivalent in size
         to a ptrdiff_t.
         "

ptrdiff_t on i386 is int32_t (from /usr/include/x86/_types.h):

100 #ifdef  __LP64__
101 typedef __int64_t       __ptrdiff_t;            /* ptr1 - ptr2 */
...
109 #else
110 typedef __int32_t       __ptrdiff_t;

So I would expect the value to be UINT32_MAX. This mismatches with the code in lib/libc/stdio/vfprintf.c vs sys/x86/include/_types.h as intmax_t is always int64_t:

 412 #define INTMAX_SIZE     (INTMAXT|SIZET|PTRDIFFT|LLONGINT)
 413 #define SJARG() \
 414         (flags&INTMAXT ? GETARG(intmax_t) : \
 415             flags&SIZET ? (intmax_t)GETARG(ssize_t) : \
 416             flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
 417             (intmax_t)GETARG(long long))
 418 #define UJARG() \
 419         (flags&INTMAXT ? GETARG(uintmax_t) : \
 420             flags&SIZET ? (uintmax_t)GETARG(size_t) : \
 421             flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
 422             (uintmax_t)GETARG(unsigned long long))
...
 602                 case 't':
 603                         flags |= PTRDIFFT;
 604                         goto rflag;

From sys/x86/include/_types.h:
 91 typedef __int64_t       __intmax_t;

Logically, I would expect this to be true IFF the i386 architecture was PAE-enabled.

# uname -a
FreeBSD isilon-fuji-current.local 11.0-CURRENT FreeBSD 11.0-CURRENT #12 r267851+3e60b32(isilon-atf-fix-bsd-progs): Thu Jun 26 12:06:01 PDT 2014     root@isilon-fuji-current.local:/usr/obj/usr/src/sys/FUJI  i386
# pwd
/usr/src/tools/regression/lib/libc/stdio
# prove test-printbasic.t 
test-printbasic.t .. 80: printf("%tu", (size_t)-1) ==> [18446744073709551615], expected [4294967295]
test-printbasic.t .. Failed 2/2 subtests 

Test Summary Report
-------------------
test-printbasic.t (Wstat: 134 Tests: 0 Failed: 0)
  Non-zero wait status: 134
  Parse errors: Bad plan.  You planned 2 tests but ran 0.
Files=1, Tests=0,  2 wallclock secs ( 0.00 usr  0.04 sys +  0.02 cusr  0.30 csys =  0.36 CPU)
Result: FAIL
Comment 1 Enji Cooper freebsd_committer 2017-02-07 05:23:08 UTC
arm/i386/mips are affected by this because it casts the value up to [u]intmax_t, whereas the type itself can only represent [U]INT_MAX. I know where the general problem exists, but I don't feel comfortable right now making the change.
Comment 2 commit-hook freebsd_committer 2017-02-07 05:39:19 UTC
A commit references this bug:

Author: ngie
Date: Tue Feb  7 05:39:01 UTC 2017
New revision: 313379
URL: https://svnweb.freebsd.org/changeset/base/313379

Log:
  Expect :int_within_limits to fail when ptrdiff_t/*intmax_t differ in base type

  The %t{d,u} (ptrdiff_t) tests fail for the following reasons:
  - ptrdiff_t is by definition int32_t on !LP64 architectures and int64_t on
    LP64 architectures.
  - intmax_t is by definition fixed to int64_t on all architectures.
  - Some of the code in lib/libc/stdio/... is promoting ptrdiff_t to *intmax_t
    when parsing/representing the value.

  PR:		191674
  MFC after:	1 week
  Sponsored by:	Dell EMC Isilon

Changes:
  head/lib/libc/tests/stdio/printbasic_test.c
Comment 3 commit-hook freebsd_committer 2017-02-14 04:49:52 UTC
A commit references this bug:

Author: ngie
Date: Tue Feb 14 04:49:07 UTC 2017
New revision: 313722
URL: https://svnweb.freebsd.org/changeset/base/313722

Log:
  MFC r313378,r313379:

  r313378:

  Wrap strcmp/wcscmp calls with ATF_CHECK_MSG and drop atf_tc_fail use

  The reasoning here was the same as what was done in r313376:
  - Gather as many results as possible instead of failing early and
    not testing the rest of the cases.
  - Simplify logic when checking test inputs vs outputs and printing
    test result.

  r313379:

  Expect :int_within_limits to fail when ptrdiff_t/*intmax_t differ in base type

  The %t{d,u} (ptrdiff_t) tests fail for the following reasons:
  - ptrdiff_t is by definition int32_t on !LP64 architectures and int64_t on
    LP64 architectures.
  - intmax_t is by definition fixed to int64_t on all architectures.
  - Some of the code in lib/libc/stdio/... is promoting ptrdiff_t to *intmax_t
    when parsing/representing the value.

  PR:		191674

Changes:
_U  stable/11/
  stable/11/lib/libc/tests/stdio/printbasic_test.c
Comment 4 commit-hook freebsd_committer 2017-02-14 04:49:53 UTC
A commit references this bug:

Author: ngie
Date: Tue Feb 14 04:49:24 UTC 2017
New revision: 313723
URL: https://svnweb.freebsd.org/changeset/base/313723

Log:
  MFC r313378,r313379:

  r313378:

  Wrap strcmp/wcscmp calls with ATF_CHECK_MSG and drop atf_tc_fail use

  The reasoning here was the same as what was done in r313376:
  - Gather as many results as possible instead of failing early and
    not testing the rest of the cases.
  - Simplify logic when checking test inputs vs outputs and printing
    test result.

  r313379:

  Expect :int_within_limits to fail when ptrdiff_t/*intmax_t differ in base type

  The %t{d,u} (ptrdiff_t) tests fail for the following reasons:
  - ptrdiff_t is by definition int32_t on !LP64 architectures and int64_t on
    LP64 architectures.
  - intmax_t is by definition fixed to int64_t on all architectures.
  - Some of the code in lib/libc/stdio/... is promoting ptrdiff_t to *intmax_t
    when parsing/representing the value.

  PR:		191674

Changes:
_U  stable/10/
  stable/10/lib/libc/tests/stdio/printbasic_test.c