When getpwnam(3) reaches out to YP, it calls clntudp_create(3) with a
authorderaadt <deraadt@openbsd.org>
Mon, 22 Jan 2024 16:18:06 +0000 (16:18 +0000)
committerderaadt <deraadt@openbsd.org>
Mon, 22 Jan 2024 16:18:06 +0000 (16:18 +0000)
pre-initialized ypconnect(2) socket.  That calls clntudp_bufcreate(),
which contains code checking if the socket and address are configured..
If not, socket(2) is called, or an address allocation is performed via
the portmapper (which calls a whole lot more code).

Split clnt_udp.c into two .c files (which will compile as seperate .o
files), and create a new libc-private clntudp_bufcreate_simple() function
which skips the socket and address work.

Result: In most static binaries, this reduces the text segment by
~100K, and removes 5-7 system call stubs -- which might matter for
non-pledged binaries with otherwise lack socket(2).
ok millert jmatthew

lib/libc/rpc/Makefile.inc
lib/libc/rpc/clnt_udp.c
lib/libc/rpc/clnt_udp.h [new file with mode: 0644]
lib/libc/rpc/clnt_udp_bufcreate.c [new file with mode: 0644]
lib/libc/yp/yp_bind.c

index 940fa41..f8b1f63 100644 (file)
@@ -1,11 +1,11 @@
-#      $OpenBSD: Makefile.inc,v 1.18 2016/03/30 06:38:41 jmc Exp $
+#      $OpenBSD: Makefile.inc,v 1.19 2024/01/22 16:18:06 deraadt Exp $
 
 # librpc sources
 .PATH: ${LIBCSRCDIR}/arch/${MACHINE}/rpc ${LIBCSRCDIR}/rpc
 
 SRCS+= auth_none.c auth_unix.c  authunix_prot.c bindresvport.c \
        clnt_generic.c clnt_perror.c clnt_raw.c clnt_simple.c clnt_tcp.c \
-       clnt_udp.c get_myaddress.c getrpcent.c getrpcport.c \
+       clnt_udp.c clnt_udp_bufcreate.c get_myaddress.c getrpcent.c getrpcport.c \
        pmap_clnt.c pmap_getmaps.c pmap_getport.c pmap_prot.c \
        pmap_prot2.c pmap_rmt.c rpc_prot.c rpc_commondata.c rpc_callmsg.c \
        svc.c svc_auth.c svc_auth_unix.c svc_raw.c svc_run.c svc_simple.c \
