Subject: lib/1116: memory xdr streams do unaligned accesses
To: None <gnats-admin@sun-lamp.cs.berkeley.edu>
From: der Mouse <mouse@Collatz.McRCIM.McGill.EDU>
List: netbsd-bugs
Date: 06/05/1995 07:35:03
>Number:         1116
>Category:       lib
>Synopsis:       memory xdr streams do unaligned accesses
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people (Library Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Jun  5 07:35:02 1995
>Originator:     der Mouse
>Organization:
	Dis-
>Release:        -current as of minutes ago (June 5th)
>Environment:
	Any that gets upset at unaligned accesses
>Description:
	An XDR stream created with xdrmem_create works only if the
	memory block handed to xdrmem_create() is 32bit-aligned,
	because xdrmem_getlong and xdrmem_putlong store through pointer
	casts.  If the block is unaligned, depending on the machine, it
	may (a) work fine, (b) work but with a performance penalty, (c)
	coredump, or (d) silently access memory one to three bytes
	earlier than it should.  (BTW, does NetBSD run on any machines
	that would do (d)?)
>How-To-Repeat:
	This coredumps on NetBSD/sparc.  If the first assignment has
	the +1 removed, it works fine.

	#include <stdlib.h>
	#include <rpc/rpc.h>
	
	void main(void)
	{
	 char *buf;
	 XDR x;
	 double d1;
	 double d2;
	
	 buf = malloc(64) + 1;
	 xdrmem_create(&x,buf,32,XDR_ENCODE);
	 d1 = 123.456789;
	 xdr_double(&x,&d1);
	 xdr_destroy(&x);
	 d2 = 987654.321;
	 xdrmem_create(&x,buf,32,XDR_DECODE);
	 xdr_double(&x,&d2);
	 xdr_destroy(&x);
	 printf("%20.10f -> %20.10f [%20.10f]\n",d1,d2,d1-d2);
	 exit(0);
	}
>Fix:
	(a) Change the docs to note that the memory area must be
	    32bit-aligned,
	(b) Make xdrmem_getlong and xdrmem_putlong do four one-byte
	    loads/stores instead,
	(c) Duplicate enough internals so that there are two versions
	    of xdrmem_{ge,pu}tlong, one for aligned and one for
	    unaligned, and have xdrmem_create choose the correct ops
	    vector depending on the alignment of its argument.

	Personally, I prefer (c).  Here are patches to
	lib/libc/rpc/xdr_mem.c to implement it; with these fixes, the
	above program starts working for me.

--- /sources/current-usr-src/./lib/libc/rpc/xdr_mem.c	Tue May 30 12:37:43 1995
+++ /usr/src/./lib/libc/rpc/xdr_mem.c	Mon Jun  5 10:13:23 1995
@@ -51,29 +51,49 @@
 #include <rpc/xdr.h>
 #include <netinet/in.h>
 
-static bool_t	xdrmem_getlong();
-static bool_t	xdrmem_putlong();
+/* A case could be made that xdrmem_inline should not exist in both aligned
+   and unaligned versions.  However, since the return type is not void *
+   (or char *), it may break badly on architectures that silently ignore
+   the low bits of a pointer.  So we do an unaligned version that fails. */
+
+static bool_t	xdrmem_getlong_aligned();
+static bool_t	xdrmem_putlong_aligned();
+static bool_t	xdrmem_getlong_unaligned();
+static bool_t	xdrmem_putlong_unaligned();
 static bool_t	xdrmem_getbytes();
 static bool_t	xdrmem_putbytes();
 static u_int	xdrmem_getpos(); /* XXX w/64-bit pointers, u_int not enough! */
 static bool_t	xdrmem_setpos();
-static int32_t *xdrmem_inline();
+static int32_t *xdrmem_inline_aligned();
+static int32_t *xdrmem_inline_unaligned();
 static void	xdrmem_destroy();
 
-static struct	xdr_ops xdrmem_ops = {
-	xdrmem_getlong,
-	xdrmem_putlong,
+static struct	xdr_ops xdrmem_ops_aligned = {
+	xdrmem_getlong_aligned,
+	xdrmem_putlong_aligned,
 	xdrmem_getbytes,
 	xdrmem_putbytes,
 	xdrmem_getpos,
 	xdrmem_setpos,
-	xdrmem_inline,
+	xdrmem_inline_aligned,
+	xdrmem_destroy
+};
+
+static struct	xdr_ops xdrmem_ops_unaligned = {
+	xdrmem_getlong_unaligned,
+	xdrmem_putlong_unaligned,
+	xdrmem_getbytes,
+	xdrmem_putbytes,
+	xdrmem_getpos,
+	xdrmem_setpos,
+	xdrmem_inline_unaligned,
 	xdrmem_destroy
 };
 
 /*
  * The procedure xdrmem_create initializes a stream descriptor for a
- * memory buffer.  
+ * memory buffer.  XXX assumes casting pointer to int allows us to
+ * detect whether it's 32bit-aligned. XXX
  */
 void
 xdrmem_create(xdrs, addr, size, op)
@@ -82,9 +102,9 @@
 	u_int size;
 	enum xdr_op op;
 {
-
 	xdrs->x_op = op;
-	xdrs->x_ops = &xdrmem_ops;
+	xdrs->x_ops = (((int)addr)&3) ? &xdrmem_ops_unaligned
+				      : &xdrmem_ops_aligned;
 	xdrs->x_private = xdrs->x_base = addr;
 	xdrs->x_handy = size;
 }
@@ -96,11 +116,10 @@
 }
 
 static bool_t
-xdrmem_getlong(xdrs, lp)
+xdrmem_getlong_aligned(xdrs, lp)
 	register XDR *xdrs;
 	long *lp;
 {
-
 	if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
 		return (FALSE);
 	*lp = (long)ntohl((u_int32_t)(*((int32_t *)(xdrs->x_private))));
@@ -109,11 +128,10 @@
 }
 
 static bool_t
-xdrmem_putlong(xdrs, lp)
+xdrmem_putlong_aligned(xdrs, lp)
 	register XDR *xdrs;
 	long *lp;
 {
-
 	if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
 		return (FALSE);
 	*(int32_t *)xdrs->x_private = (int32_t)htonl((int32_t)(*lp));
@@ -122,6 +140,36 @@
 }
 
 static bool_t
+xdrmem_getlong_unaligned(xdrs, lp)
+	register XDR *xdrs;
+	long *lp;
+{
+	u_int32_t l;
+
+	if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
+		return (FALSE);
+	bcopy(xdrs->x_private,&l,4);
+	*lp = (long)ntohl(l);
+	xdrs->x_private += sizeof(int32_t);
+	return (TRUE);
+}
+
+static bool_t
+xdrmem_putlong_unaligned(xdrs, lp)
+	register XDR *xdrs;
+	long *lp;
+{
+	u_int32_t l;
+
+	if ((xdrs->x_handy -= sizeof(int32_t)) < 0)
+		return (FALSE);
+	l = htonl((u_int32_t)*lp);
+	bcopy(&l,xdrs->x_private,4);
+	xdrs->x_private += sizeof(int32_t);
+	return (TRUE);
+}
+
+static bool_t
 xdrmem_getbytes(xdrs, addr, len)
 	register XDR *xdrs;
 	caddr_t addr;
@@ -174,7 +222,7 @@
 }
 
 static int32_t *
-xdrmem_inline(xdrs, len)
+xdrmem_inline_aligned(xdrs, len)
 	register XDR *xdrs;
 	int len;
 {
@@ -186,4 +234,12 @@
 		xdrs->x_private += len;
 	}
 	return (buf);
+}
+
+static int32_t *
+xdrmem_inline_unaligned(xdrs, len)
+	register XDR *xdrs;
+	int len;
+{
+	return (0);
 }

					der Mouse

			    mouse@collatz.mcrcim.mcgill.edu
>Audit-Trail:
>Unformatted: