Re: [Libtirpc-devel] [PATCH V4] xdrstdio_create buffers do not output encoded values on ppc

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 2018-07-11 at 16:42 -0400, Chuck Lever wrote:
> > On Jul 11, 2018, at 2:19 PM, Trond Myklebust <trondmy@hammerspace.c
> > om> wrote:
> > 
> > On Wed, 2018-07-11 at 14:06 -0400, Chuck Lever wrote:
> > > > On Jul 11, 2018, at 12:38 PM, Trond Myklebust <trondmy@hammersp
> > > > ace.
> > > > com> wrote:
> > > > 
> > > > On Wed, 2018-07-11 at 12:05 -0400, Steve Dickson wrote:
> > > > > Hey Trond,
> > > > > 
> > > > > On 07/11/2018 11:25 AM, Steve Dickson wrote:
> > > > > > The cause is that the xdr_putlong uses a long to store the
> > > > > > converted value, then passes it to fwrite as a byte buffer.
> > > > > > Only the first 4 bytes are written, which is okay for a LE
> > > > > > system after byteswapping, but writes all zeroes on BE
> > > > > > systems.
> > > > > > 
> > > > > > Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1261738
> > > > > > 
> > > > > > Reviewed-by: Chuck Lever <chuck.lever@xxxxxxxxxx>
> > > > > > Signed-off-by: Steve Dickson <steved@xxxxxxxxxx>
> > > > > > ---
> > > > > > v4: Use UINT32_MAX instead of INT32_MAX in boundary check.
> > > > > 
> > > > > Talking with some old crusty types ;-) they pointed out
> > > > > that all these routines use a signed arguments and
> > > > > always have... So again why is using an unsigned max
> > > > > the right thing to do? 
> > > > 
> > > > As I said earlier, you are casting from a larger type to a
> > > > smaller
> > > > type, and you want it to work for both signed and unsigned 32-
> > > > bit
> > > > values. Consider this kind of code:
> > > > 
> > > >       int32_t foo = 0xFFFF0000;
> > > >       ret = xdrstdio_putlong(xdr, foo);
> 
> I doubt that will compile everywhere, since the second parameter
> is supposed to be a pointer. Even if we add "&" the C compiler
> will likely not allow an implicit typecast and an "address of".
> 
> Let's call the API portably:
> 
>      long foo = 0xFFFF0000;
>      ret = xdrstdio_putlong(xdr, &foo);

I should have paid more attention. This is what I meant to write:

       int32_t foo = 0xFFFF0000;
       long tmp = foo;

       ret = xdrstdio_putlong(xdr, &tmp);

and/or

      uint32_t foo = 0xFFFF0000U;
      long tmp = foo;

      ret = xdrstdio_putlong(xdr, &tmp);

...and yes, they will still behave differently.

> 
> On a system with 32-bit longs, foo contains a negative value.
> 
> On a system with 64-bit longs, foo contains a positive value. My
> narrow range check will fail, but a UINT32_MAX check would allow
> xdrstdio_putlong to proceed.

> My desired outcome is that the API works for the same range of
> values on platforms with 32-bit longs and 64-bit longs. If this
> API is to work the same on both classes of platform, I guess we
> do want "*lp > UINT32_MAX || *lp < INT32_MIN".

Just see below at the implementations of xdr_int32_t() and
xdr_u_int32_t() that were cut n' pasted directly from the current
libtirpc code. Not only is the outcome desirable, but it is a direct
internal requirement of the code.