index e40347b..bcb5b35 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: clnt_udp.c,v 1.40 2022/08/24 01:32:21 deraadt Exp $ */
+/*     $OpenBSD: clnt_udp.c,v 1.41 2024/01/22 16:18:06 deraadt Exp $ */
 
 /*
  * Copyright (c) 2010, Oracle America, Inc.
@@ -44,7 +44,7 @@
 #include <sys/socket.h>
 #include <netdb.h>
 #include <errno.h>
-#include <rpc/pmap_clnt.h>
+#include "clnt_udp.h"
 
 /*
  * UDP bases client side rpc operations
@@ -66,31 +66,65 @@ static const struct clnt_ops udp_ops = {
        clntudp_control
 };
 
-/* 
- * Private data kept per client handle
- */
-struct cu_data {
-       int                cu_sock;
-       bool_t             cu_closeit;
-       struct sockaddr_in cu_raddr;
-       int                cu_connected;        /* use send() instead */
-       int                cu_rlen;
-       struct timeval     cu_wait;
-       struct timeval     cu_total;
-       struct rpc_err     cu_error;
-       XDR                cu_outxdrs;
-       u_int              cu_xdrpos;
-       u_int              cu_sendsz;
-       char               *cu_outbuf;
-       u_int              cu_recvsz;
-       char               cu_inbuf[1];
-};
+int
+clntudp_bufcreate1(struct clntudp_bufcreate_args *args)
+{
+       args->cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
+       if (args->cl == NULL) {
+               rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+               rpc_createerr.cf_error.re_errno = errno;
+               return -1;
+       }
+       args->sendsz = ((args->sendsz + 3) / 4) * 4;
+       args->recvsz = ((args->recvsz + 3) / 4) * 4;
+       args->cu = (struct cu_data *)mem_alloc(sizeof(args->cu) +
+           args->sendsz + args->recvsz);
+       if (args->cu == NULL) {
+               rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+               rpc_createerr.cf_error.re_errno = errno;
+               return -1;
+       }
+       args->cu->cu_outbuf = &args->cu->cu_inbuf[args->recvsz];
+       args->cl->cl_ops = &udp_ops;
+       args->cl->cl_private = (caddr_t)args->cu;
+       args->cu->cu_connected = 0;
+       args->cu->cu_rlen = sizeof (args->cu->cu_raddr);
+       args->cu->cu_wait = args->wait;
+       args->cu->cu_total.tv_sec = -1;
+       args->cu->cu_total.tv_usec = -1;
+       args->cu->cu_sendsz = args->sendsz;
+       args->cu->cu_recvsz = args->recvsz;
+       args->cu->cu_closeit = FALSE;
+       args->call_msg.rm_xid = arc4random();
+       args->call_msg.rm_direction = CALL;
+       args->call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+       args->call_msg.rm_call.cb_prog = args->program;
+       args->call_msg.rm_call.cb_vers = args->version;
+       return 0;
+}
+
+int
+clntudp_bufcreate2(struct clntudp_bufcreate_args *args)
+{
+       xdrmem_create(&(args->cu->cu_outxdrs), args->cu->cu_outbuf,
+           args->sendsz, XDR_ENCODE);
+       if (!xdr_callhdr(&(args->cu->cu_outxdrs), &args->call_msg))
+               return -1;
+       args->cu->cu_xdrpos = XDR_GETPOS(&(args->cu->cu_outxdrs));
+       args->cl->cl_auth = authnone_create();
+       if (args->cl->cl_auth == NULL) {
+               rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+               rpc_createerr.cf_error.re_errno = errno;
+               return -1;
+       }
+       return 0;
+}
 
 /*
  * Create a UDP based client handle.
- * If *sockp<0, *sockp is set to a newly created UPD socket.
+ * If *sockp<0, *sockp is set to a newly created UPD socket.  (***)
  * If raddr->sin_port is 0 a binder on the remote machine
- * is consulted for the correct port number.
+ * is consulted for the correct port number.                  (***)
  * NB: It is the client's responsibility to close *sockp, unless
  *     clntudp_bufcreate() was called with *sockp = -1 (so it created
  *     the socket), and CLNT_DESTROY() is used.
@@ -103,100 +137,45 @@ struct cu_data {
  *
  * sendsz and recvsz are the maximum allowable packet sizes that can be
  * sent and received.
+ *
+ * This is a reduced-functionality version of clntudp_bufcreate() that
+ * does not allocate socket or binding (***, above).
+ * The official function clntudp_bufcreate(), which does perform those
+ * two steps, is in clnt_udp_bufcreate.c.  This split avoids pulling
+ * socket / portmap related code into programs only using getpwent / YP code.
  */
+
 CLIENT *
-clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version,
+clntudp_bufcreate_simple(struct sockaddr_in *raddr, u_long program, u_long version,
     struct timeval wait, int *sockp, u_int sendsz, u_int recvsz)
 {
-       CLIENT *cl;
-       struct cu_data *cu = NULL;
-       struct rpc_msg call_msg;
-
-       cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
-       if (cl == NULL) {
-               rpc_createerr.cf_stat = RPC_SYSTEMERROR;
-               rpc_createerr.cf_error.re_errno = errno;
+       struct clntudp_bufcreate_args args;
+
+       args.raddr = raddr;
+       args.program = program;
+       args.version = version;
+       args.wait = wait;
+       args.sockp = sockp;
+       args.sendsz = sendsz;
+       args.recvsz = recvsz;
+       args.cl = NULL;
+       args.cu = NULL;
+
+       if (clntudp_bufcreate1(&args) == -1)
                goto fooy;
-       }
-       sendsz = ((sendsz + 3) / 4) * 4;
-       recvsz = ((recvsz + 3) / 4) * 4;
-       cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
-       if (cu == NULL) {
-               rpc_createerr.cf_stat = RPC_SYSTEMERROR;
-               rpc_createerr.cf_error.re_errno = errno;
+       args.cu->cu_raddr = *raddr;
+       args.cu->cu_sock = *sockp;
+       if (clntudp_bufcreate2(&args) == -1)
                goto fooy;
-       }
-       cu->cu_outbuf = &cu->cu_inbuf[recvsz];
-
-       if (raddr->sin_port == 0) {
-               u_short port;
-               if ((port =
-                   pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
-                       goto fooy;
-               }
-               raddr->sin_port = htons(port);
-       }
-       cl->cl_ops = &udp_ops;
-       cl->cl_private = (caddr_t)cu;
-       cu->cu_raddr = *raddr;
-       cu->cu_connected = 0;
-       cu->cu_rlen = sizeof (cu->cu_raddr);
-       cu->cu_wait = wait;
-       cu->cu_total.tv_sec = -1;
-       cu->cu_total.tv_usec = -1;
-       cu->cu_sendsz = sendsz;
-       cu->cu_recvsz = recvsz;
-       call_msg.rm_xid = arc4random();
-       call_msg.rm_direction = CALL;
-       call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
-       call_msg.rm_call.cb_prog = program;
-       call_msg.rm_call.cb_vers = version;
-       xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
-           sendsz, XDR_ENCODE);
-       if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
-               goto fooy;
-       }
-       cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
-       if (*sockp < 0) {
-               *sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK,
-                   IPPROTO_UDP);
-               if (*sockp == -1) {
-                       rpc_createerr.cf_stat = RPC_SYSTEMERROR;
-                       rpc_createerr.cf_error.re_errno = errno;
-                       goto fooy;
-               }
-               /* attempt to bind to priv port */
-               (void)bindresvport(*sockp, NULL);
-               cu->cu_closeit = TRUE;
-       } else {
-               cu->cu_closeit = FALSE;
-       }
-       cu->cu_sock = *sockp;
-       cl->cl_auth = authnone_create();
-       if (cl->cl_auth == NULL) {
-               rpc_createerr.cf_stat = RPC_SYSTEMERROR;
-               rpc_createerr.cf_error.re_errno = errno;
-               goto fooy;
-       }
-       return (cl);
+       return (args.cl);
 fooy:
-       if (cu)
-               mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
-       if (cl)
-               mem_free((caddr_t)cl, sizeof(CLIENT));
+       if (args.cu)
+               mem_free((caddr_t)args.cu,
+                   sizeof(*args.cu) + args.sendsz + args.recvsz);
+       if (args.cl)
+               mem_free((caddr_t)args.cl, sizeof(CLIENT));
        return (NULL);
 }
-DEF_WEAK(clntudp_bufcreate);
-
-CLIENT *
-clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version,
-    struct timeval wait, int *sockp)
-{
-
-       return(clntudp_bufcreate(raddr, program, version, wait, sockp,
-           UDPMSGSIZE, UDPMSGSIZE));
-}
-DEF_WEAK(clntudp_create);
 
 static enum clnt_stat 
 clntudp_call(CLIENT *cl,       /* client handle */
diff --git a/lib/libc/rpc/clnt_udp.h b/lib/libc/rpc/clnt_udp.h
new file mode 100644 (file)
index 0000000..745540b
--- /dev/null
@@ -0,0 +1,70 @@
+/*     $OpenBSD: clnt_udp.h,v 1.1 2024/01/22 16:18:06 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2010, Oracle America, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *     * Neither the name of the "Oracle America, Inc." nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* 
+ * Private data kept per client handle
+ */
+struct cu_data {
+       int                cu_sock;
+       bool_t             cu_closeit;
+       struct sockaddr_in cu_raddr;
+       int                cu_connected;        /* use send() instead */
+       int                cu_rlen;
+       struct timeval     cu_wait;
+       struct timeval     cu_total;
+       struct rpc_err     cu_error;
+       XDR                cu_outxdrs;
+       u_int              cu_xdrpos;
+       u_int              cu_sendsz;
+       char               *cu_outbuf;
+       u_int              cu_recvsz;
+       char               cu_inbuf[1];
+};
+
+struct clntudp_bufcreate_args {
+       struct sockaddr_in *raddr;
+       u_long program;
+       u_long version;
+       struct timeval wait;
+       int *sockp;
+       u_int sendsz;
+       u_int recvsz;
+       CLIENT *cl;
+       struct cu_data *cu;
+       struct rpc_msg call_msg;
+};
+
+__BEGIN_HIDDEN_DECLS
+extern int clntudp_bufcreate1(struct clntudp_bufcreate_args *);
+extern int clntudp_bufcreate2(struct clntudp_bufcreate_args *);
+__END_HIDDEN_DECLS
diff --git a/lib/libc/rpc/clnt_udp_bufcreate.c b/lib/libc/rpc/clnt_udp_bufcreate.c
new file mode 100644 (file)
index 0000000..b112c8a
--- /dev/null
@@ -0,0 +1,130 @@
+/*     $OpenBSD: clnt_udp_bufcreate.c,v 1.1 2024/01/22 16:18:06 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2010, Oracle America, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *     * Neither the name of the "Oracle America, Inc." nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * clnt_udp.c, Implements a UDP/IP based, client side RPC.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <rpc/pmap_clnt.h>
+#include "clnt_udp.h"
+
+/*
+ * Create a UDP based client handle.
+ * If *sockp<0, *sockp is set to a newly created UPD socket.
+ * If raddr->sin_port is 0 a binder on the remote machine
+ * is consulted for the correct port number.
+ * NB: It is the client's responsibility to close *sockp, unless
+ *     clntudp_bufcreate() was called with *sockp = -1 (so it created
+ *     the socket), and CLNT_DESTROY() is used.
+ * NB: The rpch->cl_auth is initialized to null authentication.
+ *     Caller may wish to set this something more useful.
+ *
+ * wait is the amount of time used between retransmitting a call if
+ * no response has been heard;  retransmission occurs until the actual
+ * rpc call times out.
+ *
+ * sendsz and recvsz are the maximum allowable packet sizes that can be
+ * sent and received.
+ */
+
+CLIENT *
+clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version,
+    struct timeval wait, int *sockp, u_int sendsz, u_int recvsz)
+{
+       struct clntudp_bufcreate_args args;
+
+       args.raddr = raddr;
+       args.program = program;
+       args.version = version;
+       args.wait = wait;
+       args.sockp = sockp;
+       args.sendsz = sendsz;
+       args.recvsz = recvsz;
+
+       if (clntudp_bufcreate1(&args) == -1)
+               goto fooy;
+
+       if (raddr->sin_port == 0) {
+               u_short port;
+               if ((port =
+                   pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
+                       goto fooy;
+               }
+               raddr->sin_port = htons(port);
+       }
+       args.cu->cu_raddr = *raddr;
+       if (*sockp < 0) {
+               *sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK,
+                   IPPROTO_UDP);
+               if (*sockp == -1) {
+                       rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+                       rpc_createerr.cf_error.re_errno = errno;
+                       goto fooy;
+               }
+               /* attempt to bind to priv port */
+               (void)bindresvport(*sockp, NULL);
+               args.cu->cu_closeit = TRUE;
+       }
+       args.cu->cu_sock = *args.sockp;
+
+       if (clntudp_bufcreate2(&args) == -1)
+               goto fooy;
+       return (args.cl);
+fooy:
+       if (args.cu)
+               mem_free((caddr_t)args.cu,
+                   sizeof(*args.cu) + args.sendsz + args.recvsz);
+       if (args.cl)
+               mem_free((caddr_t)args.cl, sizeof(CLIENT));
+       return (NULL);
+}
+DEF_WEAK(clntudp_bufcreate);
+
+CLIENT *
+clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version,
+    struct timeval wait, int *sockp)
+{
+
+       return(clntudp_bufcreate(raddr, program, version, wait, sockp,
+           UDPMSGSIZE, UDPMSGSIZE));
+}
+DEF_WEAK(clntudp_create);
index af6775e..d99a596 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: yp_bind.c,v 1.32 2022/08/02 16:59:29 deraadt Exp $ */
+/*     $OpenBSD: yp_bind.c,v 1.33 2024/01/22 16:18:06 deraadt Exp $ */
 /*
  * Copyright (c) 1992, 1993, 1996 Theo de Raadt <deraadt@theos.com>
  * All rights reserved.
 char _yp_domain[HOST_NAME_MAX+1];
 int _yplib_timeout = 10;
 
+extern CLIENT *
+clntudp_bufcreate_simple(struct sockaddr_in *raddr, u_long program, u_long version,
+    struct timeval wait, int *sockp, u_int sendsz, u_int recvsz);
+
 int
 _yp_dobind(const char *dom, struct dom_binding **ypdb)
 {
@@ -72,8 +76,8 @@ again:
 
        tv.tv_sec = _yplib_timeout / 2;
        tv.tv_usec = 0;
-       ypbinding->dom_client = clntudp_create(&ypbinding->dom_server_addr,
-           YPPROG, YPVERS, tv, &ypbinding->dom_socket);
+       ypbinding->dom_client = clntudp_bufcreate_simple(&ypbinding->dom_server_addr,
+           YPPROG, YPVERS, tv, &ypbinding->dom_socket, UDPMSGSIZE, UDPMSGSIZE);
        if (ypbinding->dom_client == NULL) {
                close(ypbinding->dom_socket);
                ypbinding->dom_socket = -1;