Bug 19412

Summary: xdrmem_setpos: UB when pos is large
Product: glibc Reporter: Alexander Cherepanov <cherepan>
Component: networkAssignee: Not yet assigned to anyone <unassigned>
Status: NEW ---    
Severity: normal    
Priority: P2    
Version: 2.22   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Alexander Cherepanov 2015-12-28 14:10:34 UTC
It seems xdrmem_setpos tries to defend against out-of-bounds values of the pos argument:

 174 static bool_t
 175 xdrmem_setpos (XDR *xdrs, u_int pos)
 176 {
 177   caddr_t newaddr = xdrs->x_base + pos;
 178   caddr_t lastaddr = xdrs->x_private + xdrs->x_handy;
 179   size_t handy = lastaddr - newaddr;
 180 
 181   if (newaddr > lastaddr
 182       || newaddr < xdrs->x_base
 183       || handy != (u_int) handy)
 184     return FALSE;
 185 
 186   xdrs->x_private = newaddr;
 187   xdrs->x_handy = (u_int) handy;
 188   return TRUE;
 189 }

https://sourceware.org/git/?p=glibc.git;a=blob;f=sunrpc/xdr_mem.c;h=8b9858959476ad8bee7f7eaa7250c516beda55b4;hb=HEAD#l174

This code is invalid C. The sum "xdrs->x_base + pos" (line 177) is undefined by the C standard when the result doesn't point into the same array. Then, checks for pointer wrapping like "newaddr < xdrs->x_base" (line 182) are already "miscompiled" by clang and, I guess, could be expected to be broken by gcc in the future.

Similar to pr19391.