> > > > Since foo is signed, then (long)foo ends up getting cast to
> > > > 0xFFFFFFFFFFFF0000L, which is negative, but is >
> > > > (long)INT32_MIN.
> > > > So
> > > > ret == TRUE;
> 
> 
> > > On 32-bit systems, xdrstdio_putlong() cannot work for values
> > > between INT32_MAX and UINT32_MAX. It should return FALSE for
> > > these values on 64-bit systems. In fact, that is the way this
> > > API behaves for other 64-bit aware libtirpc implementations.
> > 
> > On a 32-bit system there is no need to check anything, because
> > sizeof(long) == sizeof(int) == 4.
> > The problems occur once you start casting from a smaller sized
> > integer
> > to a larger one because the unsigned cast will behave differently
> > to
> > the signed cast and result in a bitwise very different value.
> 
> I'm confused. The patch _removes_ unsigned typecasts. We're no
> longer converting between signed and unsigned integers in these
> two API functions.
> 
> xdrstdio_putlong converts a (potentially) larger signed integer
> to a (potentially) smaller signed integer. If a larger integer
> cannot fit in a smaller destination, the only thing that can
> happen is that the most-significant bits of the larger integer
> are discarded, IIUC.
> 
> 
> > > > Now try:
> > > > 
> > > >       uint32_t bar = 0xFFFF0000U;
> > > >       ret = xdrstdio_putlong(xdr, bar);
> > > > 
> > > > 
> > > > Since bar is unsigned, then (long)bar gets cast to 0xFFFF0000L.
> > > > That is
> > > > clearly > (long)INT32_MAX == 0x7FFFFFFFL, and so the above
> > > > fails
> > > > with
> > > > ret == FALSE.
> > > > 
> > > > 
> > > > BTW: The above is pretty much exactly how xdr_int32_t() and
> > > > xdr_uint32_t() work. The former does a signed cast to long,
> > > > while
> > > > the
> > > > latter does an unsigned cast to long.
> > > 
> > > If that's the case, we should examine how other 64-bit aware
> > > libtirpc implementations work and fix ours to behave the same.
> > > Our libtirpc forked in the late 1990s before 64-bit systems
> > > were widely deployed, so I have some doubts whether our
> > > implementation is correct.
> > 
> > Observe:
> > 
> > bool_t
> > xdr_int32_t(xdrs, int32_p)
> >        XDR *xdrs;
> >        int32_t *int32_p;
> > {
> >        long l;
> > 
> >        switch (xdrs->x_op) {
> > 
> >        case XDR_ENCODE:
> >                l = (long) *int32_p;
> >                return (XDR_PUTLONG(xdrs, &l));
> > 
> >        case XDR_DECODE:
> >                if (!XDR_GETLONG(xdrs, &l)) {
> >                        return (FALSE);
> >                }
> >                *int32_p = (int32_t) l;
> >                return (TRUE);
> > 
> >        case XDR_FREE:
> >                return (TRUE);
> >        }
> >        /* NOTREACHED */
> >        return (FALSE);
> > }
> > 
> > bool_t
> > xdr_u_int32_t(xdrs, u_int32_p)
> >        XDR *xdrs;
> >        u_int32_t *u_int32_p;
> > {
> >        u_long l;
> > 
> >        switch (xdrs->x_op) {
> > 
> >        case XDR_ENCODE:
> >                l = (u_long) *u_int32_p;
> >                return (XDR_PUTLONG(xdrs, (long *)&l));
> > 
> >        case XDR_DECODE:
> >                if (!XDR_GETLONG(xdrs, (long *)&l)) {
> >                        return (FALSE);
> >                }
> >                *u_int32_p = (u_int32_t) l;
> >                return (TRUE);
> > 
> >        case XDR_FREE:
> >                return (TRUE);
> >        }
> >        /* NOTREACHED */
> >        return (FALSE);
> > }
> > 
> > > > > steved.
> > > > > 
> > > > > > 
> > > > > > v3: Reworked the bounds checking
> > > > > > 
> > > > > > v2: Added bounds checking
> > > > > >   Changed from unsigned to signed
> > > > > > 
> > > > > > src/xdr_stdio.c | 15 ++++++++++++---
> > > > > > 1 file changed, 12 insertions(+), 3 deletions(-)
> > > > > > 
> > > > > > diff --git a/src/xdr_stdio.c b/src/xdr_stdio.c
> > > > > > index 4410262..846c7bf 100644
> > > > > > --- a/src/xdr_stdio.c
> > > > > > +++ b/src/xdr_stdio.c
> > > > > > @@ -38,6 +38,7 @@
> > > > > > */
> > > > > > 
> > > > > > #include <stdio.h>
> > > > > > +#include <stdint.h>
> > > > > > 
> > > > > > #include <arpa/inet.h>
> > > > > > #include <rpc/types.h>
> > > > > > @@ -103,10 +104,12 @@ xdrstdio_getlong(xdrs, lp)
> > > > > > 	XDR *xdrs;
> > > > > > 	long *lp;
> > > > > > {
> > > > > > +	int32_t mycopy;
> > > > > > 
> > > > > > -	if (fread(lp, sizeof(int32_t), 1, (FILE *)xdrs-
> > > > > > > x_private) 
> > > > > > 
> > > > > > != 1)
> > > > > > +	if (fread(&mycopy, sizeof(int32_t), 1, (FILE
> > > > > > *)xdrs-
> > > > > > > x_private) != 1)
> > > > > > 
> > > > > > 		return (FALSE);
> > > > > > -	*lp = (long)ntohl((u_int32_t)*lp);
> > > > > > +
> > > > > > +	*lp = (long)ntohl(mycopy);
> > > > > > 	return (TRUE);
> > > > > > }
> > > > > > 
> > > > > > @@ -115,8 +118,14 @@ xdrstdio_putlong(xdrs, lp)
> > > > > > 	XDR *xdrs;
> > > > > > 	const long *lp;
> > > > > > {
> > > > > > -	long mycopy = (long)htonl((u_int32_t)*lp);
> > > > > > +	int32_t mycopy;
> > > > > > +
> > > > > > +#if defined(_LP64)
> > > > > > +	if ((*lp > UINT32_MAX) || (*lp < INT32_MIN))
> > > > > > +		return (FALSE);
> > > > > > +#endif
> > > > > > 
> > > > > > +	mycopy = (int32_t)htonl((int32_t)*lp);
> > > > > > 	if (fwrite(&mycopy, sizeof(int32_t), 1, (FILE *)xdrs-
> > > > > > > x_private) != 1)
> > > > > > 
> > > > > > 		return (FALSE);
> > > > > > 	return (TRUE);
> > > > > > 
> > > > > 
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe
> > > > > linux-
> > > > > nfs"
> > > > > in
> > > > > the body of a message to majordomo@xxxxxxxxxxxxxxx
> > > > > More majordomo info at  http://vger.kernel.org/majordomo-info
> > > > > .htm
> > > > > l
> > > > 
> > > > -- 
> > > > Trond Myklebust
> > > > Linux NFS client maintainer, Hammerspace
> > > > trond.myklebust@xxxxxxxxxxxxxxx
> > > > 
> > > > -------------------------------------------------------------
> > > > ----
> > > > -------------
> > > > Check out the vibrant tech community on one of the world's most
> > > > engaging tech sites, Slashdot.org! http://sdm.link/slashdot
> > > > _______________________________________________
> > > > Libtirpc-devel mailing list
> > > > Libtirpc-devel@xxxxxxxxxxxxxxxxxxxxx
> > > > https://lists.sourceforge.net/lists/listinfo/libtirpc-devel
> > > 
> > > --
> > > Chuck Lever
> > > 
> > > 
> > > 
> > 
> > -- 
> > Trond Myklebust
> > CTO, Hammerspace Inc
> > 4300 El Camino Real, Suite 105
> > Los Altos, CA 94022
> > www.hammer.space
> 
> --
> Chuck Lever
> 
> 
> 
Trond Myklebust
CTO, Hammerspace Inc
4300 El Camino Real, Suite 105
Los Altos, CA 94022
www.hammer.space

-- 
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trond.myklebust@xxxxxxxxxxxxxxx

��.n��������+%������w��{.n�����{��w���jg��������ݢj����G�������j:+v���w�m������w�������h�����٥




[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux