Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
8a2c4f8793
!48 backport to fix rpc_gss_seccreate passed in credential
From: @eaglegai 
Reviewed-by: @jiangheng12 
Signed-off-by: @jiangheng12
2024-09-11 01:38:25 +00:00
eaglegai
2db8c87a00 backport to fix rpc_gss_seccreate passed in credential 2024-09-10 12:05:41 +00:00
openeuler-ci-bot
7a1a7cc05c
!23 [sync] PR-22: fix "mount.nfs: Cannot allocate memory" error
From: @openeuler-sync-bot 
Reviewed-by: @seuzw 
Signed-off-by: @seuzw
2022-11-17 02:18:17 +00:00
eaglegai
463354b212 fix 'mount.nfs: Cannot allocate memory' error
(cherry picked from commit 053ee52ab9d86273dbd181fcbe08b47f67932f43)
2022-11-17 09:49:29 +08:00
openeuler-ci-bot
3aa7f23ab8
!19 【轻量级 PR】:Fix obsoletes bug when both new and old versions existed
From: @zhangshaoning_uniontech 
Reviewed-by: @zengwefeng 
Signed-off-by: @zengwefeng
2022-10-11 12:32:58 +00:00
张少宁
054b117630
Fix obsoletes bug when both new and old versions existed
Signed-off-by: 张少宁 <zhangshaoning@uniontech.com>
2022-10-11 01:09:12 +00:00
openeuler-ci-bot
d766f21606
!13 [sync] PR-11: fix CVE-2021-46828
From: @openeuler-sync-bot 
Reviewed-by: @seuzw 
Signed-off-by: @seuzw
2022-07-26 01:51:33 +00:00
eaglegai
f0a779808b fix CVE-2021-46828
(cherry picked from commit c6443dcb9b6da923360cdf84e341251299cb221c)
2022-07-23 14:54:15 +08:00
openeuler-ci-bot
864127dd09 !1 update to 1.2.6
Merge pull request !1 from seuzw/master
2020-05-12 15:20:06 +08:00
seuzw
9cefa3b3b8 update to 1.2.6 2020-05-12 14:54:35 +08:00
11 changed files with 1953 additions and 6 deletions

View File

@ -0,0 +1,561 @@
From 86529758570cef4c73fb9b9c4104fdc510f701ed Mon Sep 17 00:00:00 2001
From: Dai Ngo <dai.ngo@oracle.com>
Date: Sat, 21 Aug 2021 13:16:23 -0400
Subject: [PATCH] Fix DoS vulnerability in libtirpc
Currently svc_run does not handle poll timeout and rendezvous_request
does not handle EMFILE error returned from accept(2 as it used to.
These two missing functionality were removed by commit b2c9430f46c4.
The effect of not handling poll timeout allows idle TCP conections
to remain ESTABLISHED indefinitely. When the number of connections
reaches the limit of the open file descriptors (ulimit -n) then
accept(2) fails with EMFILE. Since there is no handling of EMFILE
error this causes svc_run() to get in a tight loop calling accept(2).
This resulting in the RPC service of svc_run is being down, it's
no longer able to service any requests.
RPC service rpcbind, statd and mountd are effected by this
problem.
Fix by enhancing rendezvous_request to keep the number of
SVCXPRT conections to 4/5 of the size of the file descriptor
table. When this thresold is reached, it destroys the idle
TCP connections or destroys the least active connection if
no idle connnction was found.
Fixes: 44bf15b8 rpcbind: don't use obsolete svc_fdset interface of libtirpc
Signed-off-by: dai.ngo@oracle.com
Signed-off-by: Steve Dickson <steved@redhat.com>
---
INSTALL | 371 +----------------------------------------------------------
src/svc.c | 17 ++-
src/svc_vc.c | 62 +++++++++-
3 files changed, 78 insertions(+), 372 deletions(-)
mode change 100644 => 120000 INSTALL
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 2099840..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,370 +0,0 @@
-Installation Instructions
-*************************
-
-Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
-Inc.
-
- Copying and distribution of this file, with or without modification,
-are permitted in any medium without royalty provided the copyright
-notice and this notice are preserved. This file is offered as-is,
-without warranty of any kind.
-
-Basic Installation
-==================
-
- Briefly, the shell command `./configure && make && make install'
-should configure, build, and install this package. The following
-more-detailed instructions are generic; see the `README' file for
-instructions specific to this package. Some packages provide this
-`INSTALL' file but do not implement all of the features documented
-below. The lack of an optional feature in a given package is not
-necessarily a bug. More recommendations for GNU packages can be found
-in *note Makefile Conventions: (standards)Makefile Conventions.
-
- The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation. It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions. Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, and a
-file `config.log' containing compiler output (useful mainly for
-debugging `configure').
-
- It can also use an optional file (typically called `config.cache'
-and enabled with `--cache-file=config.cache' or simply `-C') that saves
-the results of its tests to speed up reconfiguring. Caching is
-disabled by default to prevent problems with accidental use of stale
-cache files.
-
- If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README' so they can
-be considered for the next release. If you are using the cache, and at
-some point `config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
- The file `configure.ac' (or `configure.in') is used to create
-`configure' by a program called `autoconf'. You need `configure.ac' if
-you want to change it or regenerate `configure' using a newer version
-of `autoconf'.
-
- The simplest way to compile this package is:
-
- 1. `cd' to the directory containing the package's source code and type
- `./configure' to configure the package for your system.
-
- Running `configure' might take a while. While running, it prints
- some messages telling which features it is checking for.
-
- 2. Type `make' to compile the package.
-
- 3. Optionally, type `make check' to run any self-tests that come with
- the package, generally using the just-built uninstalled binaries.
-
- 4. Type `make install' to install the programs and any data files and
- documentation. When installing into a prefix owned by root, it is
- recommended that the package be configured and built as a regular
- user, and only the `make install' phase executed with root
- privileges.
-
- 5. Optionally, type `make installcheck' to repeat any self-tests, but
- this time using the binaries in their final installed location.
- This target does not install anything. Running this target as a
- regular user, particularly if the prior `make install' required
- root privileges, verifies that the installation completed
- correctly.
-
- 6. You can remove the program binaries and object files from the
- source code directory by typing `make clean'. To also remove the
- files that `configure' created (so you can compile the package for
- a different kind of computer), type `make distclean'. There is
- also a `make maintainer-clean' target, but that is intended mainly
- for the package's developers. If you use it, you may have to get
- all sorts of other programs in order to regenerate files that came
- with the distribution.
-
- 7. Often, you can also type `make uninstall' to remove the installed
- files again. In practice, not all packages have tested that
- uninstallation works correctly, even though it is required by the
- GNU Coding Standards.
-
- 8. Some packages, particularly those that use Automake, provide `make
- distcheck', which can by used by developers to test that all other
- targets like `make install' and `make uninstall' work correctly.
- This target is generally not run by end users.
-
-Compilers and Options
-=====================
-
- Some systems require unusual options for compilation or linking that
-the `configure' script does not know about. Run `./configure --help'
-for details on some of the pertinent environment variables.
-
- You can give `configure' initial values for configuration parameters
-by setting variables in the command line or in the environment. Here
-is an example:
-
- ./configure CC=c99 CFLAGS=-g LIBS=-lposix
-
- *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
- You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory. To do this, you can use GNU `make'. `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script. `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'. This
-is known as a "VPATH" build.
-
- With a non-GNU `make', it is safer to compile the package for one
-architecture at a time in the source code directory. After you have
-installed the package for one architecture, use `make distclean' before
-reconfiguring for another architecture.
-
- On MacOS X 10.5 and later systems, you can create libraries and
-executables that work on multiple system types--known as "fat" or
-"universal" binaries--by specifying multiple `-arch' options to the
-compiler but only a single `-arch' option to the preprocessor. Like
-this:
-
- ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
- CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
- CPP="gcc -E" CXXCPP="g++ -E"
-
- This is not guaranteed to produce working output in all cases, you
-may have to build one architecture at a time and combine the results
-using the `lipo' tool if you have problems.
-
-Installation Names
-==================
-
- By default, `make install' installs the package's commands under
-`/usr/local/bin', include files under `/usr/local/include', etc. You
-can specify an installation prefix other than `/usr/local' by giving
-`configure' the option `--prefix=PREFIX', where PREFIX must be an
-absolute file name.
-
- You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files. If you
-pass the option `--exec-prefix=PREFIX' to `configure', the package uses
-PREFIX as the prefix for installing programs and libraries.
-Documentation and other data files still use the regular prefix.
-
- In addition, if you use an unusual directory layout you can give
-options like `--bindir=DIR' to specify different values for particular
-kinds of files. Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them. In general, the
-default for these options is expressed in terms of `${prefix}', so that
-specifying just `--prefix' will affect all of the other directory
-specifications that were not explicitly provided.
-
- The most portable way to affect installation locations is to pass the
-correct locations to `configure'; however, many packages provide one or
-both of the following shortcuts of passing variable assignments to the
-`make install' command line to change installation locations without
-having to reconfigure or recompile.
-
- The first method involves providing an override variable for each
-affected directory. For example, `make install
-prefix=/alternate/directory' will choose an alternate location for all
-directory configuration variables that were expressed in terms of
-`${prefix}'. Any directories that were specified during `configure',
-but not in terms of `${prefix}', must each be overridden at install
-time for the entire installation to be relocated. The approach of
-makefile variable overrides for each directory variable is required by
-the GNU Coding Standards, and ideally causes no recompilation.
-However, some platforms have known limitations with the semantics of
-shared libraries that end up requiring recompilation when using this
-method, particularly noticeable in packages that use GNU Libtool.
-
- The second method involves providing the `DESTDIR' variable. For
-example, `make install DESTDIR=/alternate/directory' will prepend
-`/alternate/directory' before all installation names. The approach of
-`DESTDIR' overrides is not required by the GNU Coding Standards, and
-does not work on platforms that have drive letters. On the other hand,
-it does better at avoiding recompilation issues, and works well even
-when some directory options were not specified in terms of `${prefix}'
-at `configure' time.
-
-Optional Features
-=================
-
- If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
- Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System). The
-`README' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
- For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
- Some packages offer the ability to configure how verbose the
-execution of `make' will be. For these packages, running `./configure
---enable-silent-rules' sets the default to minimal output, which can be
-overridden with `make V=1'; while running `./configure
---disable-silent-rules' sets the default to verbose, which can be
-overridden with `make V=0'.
-
-Particular systems
-==================
-
- On HP-UX, the default C compiler is not ANSI C compatible. If GNU
-CC is not installed, it is recommended to use the following options in
-order to use an ANSI C compiler:
-
- ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
-
-and if that doesn't work, install pre-built binaries of GCC for HP-UX.
-
- HP-UX `make' updates targets which have the same time stamps as
-their prerequisites, which makes it generally unusable when shipped
-generated files such as `configure' are involved. Use GNU `make'
-instead.
-
- On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
-parse its `<wchar.h>' header file. The option `-nodtk' can be used as
-a workaround. If GNU CC is not installed, it is therefore recommended
-to try
-
- ./configure CC="cc"
-
-and if that doesn't work, try
-
- ./configure CC="cc -nodtk"
-
- On Solaris, don't put `/usr/ucb' early in your `PATH'. This
-directory contains several dysfunctional programs; working variants of
-these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
-in your `PATH', put it _after_ `/usr/bin'.
-
- On Haiku, software installed for all users goes in `/boot/common',
-not `/usr/local'. It is recommended to use the following options:
-
- ./configure --prefix=/boot/common
-
-Specifying the System Type
-==========================
-
- There may be some features `configure' cannot figure out
-automatically, but needs to determine by the type of machine the package
-will run on. Usually, assuming the package is built to be run on the
-_same_ architectures, `configure' can figure that out, but if it prints
-a message saying it cannot guess the machine type, give it the
-`--build=TYPE' option. TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name which has the form:
-
- CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
- OS
- KERNEL-OS
-
- See the file `config.sub' for the possible values of each field. If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
- If you are _building_ compiler tools for cross-compiling, you should
-use the option `--target=TYPE' to select the type of system they will
-produce code for.
-
- If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with `--host=TYPE'.
-
-Sharing Defaults
-================
-
- If you want to set default values for `configure' scripts to share,
-you can create a site shell script called `config.site' that gives
-default values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists. Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Defining Variables
-==================
-
- Variables not defined in a site shell script can be set in the
-environment passed to `configure'. However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost. In order to avoid this problem, you should set
-them in the `configure' command line, using `VAR=value'. For example:
-
- ./configure CC=/usr/local2/bin/gcc
-
-causes the specified `gcc' to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-Unfortunately, this technique does not work for `CONFIG_SHELL' due to
-an Autoconf limitation. Until the limitation is lifted, you can use
-this workaround:
-
- CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
-
-`configure' Invocation
-======================
-
- `configure' recognizes the following options to control how it
-operates.
-
-`--help'
-`-h'
- Print a summary of all of the options to `configure', and exit.
-
-`--help=short'
-`--help=recursive'
- Print a summary of the options unique to this package's
- `configure', and exit. The `short' variant lists options used
- only in the top level, while the `recursive' variant lists options
- also present in any nested packages.
-
-`--version'
-`-V'
- Print the version of Autoconf used to generate the `configure'
- script, and exit.
-
-`--cache-file=FILE'
- Enable the cache: use and save the results of the tests in FILE,
- traditionally `config.cache'. FILE defaults to `/dev/null' to
- disable caching.
-
-`--config-cache'
-`-C'
- Alias for `--cache-file=config.cache'.
-
-`--quiet'
-`--silent'
-`-q'
- Do not print messages saying which checks are being made. To
- suppress all normal output, redirect it to `/dev/null' (any error
- messages will still be shown).
-
-`--srcdir=DIR'
- Look for the package's source code in directory DIR. Usually
- `configure' can determine that directory automatically.
-
-`--prefix=DIR'
- Use DIR as the installation prefix. *note Installation Names::
- for more details, including other options available for fine-tuning
- the installation locations.
-
-`--no-create'
-`-n'
- Run the configure checks, but stop before creating any output
- files.
-
-`configure' also accepts some other, not widely useful, options. Run
-`configure --help' for more details.
diff --git a/INSTALL b/INSTALL
new file mode 120000
index 0000000..e3f22c0
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1 @@
+/usr/share/automake-1.16/INSTALL
\ No newline at end of file
diff --git a/src/svc.c b/src/svc.c
index 6db164b..3a8709f 100644
--- a/src/svc.c
+++ b/src/svc.c
@@ -57,7 +57,7 @@
#define max(a, b) (a > b ? a : b)
-static SVCXPRT **__svc_xports;
+SVCXPRT **__svc_xports;
int __svc_maxrec;
/*
@@ -194,6 +194,21 @@ __xprt_do_unregister (xprt, dolock)
rwlock_unlock (&svc_fd_lock);
}
+int
+svc_open_fds()
+{
+ int ix;
+ int nfds = 0;
+
+ rwlock_rdlock (&svc_fd_lock);
+ for (ix = 0; ix < svc_max_pollfd; ++ix) {
+ if (svc_pollfd[ix].fd != -1)
+ nfds++;
+ }
+ rwlock_unlock (&svc_fd_lock);
+ return (nfds);
+}
+
/*
* Add a service program to the callout list.
* The dispatch routine will be called when a rpc request for this
diff --git a/src/svc_vc.c b/src/svc_vc.c
index f1d9f00..3dc8a75 100644
--- a/src/svc_vc.c
+++ b/src/svc_vc.c
@@ -64,6 +64,8 @@
extern rwlock_t svc_fd_lock;
+extern SVCXPRT **__svc_xports;
+extern int svc_open_fds();
static SVCXPRT *makefd_xprt(int, u_int, u_int);
static bool_t rendezvous_request(SVCXPRT *, struct rpc_msg *);
@@ -82,6 +84,7 @@ static void svc_vc_ops(SVCXPRT *);
static bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in);
static bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq,
void *in);
+static int __svc_destroy_idle(int timeout);
struct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */
u_int sendsize;
@@ -313,13 +316,14 @@ done:
return (xprt);
}
+
/*ARGSUSED*/
static bool_t
rendezvous_request(xprt, msg)
SVCXPRT *xprt;
struct rpc_msg *msg;
{
- int sock, flags;
+ int sock, flags, nfds, cnt;
struct cf_rendezvous *r;
struct cf_conn *cd;
struct sockaddr_storage addr;
@@ -379,6 +383,16 @@ again:
gettimeofday(&cd->last_recv_time, NULL);
+ nfds = svc_open_fds();
+ if (nfds >= (_rpc_dtablesize() / 5) * 4) {
+ /* destroy idle connections */
+ cnt = __svc_destroy_idle(15);
+ if (cnt == 0) {
+ /* destroy least active */
+ __svc_destroy_idle(0);
+ }
+ }
+
return (FALSE); /* there is never an rpc msg to be processed */
}
@@ -820,3 +834,49 @@ __svc_clean_idle(fd_set *fds, int timeout, bool_t cleanblock)
{
return FALSE;
}
+
+static int
+__svc_destroy_idle(int timeout)
+{
+ int i, ncleaned = 0;
+ SVCXPRT *xprt, *least_active;
+ struct timeval tv, tdiff, tmax;
+ struct cf_conn *cd;
+
+ gettimeofday(&tv, NULL);
+ tmax.tv_sec = tmax.tv_usec = 0;
+ least_active = NULL;
+ rwlock_wrlock(&svc_fd_lock);
+
+ for (i = 0; i <= svc_max_pollfd; i++) {
+ if (svc_pollfd[i].fd == -1)
+ continue;
+ xprt = __svc_xports[i];
+ if (xprt == NULL || xprt->xp_ops == NULL ||
+ xprt->xp_ops->xp_recv != svc_vc_recv)
+ continue;
+ cd = (struct cf_conn *)xprt->xp_p1;
+ if (!cd->nonblock)
+ continue;
+ if (timeout == 0) {
+ timersub(&tv, &cd->last_recv_time, &tdiff);
+ if (timercmp(&tdiff, &tmax, >)) {
+ tmax = tdiff;
+ least_active = xprt;
+ }
+ continue;
+ }
+ if (tv.tv_sec - cd->last_recv_time.tv_sec > timeout) {
+ __xprt_unregister_unlocked(xprt);
+ __svc_vc_dodestroy(xprt);
+ ncleaned++;
+ }
+ }
+ if (timeout == 0 && least_active != NULL) {
+ __xprt_unregister_unlocked(least_active);
+ __svc_vc_dodestroy(least_active);
+ ncleaned++;
+ }
+ rwlock_unlock(&svc_fd_lock);
+ return (ncleaned);
+}
--
1.8.3.1

View File

@ -0,0 +1,115 @@
From 21718bbbfa2a4bf4992bd295e25cbc67868dcfc1 Mon Sep 17 00:00:00 2001
From: Attila Kovacs <attila.kovacs@cfa.harvard.edu>
Date: Wed, 20 Jul 2022 17:03:28 -0400
Subject: [PATCH] Eliminate deadlocks in connects with an MT environment
In cnlt_dg_freeres() and clnt_vc_freeres(), cond_signal() is called after
unlocking the mutex (clnt_fd_lock). The manual of pthread_cond_signal()
allows that, but mentions that for consistent scheduling, cond_signal()
should be called with the waiting mutex locked.
clnt_fd_lock is locked on L171, but then not released if jumping to the
err1 label on an error (L175 and L180). This means that those errors
will deadlock all further operations that require clnt_fd_lock access.
Same in clnt_vc.c in clnt_vc_create, on lines 215, 222, and 230 respectively.
Signed-off-by: Steve Dickson <steved@redhat.com>
---
src/clnt_dg.c | 9 ++++++---
src/clnt_vc.c | 12 ++++++++----
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/src/clnt_dg.c b/src/clnt_dg.c
index b3d82e7..7c5d22e 100644
--- a/src/clnt_dg.c
+++ b/src/clnt_dg.c
@@ -101,9 +101,9 @@ extern mutex_t clnt_fd_lock;
#define release_fd_lock(fd_lock, mask) { \
mutex_lock(&clnt_fd_lock); \
fd_lock->active = FALSE; \
- mutex_unlock(&clnt_fd_lock); \
thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
cond_signal(&fd_lock->cv); \
+ mutex_unlock(&clnt_fd_lock); \
}
static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
@@ -172,12 +172,15 @@ clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
if (dg_fd_locks == (fd_locks_t *) NULL) {
dg_fd_locks = fd_locks_init();
if (dg_fd_locks == (fd_locks_t *) NULL) {
+ mutex_unlock(&clnt_fd_lock);
goto err1;
}
}
fd_lock = fd_lock_create(fd, dg_fd_locks);
- if (fd_lock == (fd_lock_t *) NULL)
+ if (fd_lock == (fd_lock_t *) NULL) {
+ mutex_unlock(&clnt_fd_lock);
goto err1;
+ }
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
@@ -573,9 +576,9 @@ clnt_dg_freeres(cl, xdr_res, res_ptr)
cu->cu_fd_lock->active = TRUE;
xdrs->x_op = XDR_FREE;
dummy = (*xdr_res)(xdrs, res_ptr);
- mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &mask, NULL);
cond_signal(&cu->cu_fd_lock->cv);
+ mutex_unlock(&clnt_fd_lock);
return (dummy);
}
diff --git a/src/clnt_vc.c b/src/clnt_vc.c
index a07e297..3c73e65 100644
--- a/src/clnt_vc.c
+++ b/src/clnt_vc.c
@@ -153,9 +153,9 @@ extern mutex_t clnt_fd_lock;
#define release_fd_lock(fd_lock, mask) { \
mutex_lock(&clnt_fd_lock); \
fd_lock->active = FALSE; \
- mutex_unlock(&clnt_fd_lock); \
thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL); \
cond_signal(&fd_lock->cv); \
+ mutex_unlock(&clnt_fd_lock); \
}
static const char clnt_vc_errstr[] = "%s : %s";
@@ -216,7 +216,9 @@ clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
if (vc_fd_locks == (fd_locks_t *) NULL) {
vc_fd_locks = fd_locks_init();
if (vc_fd_locks == (fd_locks_t *) NULL) {
- struct rpc_createerr *ce = &get_rpc_createerr();
+ struct rpc_createerr *ce;
+ mutex_unlock(&clnt_fd_lock);
+ ce = &get_rpc_createerr();
ce->cf_stat = RPC_SYSTEMERROR;
ce->cf_error.re_errno = errno;
goto err;
@@ -224,7 +226,9 @@ clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
}
fd_lock = fd_lock_create(fd, vc_fd_locks);
if (fd_lock == (fd_lock_t *) NULL) {
- struct rpc_createerr *ce = &get_rpc_createerr();
+ struct rpc_createerr *ce;
+ mutex_unlock(&clnt_fd_lock);
+ ce = &get_rpc_createerr();
ce->cf_stat = RPC_SYSTEMERROR;
ce->cf_error.re_errno = errno;
goto err;
@@ -495,9 +499,9 @@ clnt_vc_freeres(cl, xdr_res, res_ptr)
cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
xdrs->x_op = XDR_FREE;
dummy = (*xdr_res)(xdrs, res_ptr);
- mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
cond_signal(&ct->ct_fd_lock->cv);
+ mutex_unlock(&clnt_fd_lock);
return dummy;
}
--
1.8.3.1

View File

@ -0,0 +1,119 @@
From 57440adcd67be82720771999384420d00a7f94ea Mon Sep 17 00:00:00 2001
From: Jaime Caamano Ruiz <jcaamano@suse.com>
Date: Thu, 25 Jun 2020 11:27:58 -0400
Subject: [PATCH] Fix memory management issues of fd locks
Fix the use of an fd_lock referenced from private client data after it
was freed.
Signed-off-by: Steve Dickson <steved@redhat.com>
---
src/clnt_dg.c | 9 +++++----
src/clnt_fd_locks.h | 4 +++-
src/clnt_vc.c | 14 ++++++--------
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/clnt_dg.c b/src/clnt_dg.c
index df402ec..abc09f1 100644
--- a/src/clnt_dg.c
+++ b/src/clnt_dg.c
@@ -725,14 +725,15 @@ clnt_dg_destroy(cl)
{
struct cu_data *cu = (struct cu_data *)cl->cl_private;
int cu_fd = cu->cu_fd;
+ fd_lock_t *cu_fd_lock = cu->cu_fd_lock;
sigset_t mask;
sigset_t newmask;
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (cu->cu_fd_lock->active)
- cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
+ while (cu_fd_lock->active)
+ cond_wait(&cu_fd_lock->cv, &clnt_fd_lock);
if (cu->cu_closeit)
(void)close(cu_fd);
XDR_DESTROY(&(cu->cu_outxdrs));
@@ -742,8 +743,8 @@ clnt_dg_destroy(cl)
if (cl->cl_tp && cl->cl_tp[0])
mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
mem_free(cl, sizeof (CLIENT));
- cond_signal(&cu->cu_fd_lock->cv);
- fd_lock_destroy(cu_fd, cu->cu_fd_lock, dg_fd_locks);
+ cond_signal(&cu_fd_lock->cv);
+ fd_lock_destroy(cu_fd, cu_fd_lock, dg_fd_locks);
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &mask, NULL);
}
diff --git a/src/clnt_fd_locks.h b/src/clnt_fd_locks.h
index 8263071..359f995 100644
--- a/src/clnt_fd_locks.h
+++ b/src/clnt_fd_locks.h
@@ -114,6 +114,7 @@ fd_locks_t* fd_locks_init() {
}
if ( (size_t) fd_locks_prealloc > SIZE_MAX/sizeof(fd_lock_t)) {
+ mem_free(fd_locks, sizeof (*fd_locks));
errno = EOVERFLOW;
return (NULL);
}
@@ -121,6 +122,7 @@ fd_locks_t* fd_locks_init() {
fd_lock_arraysz = fd_locks_prealloc * sizeof (fd_lock_t);
fd_locks->fd_lock_array = (fd_lock_t *) mem_alloc(fd_lock_arraysz);
if (fd_locks->fd_lock_array == (fd_lock_t *) NULL) {
+ mem_free(fd_locks, sizeof (*fd_locks));
errno = ENOMEM;
return (NULL);
}
@@ -162,7 +164,7 @@ fd_lock_t* fd_lock_create(int fd, fd_locks_t *fd_locks) {
return &fd_locks->fd_lock_array[fd];
}
#endif
- fd_lock_item_t* item;
+ fd_lock_item_t *item;
fd_lock_list_t *list = to_fd_lock_list(fd_locks);
for (item = TAILQ_FIRST(list);
diff --git a/src/clnt_vc.c b/src/clnt_vc.c
index 2f3dde6..6f7f7da 100644
--- a/src/clnt_vc.c
+++ b/src/clnt_vc.c
@@ -632,20 +632,18 @@ static void
clnt_vc_destroy(cl)
CLIENT *cl;
{
+ assert(cl != NULL);
struct ct_data *ct = (struct ct_data *) cl->cl_private;
int ct_fd = ct->ct_fd;
+ fd_lock_t *ct_fd_lock = ct->ct_fd_lock;
sigset_t mask;
sigset_t newmask;
- assert(cl != NULL);
-
- ct = (struct ct_data *) cl->cl_private;
-
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (ct->ct_fd_lock->active)
- cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
+ while (ct_fd_lock->active)
+ cond_wait(&ct_fd_lock->cv, &clnt_fd_lock);
if (ct->ct_closeit && ct->ct_fd != -1) {
(void)close(ct->ct_fd);
}
@@ -658,8 +656,8 @@ clnt_vc_destroy(cl)
if (cl->cl_tp && cl->cl_tp[0])
mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
mem_free(cl, sizeof(CLIENT));
- cond_signal(&ct->ct_fd_lock->cv);
- fd_lock_destroy(ct_fd, ct->ct_fd_lock, vc_fd_locks);
+ cond_signal(&ct_fd_lock->cv);
+ fd_lock_destroy(ct_fd, ct_fd_lock, vc_fd_locks);
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
}
--
1.8.3.1

View File

@ -0,0 +1,30 @@
From d0dc59e27263c6b53435d770010dcc6f397d58ee Mon Sep 17 00:00:00 2001
From: Frank Sorenson <sorenson@redhat.com>
Date: Mon, 17 Jan 2022 13:33:13 -0500
Subject: [PATCH] libtirpc: Fix use-after-free accessing the error number
Free the cbuf after obtaining the error number.
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
src/clnt_dg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/clnt_dg.c b/src/clnt_dg.c
index e1255de..b3d82e7 100644
--- a/src/clnt_dg.c
+++ b/src/clnt_dg.c
@@ -456,9 +456,9 @@ get_reply:
cmsg = CMSG_NXTHDR (&msg, cmsg))
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
{
- mem_free(cbuf, (outlen + 256));
e = (struct sock_extended_err *) CMSG_DATA(cmsg);
cu->cu_error.re_errno = e->ee_errno;
+ mem_free(cbuf, (outlen + 256));
release_fd_lock(cu->cu_fd_lock, mask);
return (cu->cu_error.re_status = RPC_CANTRECV);
}
--
1.8.3.1

View File

@ -0,0 +1,32 @@
From 7a6651a31038cb19807524d0422e09271c5ffec9 Mon Sep 17 00:00:00 2001
From: Attila Kovacs <attila.kovacs@cfa.harvard.edu>
Date: Tue, 26 Jul 2022 15:20:05 -0400
Subject: [PATCH] clnt_dg_freeres() uncleared set active state may deadlock.
In clnt_dg.c in clnt_dg_freeres(), cu_fd_lock->active is set to TRUE, with no
corresponding clearing when the operation (*xdr_res() call) is completed. This
would leave other waiting operations blocked indefinitely, effectively
deadlocking the client. For comparison, clnt_vd_freeres() in clnt_vc.c does not
set the active state to TRUE. I believe the vc behavior is correct, while the
dg behavior is a bug.
Signed-off-by: Attila Kovacs <attipaci@gmail.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
src/clnt_dg.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/clnt_dg.c b/src/clnt_dg.c
index 7c5d22e..b2043ac 100644
--- a/src/clnt_dg.c
+++ b/src/clnt_dg.c
@@ -573,7 +573,6 @@ clnt_dg_freeres(cl, xdr_res, res_ptr)
mutex_lock(&clnt_fd_lock);
while (cu->cu_fd_lock->active)
cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
- cu->cu_fd_lock->active = TRUE;
xdrs->x_op = XDR_FREE;
dummy = (*xdr_res)(xdrs, res_ptr);
thr_sigsetmask(SIG_SETMASK, &mask, NULL);
--
1.8.3.1

View File

@ -0,0 +1,35 @@
From 22b1c0cd6076dcd7df822cd1181e98278dc865db Mon Sep 17 00:00:00 2001
From: Olga Kornievskaia <kolga@netapp.com>
Date: Wed, 3 Jan 2024 17:50:42 -0500
Subject: [PATCH] gssapi: fix rpc_gss_seccreate passed in cred
Fix rpc_gss_seccreate() usage of the passed in gss credential.
Fixes: 5f1fe4dde861 ("Pass time_req and input_channel_bindings through to init_sec_context")
Reviewed-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
src/auth_gss.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/auth_gss.c b/src/auth_gss.c
index e317664..9d18f96 100644
--- a/src/auth_gss.c
+++ b/src/auth_gss.c
@@ -842,9 +842,9 @@ rpc_gss_seccreate(CLIENT *clnt, char *principal, char *mechanism,
gd->sec = sec;
if (req) {
- sec.req_flags = req->req_flags;
+ gd->sec.req_flags = req->req_flags;
gd->time_req = req->time_req;
- sec.cred = req->my_cred;
+ gd->sec.cred = req->my_cred;
gd->icb = req->input_channel_bindings;
}
--
2.25.1

View File

@ -0,0 +1,867 @@
From e7c34df8f57331063b9d795812c62cec3ddfbc17 Mon Sep 17 00:00:00 2001
From: Jaime Caamano Ruiz <jcaamano@suse.com>
Date: Tue, 16 Jun 2020 13:00:52 -0400
Subject: [PATCH] libtirpc: replace array with list for per-fd locks
Currently per-fd locks for the clients are pre-allocated up to
the soft limit of maximum allowed open file desciptors per process as
defined in __rpc_dtbsize():
if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
return (tbsize = (int)rl.rlim_cur);
}
This limit can be arbitrarily large for any given process resulting in
unreasonable memory allocation. For example, for systemd PID1 process
this limit is set to 1073741816 since version 240. systemd is an
indirect user of this library as it fetches information about users,
groups, etc...
This patch proposes a list implementation of per-fd locks based on glibc
doubly linked lists. It also includes support for a fixed array based
pre-allocation up to a compile-time defined limit of locks for
equivalence to the previous implementation.
Signed-off-by: Steve Dickson <steved@redhat.com>
---
src/clnt_dg.c | 116 ++++++++++++-----------------
src/clnt_fd_locks.h | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/clnt_vc.c | 149 ++++++++++++++++----------------------
tirpc/reentrant.h | 1 +
4 files changed, 317 insertions(+), 154 deletions(-)
create mode 100644 src/clnt_fd_locks.h
diff --git a/src/clnt_dg.c b/src/clnt_dg.c
index eb5467f..df402ec 100644
--- a/src/clnt_dg.c
+++ b/src/clnt_dg.c
@@ -53,6 +53,7 @@
#include <unistd.h>
#include <err.h>
#include "rpc_com.h"
+#include "clnt_fd_locks.h"
#ifdef IP_RECVERR
#include <asm/types.h>
@@ -78,24 +79,28 @@ static void clnt_dg_destroy(CLIENT *);
* This machinery implements per-fd locks for MT-safety. It is not
* sufficient to do per-CLIENT handle locks for MT-safety because a
* user may create more than one CLIENT handle with the same fd behind
- * it. Therfore, we allocate an array of flags (dg_fd_locks), protected
- * by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables
- * similarly protected. Dg_fd_lock[fd] == 1 => a call is activte on some
- * CLIENT handle created for that fd.
+ * it.
+ *
+ * We keep track of a list of per-fd locks, protected by the clnt_fd_lock
+ * mutex. Each per-fd lock consists of a predicate indicating whether is
+ * active or not: fd_lock->active == TRUE => a call is active on some
+ * CLIENT handle created for that fd. Each fd predicate is guarded by a
+ * condition variable so that the global mutex can be unlocked while
+ * waiting for the predicate to change.
+ *
* The current implementation holds locks across the entire RPC and reply,
* including retransmissions. Yes, this is silly, and as soon as this
* code is proven to work, this should be the first thing fixed. One step
* at a time.
*/
-static int *dg_fd_locks;
+static fd_locks_t *dg_fd_locks;
extern mutex_t clnt_fd_lock;
-static cond_t *dg_cv;
-#define release_fd_lock(fd, mask) { \
+#define release_fd_lock(fd_lock, mask) { \
mutex_lock(&clnt_fd_lock); \
- dg_fd_locks[fd] = 0; \
+ fd_lock->active = FALSE; \
mutex_unlock(&clnt_fd_lock); \
thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
- cond_signal(&dg_cv[fd]); \
+ cond_signal(&fd_lock->cv); \
}
static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
@@ -107,6 +112,7 @@ static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
*/
struct cu_data {
int cu_fd; /* connections fd */
+ fd_lock_t *cu_fd_lock;
bool_t cu_closeit; /* opened by library */
struct sockaddr_storage cu_raddr; /* remote address */
int cu_rlen;
@@ -155,47 +161,20 @@ clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
sigset_t newmask;
struct __rpc_sockinfo si;
int one = 1;
+ fd_lock_t *fd_lock;
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- if (dg_fd_locks == (int *) NULL) {
- size_t cv_allocsz, fd_allocsz;
- unsigned int dtbsize = __rpc_dtbsize();
-
- if ( (size_t) dtbsize > SIZE_MAX/sizeof(cond_t)) {
- mutex_unlock(&clnt_fd_lock);
- thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
- errno = EOVERFLOW;
- goto err1;
- }
-
- fd_allocsz = dtbsize * sizeof (int);
- dg_fd_locks = (int *) mem_alloc(fd_allocsz);
- if (dg_fd_locks == (int *) NULL) {
- mutex_unlock(&clnt_fd_lock);
- thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
- errno = ENOMEM;
- goto err1;
- } else
- memset(dg_fd_locks, '\0', fd_allocsz);
-
- cv_allocsz = dtbsize * sizeof (cond_t);
- dg_cv = (cond_t *) mem_alloc(cv_allocsz);
- if (dg_cv == (cond_t *) NULL) {
- mem_free(dg_fd_locks, fd_allocsz);
- dg_fd_locks = (int *) NULL;
- mutex_unlock(&clnt_fd_lock);
- thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
- errno = ENOMEM;
+ if (dg_fd_locks == (fd_locks_t *) NULL) {
+ dg_fd_locks = fd_locks_init();
+ if (dg_fd_locks == (fd_locks_t *) NULL) {
goto err1;
- } else {
- int i;
-
- for (i = 0; i < dtbsize; i++)
- cond_init(&dg_cv[i], 0, (void *) 0);
}
}
+ fd_lock = fd_lock_create(fd, dg_fd_locks);
+ if (fd_lock == (fd_lock_t *) NULL)
+ goto err1;
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
@@ -274,6 +253,7 @@ clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
*/
cu->cu_closeit = FALSE;
cu->cu_fd = fd;
+ cu->cu_fd_lock = fd_lock;
cl->cl_ops = clnt_dg_ops();
cl->cl_private = (caddr_t)(void *)cu;
cl->cl_auth = authnone_create();
@@ -319,17 +299,15 @@ clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
sigset_t newmask;
socklen_t salen;
ssize_t recvlen = 0;
- int rpc_lock_value;
u_int32_t xid, inval, outval;
outlen = 0;
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (dg_fd_locks[cu->cu_fd])
- cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
- rpc_lock_value = 1;
- dg_fd_locks[cu->cu_fd] = rpc_lock_value;
+ while (cu->cu_fd_lock->active)
+ cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
+ cu->cu_fd_lock->active = TRUE;
mutex_unlock(&clnt_fd_lock);
if (cu->cu_total.tv_usec == -1) {
timeout = utimeout; /* use supplied timeout */
@@ -473,7 +451,7 @@ get_reply:
mem_free(cbuf, (outlen + 256));
e = (struct sock_extended_err *) CMSG_DATA(cmsg);
cu->cu_error.re_errno = e->ee_errno;
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (cu->cu_error.re_status = RPC_CANTRECV);
}
mem_free(cbuf, (outlen + 256));
@@ -553,7 +531,7 @@ get_reply:
}
out:
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (cu->cu_error.re_status);
}
@@ -582,13 +560,14 @@ clnt_dg_freeres(cl, xdr_res, res_ptr)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (dg_fd_locks[cu->cu_fd])
- cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
+ while (cu->cu_fd_lock->active)
+ cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
+ cu->cu_fd_lock->active = TRUE;
xdrs->x_op = XDR_FREE;
dummy = (*xdr_res)(xdrs, res_ptr);
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &mask, NULL);
- cond_signal(&dg_cv[cu->cu_fd]);
+ cond_signal(&cu->cu_fd_lock->cv);
return (dummy);
}
@@ -609,36 +588,34 @@ clnt_dg_control(cl, request, info)
struct netbuf *addr;
sigset_t mask;
sigset_t newmask;
- int rpc_lock_value;
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (dg_fd_locks[cu->cu_fd])
- cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
- rpc_lock_value = 1;
- dg_fd_locks[cu->cu_fd] = rpc_lock_value;
+ while (cu->cu_fd_lock->active)
+ cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
+ cu->cu_fd_lock->active = TRUE;
mutex_unlock(&clnt_fd_lock);
switch (request) {
case CLSET_FD_CLOSE:
cu->cu_closeit = TRUE;
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (TRUE);
case CLSET_FD_NCLOSE:
cu->cu_closeit = FALSE;
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (TRUE);
}
/* for other requests which use info */
if (info == NULL) {
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (FALSE);
}
switch (request) {
case CLSET_TIMEOUT:
if (time_not_ok((struct timeval *)info)) {
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (FALSE);
}
cu->cu_total = *(struct timeval *)info;
@@ -652,7 +629,7 @@ clnt_dg_control(cl, request, info)
break;
case CLSET_RETRY_TIMEOUT:
if (time_not_ok((struct timeval *)info)) {
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (FALSE);
}
cu->cu_wait = *(struct timeval *)info;
@@ -672,7 +649,7 @@ clnt_dg_control(cl, request, info)
case CLSET_SVC_ADDR: /* set to new address */
addr = (struct netbuf *)info;
if (addr->len < sizeof cu->cu_raddr) {
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (FALSE);
}
(void) memcpy(&cu->cu_raddr, addr->buf, addr->len);
@@ -735,10 +712,10 @@ clnt_dg_control(cl, request, info)
cu->cu_connect = *(int *)info;
break;
default:
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (FALSE);
}
- release_fd_lock(cu->cu_fd, mask);
+ release_fd_lock(cu->cu_fd_lock, mask);
return (TRUE);
}
@@ -754,8 +731,8 @@ clnt_dg_destroy(cl)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (dg_fd_locks[cu_fd])
- cond_wait(&dg_cv[cu_fd], &clnt_fd_lock);
+ while (cu->cu_fd_lock->active)
+ cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
if (cu->cu_closeit)
(void)close(cu_fd);
XDR_DESTROY(&(cu->cu_outxdrs));
@@ -765,9 +742,10 @@ clnt_dg_destroy(cl)
if (cl->cl_tp && cl->cl_tp[0])
mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
mem_free(cl, sizeof (CLIENT));
+ cond_signal(&cu->cu_fd_lock->cv);
+ fd_lock_destroy(cu_fd, cu->cu_fd_lock, dg_fd_locks);
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &mask, NULL);
- cond_signal(&dg_cv[cu_fd]);
}
static struct clnt_ops *
diff --git a/src/clnt_fd_locks.h b/src/clnt_fd_locks.h
new file mode 100644
index 0000000..8263071
--- /dev/null
+++ b/src/clnt_fd_locks.h
@@ -0,0 +1,205 @@
+/*
+ * debug.h -- debugging routines for libtirpc
+ *
+ * Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
+ *
+ * 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 Sun Microsystems, 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.
+ */
+
+#ifndef _CLNT_FD_LOCKS_H
+#define _CLNT_FD_LOCKS_H
+
+#include <sys/queue.h>
+#include <errno.h>
+#include <reentrant.h>
+#include <rpc/xdr.h>
+
+
+/*
+ * This utility manages a list of per-fd locks for the clients.
+ *
+ * If MAX_FDLOCKS_PREALLOC is defined, a number of pre-fd locks will be
+ * pre-allocated. This number is the minimum of MAX_FDLOCKS_PREALLOC or
+ * the process soft limit of allowed fds.
+ */
+#ifdef MAX_FDLOCKS_PREALLOC
+static unsigned int fd_locks_prealloc = 0;
+#endif
+
+/* per-fd lock */
+struct fd_lock_t {
+ bool_t active;
+ cond_t cv;
+};
+typedef struct fd_lock_t fd_lock_t;
+
+
+/* internal type to store per-fd locks in a list */
+struct fd_lock_item_t {
+ /* fd_lock_t first so we can cast to fd_lock_item_t */
+ fd_lock_t fd_lock;
+ int fd;
+ unsigned int refs;
+ TAILQ_ENTRY(fd_lock_item_t) link;
+};
+typedef struct fd_lock_item_t fd_lock_item_t;
+#define to_fd_lock_item(fdlock_t_ptr) ((fd_lock_item_t*) fdlock_t_ptr)
+
+
+/* internal list of per-fd locks */
+typedef TAILQ_HEAD(,fd_lock_item_t) fd_lock_list_t;
+
+
+#ifdef MAX_FDLOCKS_PREALLOC
+
+/* With pre-allocation, keep track of both an array and a list */
+struct fd_locks_t {
+ fd_lock_list_t fd_lock_list;
+ fd_lock_t *fd_lock_array;
+};
+typedef struct fd_locks_t fd_locks_t;
+#define to_fd_lock_list(fd_locks_t_ptr) (&fd_locks_t_ptr->fd_lock_list)
+
+#else
+
+/* With no pre-allocation, just keep track of a list */
+typedef fd_lock_list_t fd_locks_t;
+#define to_fd_lock_list(fd_locks_t_ptr) ((fd_lock_list_t *) fd_locks_t_ptr)
+
+#endif
+
+
+/* allocate fd locks */
+static inline
+fd_locks_t* fd_locks_init() {
+ fd_locks_t *fd_locks;
+
+ fd_locks = (fd_locks_t *) mem_alloc(sizeof(fd_locks_t));
+ if (fd_locks == (fd_locks_t *) NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ TAILQ_INIT(to_fd_lock_list(fd_locks));
+
+#ifdef MAX_FDLOCKS_PREALLOC
+ size_t fd_lock_arraysz;
+
+ if (fd_locks_prealloc == 0) {
+ unsigned int dtbsize = __rpc_dtbsize();
+ if (0 < dtbsize && dtbsize < MAX_FDLOCKS_PREALLOC)
+ fd_locks_prealloc = dtbsize;
+ else
+ fd_locks_prealloc = MAX_FDLOCKS_PREALLOC;
+ }
+
+ if ( (size_t) fd_locks_prealloc > SIZE_MAX/sizeof(fd_lock_t)) {
+ errno = EOVERFLOW;
+ return (NULL);
+ }
+
+ fd_lock_arraysz = fd_locks_prealloc * sizeof (fd_lock_t);
+ fd_locks->fd_lock_array = (fd_lock_t *) mem_alloc(fd_lock_arraysz);
+ if (fd_locks->fd_lock_array == (fd_lock_t *) NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ else {
+ int i;
+
+ for (i = 0; i < fd_locks_prealloc; i++) {
+ fd_locks->fd_lock_array[i].active = FALSE;
+ cond_init(&fd_locks->fd_lock_array[i].cv, 0, (void *) 0);
+ }
+ }
+#endif
+
+ return fd_locks;
+}
+
+/* de-allocate fd locks */
+static inline
+void fd_locks_destroy(fd_locks_t *fd_locks) {
+#ifdef MAX_FDLOCKS_PREALLOC
+ fd_lock_t *array = fd_locks->fd_lock_array;
+ mem_free(array, fd_locks_prealloc * sizeof (fd_lock_t));
+#endif
+ fd_lock_item_t *item;
+ fd_lock_list_t *list = to_fd_lock_list(fd_locks);
+
+ TAILQ_FOREACH(item, list, link) {
+ cond_destroy(&item->fd_lock.cv);
+ mem_free(item, sizeof (*item));
+ }
+ mem_free(fd_locks, sizeof (*fd_locks));
+}
+
+/* allocate per-fd lock */
+static inline
+fd_lock_t* fd_lock_create(int fd, fd_locks_t *fd_locks) {
+#ifdef MAX_FDLOCKS_PREALLOC
+ if (fd < fd_locks_prealloc) {
+ return &fd_locks->fd_lock_array[fd];
+ }
+#endif
+ fd_lock_item_t* item;
+ fd_lock_list_t *list = to_fd_lock_list(fd_locks);
+
+ for (item = TAILQ_FIRST(list);
+ item != (fd_lock_item_t *) NULL && item->fd != fd;
+ item = TAILQ_NEXT(item, link));
+
+ if (item == (fd_lock_item_t *) NULL) {
+ item = (fd_lock_item_t *) mem_alloc(sizeof(fd_lock_item_t));
+ if (item == (fd_lock_item_t *) NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ item->fd = fd;
+ item->refs = 1;
+ item->fd_lock.active = FALSE;
+ cond_init(&item->fd_lock.cv, 0, (void *) 0);
+ TAILQ_INSERT_HEAD(list, item, link);
+ } else {
+ item->refs++;
+ }
+ return &item->fd_lock;
+}
+
+/* de-allocate per-fd lock */
+static inline
+void fd_lock_destroy(int fd, fd_lock_t *fd_lock, fd_locks_t *fd_locks) {
+#ifdef MAX_FDLOCKS_PREALLOC
+ if (fd < fd_locks_prealloc)
+ return;
+#endif
+ fd_lock_item_t* item = to_fd_lock_item(fd_lock);
+ item->refs--;
+ if (item->refs <= 0) {
+ TAILQ_REMOVE(to_fd_lock_list(fd_locks), item, link);
+ cond_destroy(&item->fd_lock.cv);
+ mem_free(item, sizeof (*item));
+ }
+}
+
+#endif /* _CLNT_FD_LOCKS_H */
diff --git a/src/clnt_vc.c b/src/clnt_vc.c
index ec58892..2f3dde6 100644
--- a/src/clnt_vc.c
+++ b/src/clnt_vc.c
@@ -67,6 +67,7 @@
#include <rpc/rpc.h>
#include "rpc_com.h"
+#include "clnt_fd_locks.h"
#define MCALL_MSG_SIZE 24
@@ -110,6 +111,7 @@ static int write_vc(void *, void *, int);
struct ct_data {
int ct_fd; /* connection's fd */
+ fd_lock_t *ct_fd_lock;
bool_t ct_closeit; /* close it on destroy */
struct timeval ct_wait; /* wait interval in milliseconds */
bool_t ct_waitset; /* wait set by clnt_control? */
@@ -124,27 +126,32 @@ struct ct_data {
};
/*
- * This machinery implements per-fd locks for MT-safety. It is not
- * sufficient to do per-CLIENT handle locks for MT-safety because a
- * user may create more than one CLIENT handle with the same fd behind
- * it. Therfore, we allocate an array of flags (vc_fd_locks), protected
- * by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables
- * similarly protected. Vc_fd_lock[fd] == 1 => a call is active on some
- * CLIENT handle created for that fd.
- * The current implementation holds locks across the entire RPC and reply.
- * Yes, this is silly, and as soon as this code is proven to work, this
- * should be the first thing fixed. One step at a time.
+ * This machinery implements per-fd locks for MT-safety. It is not
+ * sufficient to do per-CLIENT handle locks for MT-safety because a
+ * user may create more than one CLIENT handle with the same fd behind
+ * it.
+ *
+ * We keep track of a list of per-fd locks, protected by the clnt_fd_lock
+ * mutex. Each per-fd lock consists of a predicate indicating whether is
+ * active or not: fd_lock->active == TRUE => a call is active on some
+ * CLIENT handle created for that fd. Each fd predicate is guarded by a
+ * condition variable so that the global mutex can be unlocked while
+ * waiting for the predicate to change.
+ *
+ * The current implementation holds locks across the entire RPC and reply,
+ * including retransmissions. Yes, this is silly, and as soon as this
+ * code is proven to work, this should be the first thing fixed. One step
+ * at a time.
*/
-static int *vc_fd_locks;
+static fd_locks_t *vc_fd_locks;
extern pthread_mutex_t disrupt_lock;
extern mutex_t clnt_fd_lock;
-static cond_t *vc_cv;
-#define release_fd_lock(fd, mask) { \
+#define release_fd_lock(fd_lock, mask) { \
mutex_lock(&clnt_fd_lock); \
- vc_fd_locks[fd] = 0; \
+ fd_lock->active = FALSE; \
mutex_unlock(&clnt_fd_lock); \
thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL); \
- cond_signal(&vc_cv[fd]); \
+ cond_signal(&fd_lock->cv); \
}
static const char clnt_vc_errstr[] = "%s : %s";
@@ -181,6 +188,7 @@ clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
struct sockaddr_storage ss;
socklen_t slen;
struct __rpc_sockinfo si;
+ fd_lock_t *fd_lock;
mutex_lock(&disrupt_lock);
if (disrupt == 0)
@@ -201,49 +209,22 @@ clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- if (vc_fd_locks == (int *) NULL) {
- size_t cv_allocsz, fd_allocsz;
- unsigned int dtbsize = __rpc_dtbsize();
- struct rpc_createerr *ce = &get_rpc_createerr();
-
- if ( (size_t) dtbsize > SIZE_MAX/sizeof(cond_t)) {
- mutex_unlock(&clnt_fd_lock);
- thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
- ce->cf_stat = RPC_SYSTEMERROR;
- ce->cf_error.re_errno = EOVERFLOW;
- goto err;
- }
-
- fd_allocsz = dtbsize * sizeof (int);
- vc_fd_locks = (int *) mem_alloc(fd_allocsz);
- if (vc_fd_locks == (int *) NULL) {
- mutex_unlock(&clnt_fd_lock);
- thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
- ce->cf_stat = RPC_SYSTEMERROR;
- ce->cf_error.re_errno = ENOMEM;
- goto err;
- } else
- memset(vc_fd_locks, '\0', fd_allocsz);
-
- assert(vc_cv == (cond_t *) NULL);
- cv_allocsz = dtbsize * sizeof (cond_t);
- vc_cv = (cond_t *) mem_alloc(cv_allocsz);
- if (vc_cv == (cond_t *) NULL) {
- mem_free(vc_fd_locks, fd_allocsz);
- vc_fd_locks = (int *) NULL;
- mutex_unlock(&clnt_fd_lock);
- thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
+ if (vc_fd_locks == (fd_locks_t *) NULL) {
+ vc_fd_locks = fd_locks_init();
+ if (vc_fd_locks == (fd_locks_t *) NULL) {
+ struct rpc_createerr *ce = &get_rpc_createerr();
ce->cf_stat = RPC_SYSTEMERROR;
- ce->cf_error.re_errno = ENOMEM;
+ ce->cf_error.re_errno = errno;
goto err;
- } else {
- int i;
-
- for (i = 0; i < dtbsize; i++)
- cond_init(&vc_cv[i], 0, (void *) 0);
}
- } else
- assert(vc_cv != (cond_t *) NULL);
+ }
+ fd_lock = fd_lock_create(fd, vc_fd_locks);
+ if (fd_lock == (fd_lock_t *) NULL) {
+ struct rpc_createerr *ce = &get_rpc_createerr();
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = errno;
+ goto err;
+ }
/*
* Do not hold mutex during connect
@@ -279,6 +260,7 @@ clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
* Set up private data struct
*/
ct->ct_fd = fd;
+ ct->ct_fd_lock = fd_lock;
ct->ct_wait.tv_usec = 0;
ct->ct_waitset = FALSE;
ct->ct_addr.buf = malloc(raddr->maxlen);
@@ -361,17 +343,15 @@ clnt_vc_call(cl, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
bool_t shipnow;
int refreshes = 2;
sigset_t mask, newmask;
- int rpc_lock_value;
assert(cl != NULL);
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (vc_fd_locks[ct->ct_fd])
- cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
- rpc_lock_value = 1;
- vc_fd_locks[ct->ct_fd] = rpc_lock_value;
+ while (ct->ct_fd_lock->active)
+ cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
+ ct->ct_fd_lock->active = TRUE;
mutex_unlock(&clnt_fd_lock);
if (!ct->ct_waitset) {
/* If time is not within limits, we ignore it. */
@@ -395,22 +375,22 @@ call_again:
if (ct->ct_error.re_status == RPC_SUCCESS)
ct->ct_error.re_status = RPC_CANTENCODEARGS;
(void)xdrrec_endofrecord(xdrs, TRUE);
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (ct->ct_error.re_status);
}
if (! xdrrec_endofrecord(xdrs, shipnow)) {
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (ct->ct_error.re_status = RPC_CANTSEND);
}
if (! shipnow) {
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (RPC_SUCCESS);
}
/*
* Hack to provide rpc-based message passing
*/
if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return(ct->ct_error.re_status = RPC_TIMEDOUT);
}
@@ -424,14 +404,14 @@ call_again:
reply_msg.acpted_rply.ar_results.where = NULL;
reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
if (! xdrrec_skiprecord(xdrs)) {
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (ct->ct_error.re_status);
}
/* now decode and validate the response header */
if (! xdr_replymsg(xdrs, &reply_msg)) {
if (ct->ct_error.re_status == RPC_SUCCESS)
continue;
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (ct->ct_error.re_status);
}
if (reply_msg.rm_xid == x_id)
@@ -464,7 +444,7 @@ call_again:
if (refreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg))
goto call_again;
} /* end of unsuccessful completion */
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (ct->ct_error.re_status);
}
@@ -502,13 +482,13 @@ clnt_vc_freeres(cl, xdr_res, res_ptr)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (vc_fd_locks[ct->ct_fd])
- cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
+ while (ct->ct_fd_lock->active)
+ cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
xdrs->x_op = XDR_FREE;
dummy = (*xdr_res)(xdrs, res_ptr);
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
- cond_signal(&vc_cv[ct->ct_fd]);
+ cond_signal(&ct->ct_fd_lock->cv);
return dummy;
}
@@ -530,7 +510,6 @@ clnt_vc_control(cl, request, info)
void *infop = info;
sigset_t mask;
sigset_t newmask;
- int rpc_lock_value;
u_int32_t tmp;
u_int32_t ltmp;
@@ -541,20 +520,19 @@ clnt_vc_control(cl, request, info)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (vc_fd_locks[ct->ct_fd])
- cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
- rpc_lock_value = 1;
- vc_fd_locks[ct->ct_fd] = rpc_lock_value;
+ while (ct->ct_fd_lock->active)
+ cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
+ ct->ct_fd_lock->active = TRUE;
mutex_unlock(&clnt_fd_lock);
switch (request) {
case CLSET_FD_CLOSE:
ct->ct_closeit = TRUE;
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (TRUE);
case CLSET_FD_NCLOSE:
ct->ct_closeit = FALSE;
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (TRUE);
default:
break;
@@ -562,13 +540,13 @@ clnt_vc_control(cl, request, info)
/* for other requests which use info */
if (info == NULL) {
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (FALSE);
}
switch (request) {
case CLSET_TIMEOUT:
if (time_not_ok((struct timeval *)info)) {
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (FALSE);
}
ct->ct_wait = *(struct timeval *)infop;
@@ -588,7 +566,7 @@ clnt_vc_control(cl, request, info)
*(struct netbuf *)info = ct->ct_addr;
break;
case CLSET_SVC_ADDR: /* set to new address */
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (FALSE);
case CLGET_XID:
/*
@@ -642,10 +620,10 @@ clnt_vc_control(cl, request, info)
break;
default:
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (FALSE);
}
- release_fd_lock(ct->ct_fd, mask);
+ release_fd_lock(ct->ct_fd_lock, mask);
return (TRUE);
}
@@ -666,8 +644,8 @@ clnt_vc_destroy(cl)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (vc_fd_locks[ct_fd])
- cond_wait(&vc_cv[ct_fd], &clnt_fd_lock);
+ while (ct->ct_fd_lock->active)
+ cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
if (ct->ct_closeit && ct->ct_fd != -1) {
(void)close(ct->ct_fd);
}
@@ -680,9 +658,10 @@ clnt_vc_destroy(cl)
if (cl->cl_tp && cl->cl_tp[0])
mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
mem_free(cl, sizeof(CLIENT));
+ cond_signal(&ct->ct_fd_lock->cv);
+ fd_lock_destroy(ct_fd, ct->ct_fd_lock, vc_fd_locks);
mutex_unlock(&clnt_fd_lock);
thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
- cond_signal(&vc_cv[ct_fd]);
}
/*
diff --git a/tirpc/reentrant.h b/tirpc/reentrant.h
index 5f5c96e..5bb581a 100644
--- a/tirpc/reentrant.h
+++ b/tirpc/reentrant.h
@@ -57,6 +57,7 @@
#define mutex_unlock(m) pthread_mutex_unlock(m)
#define cond_init(c, a, p) pthread_cond_init(c, a)
+#define cond_destroy(c) pthread_cond_destroy(c)
#define cond_signal(m) pthread_cond_signal(m)
#define cond_broadcast(m) pthread_cond_broadcast(m)
#define cond_wait(c, m) pthread_cond_wait(c, m)
--
1.8.3.1

View File

@ -0,0 +1,156 @@
From 3f2a5459fb00c2f529d68a4a0fd7f367a77fa65a Mon Sep 17 00:00:00 2001
From: Attila Kovacs <attila.kovacs@cfa.harvard.edu>
Date: Tue, 26 Jul 2022 15:24:01 -0400
Subject: [PATCH] thread safe clnt destruction.
If clnt_dg_destroy() or clnt_vc_destroy() is awoken with other blocked
operations pending (such as clnt_*_call(), clnt_*_control(), or
clnt_*_freeres()) but no active operation currently being executed, then the
client gets destroyed. Then, as the other blocked operations get subsequently
awoken, they will try operate on an invalid client handle, potentially causing
unpredictable behavior and stack corruption.
Signed-off-by: Attila Kovacs <attipaci@gmail.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
src/clnt_dg.c | 13 ++++++++++++-
src/clnt_fd_locks.h | 2 ++
src/clnt_vc.c | 13 ++++++++++++-
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/src/clnt_dg.c b/src/clnt_dg.c
index b2043ac..166af63 100644
--- a/src/clnt_dg.c
+++ b/src/clnt_dg.c
@@ -101,6 +101,7 @@ extern mutex_t clnt_fd_lock;
#define release_fd_lock(fd_lock, mask) { \
mutex_lock(&clnt_fd_lock); \
fd_lock->active = FALSE; \
+ fd_lock->pending--; \
thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
cond_signal(&fd_lock->cv); \
mutex_unlock(&clnt_fd_lock); \
@@ -311,6 +312,7 @@ clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
+ cu->cu_fd_lock->pending++;
while (cu->cu_fd_lock->active)
cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
cu->cu_fd_lock->active = TRUE;
@@ -571,10 +573,12 @@ clnt_dg_freeres(cl, xdr_res, res_ptr)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
+ cu->cu_fd_lock->pending++;
while (cu->cu_fd_lock->active)
cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
xdrs->x_op = XDR_FREE;
dummy = (*xdr_res)(xdrs, res_ptr);
+ cu->cu_fd_lock->pending--;
thr_sigsetmask(SIG_SETMASK, &mask, NULL);
cond_signal(&cu->cu_fd_lock->cv);
mutex_unlock(&clnt_fd_lock);
@@ -602,6 +606,7 @@ clnt_dg_control(cl, request, info)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
+ cu->cu_fd_lock->pending++;
while (cu->cu_fd_lock->active)
cond_wait(&cu->cu_fd_lock->cv, &clnt_fd_lock);
cu->cu_fd_lock->active = TRUE;
@@ -742,8 +747,14 @@ clnt_dg_destroy(cl)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (cu_fd_lock->active)
+ /* wait until all pending operations on client are completed. */
+ while (cu_fd_lock->pending > 0) {
+ /* If a blocked operation can be awakened, then do it. */
+ if (cu_fd_lock->active == FALSE)
+ cond_signal(&cu_fd_lock->cv);
+ /* keep waiting... */
cond_wait(&cu_fd_lock->cv, &clnt_fd_lock);
+ }
if (cu->cu_closeit)
(void)close(cu_fd);
XDR_DESTROY(&(cu->cu_outxdrs));
diff --git a/src/clnt_fd_locks.h b/src/clnt_fd_locks.h
index 359f995..6ba62cb 100644
--- a/src/clnt_fd_locks.h
+++ b/src/clnt_fd_locks.h
@@ -50,6 +50,7 @@ static unsigned int fd_locks_prealloc = 0;
/* per-fd lock */
struct fd_lock_t {
bool_t active;
+ int pending; /* Number of pending operations on fd */
cond_t cv;
};
typedef struct fd_lock_t fd_lock_t;
@@ -180,6 +181,7 @@ fd_lock_t* fd_lock_create(int fd, fd_locks_t *fd_locks) {
item->fd = fd;
item->refs = 1;
item->fd_lock.active = FALSE;
+ item->fd_lock.pending = 0;
cond_init(&item->fd_lock.cv, 0, (void *) 0);
TAILQ_INSERT_HEAD(list, item, link);
} else {
diff --git a/src/clnt_vc.c b/src/clnt_vc.c
index 3c73e65..5bbc78b 100644
--- a/src/clnt_vc.c
+++ b/src/clnt_vc.c
@@ -153,6 +153,7 @@ extern mutex_t clnt_fd_lock;
#define release_fd_lock(fd_lock, mask) { \
mutex_lock(&clnt_fd_lock); \
fd_lock->active = FALSE; \
+ fd_lock->pending--; \
thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL); \
cond_signal(&fd_lock->cv); \
mutex_unlock(&clnt_fd_lock); \
@@ -357,6 +358,7 @@ clnt_vc_call(cl, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
+ ct->ct_fd_lock->pending++;
while (ct->ct_fd_lock->active)
cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
ct->ct_fd_lock->active = TRUE;
@@ -495,10 +497,12 @@ clnt_vc_freeres(cl, xdr_res, res_ptr)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
+ ct->ct_fd_lock->pending++;
while (ct->ct_fd_lock->active)
cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
xdrs->x_op = XDR_FREE;
dummy = (*xdr_res)(xdrs, res_ptr);
+ ct->ct_fd_lock->pending--;
thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
cond_signal(&ct->ct_fd_lock->cv);
mutex_unlock(&clnt_fd_lock);
@@ -533,6 +537,7 @@ clnt_vc_control(cl, request, info)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
+ ct->ct_fd_lock->pending++;
while (ct->ct_fd_lock->active)
cond_wait(&ct->ct_fd_lock->cv, &clnt_fd_lock);
ct->ct_fd_lock->active = TRUE;
@@ -655,8 +660,14 @@ clnt_vc_destroy(cl)
sigfillset(&newmask);
thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
mutex_lock(&clnt_fd_lock);
- while (ct_fd_lock->active)
+ /* wait until all pending operations on client are completed. */
+ while (ct_fd_lock->pending > 0) {
+ /* If a blocked operation can be awakened, then do it. */
+ if (ct_fd_lock->active == FALSE)
+ cond_signal(&ct_fd_lock->cv);
+ /* keep waiting... */
cond_wait(&ct_fd_lock->cv, &clnt_fd_lock);
+ }
if (ct->ct_closeit && ct->ct_fd != -1) {
(void)close(ct->ct_fd);
}
--
1.8.3.1

Binary file not shown.

BIN
libtirpc-1.2.6.tar.bz2 Normal file

Binary file not shown.

View File

@ -1,11 +1,20 @@
Name: libtirpc Name: libtirpc
Version: 1.1.4 Version: 1.2.6
Release: 1 Release: 5
Summary: Transport-independent RPC library Summary: Transport-independent RPC library
License: SISSL and BSD License: SISSL and BSD
URL: http://git.linux-nfs.org/?p=steved/libtirpc.git;a=summary URL: http://git.linux-nfs.org/?p=steved/libtirpc.git;a=summary
Source0: http://downloads.sourceforge.net/libtirpc/%{name}-%{version}.tar.bz2 Source0: http://downloads.sourceforge.net/libtirpc/%{name}-%{version}.tar.bz2
Patch01: 0001-update-libtirpc-to-enable-tcp-port-listening.patch Patch01: 0001-update-libtirpc-to-enable-tcp-port-listening.patch
Patch02: backport-CVE-2021-46828.patch
Patch03: backport-libtirpc-replace-array-with-list-for-per-fd-locks.patch
Patch04: backport-Fix-memory-management-issues-of-fd-locks.patch
Patch05: backport-Fix-use-after-free-accessing-the-error-number.patch
Patch06: backport-Eliminate-deadlocks-in-connects-with-an-MT-environment.patch
Patch07: backport-clnt_dg_freeres-uncleared-set-active-state-may-deadlock.patch
Patch08: backport-thread-safe-clnt-destruction.patch
Patch09: backport-fix-rpc_gss_seccreate-passed-in-cred.patch
BuildRequires: automake autoconf libtool pkgconfig krb5-devel BuildRequires: automake autoconf libtool pkgconfig krb5-devel
%description %description
@ -14,7 +23,7 @@ Libtirpc is a Transport-Independent RPC library for Linux
%package devel %package devel
Summary: Development files for the %{name} Summary: Development files for the %{name}
Requires: %{name} = %{version}-%{release} Requires: %{name} = %{version}-%{release}
Requires: pkgconfig man-db Requires: pkgconfig
%description devel %description devel
The %{name}-devel package contains development files for %{name}. The %{name}-devel package contains development files for %{name}.
@ -24,6 +33,8 @@ The %{name}-devel package contains development files for %{name}.
%prep %prep
%autosetup -n %{name}-%{version} -p1 %autosetup -n %{name}-%{version} -p1
find . -name "*.orig" | xargs rm -f
%build %build
sh autogen.sh sh autogen.sh
autoreconf -fisv autoreconf -fisv
@ -31,6 +42,9 @@ autoreconf -fisv
%make_build %make_build
%install %install
mkdir -p %{buildroot}/etc
mkdir -p %{buildroot}/%{_lib}
mkdir -p %{buildroot}%{_libdir}/pkgconfig
%make_install libdir=/%{_lib} pkgconfigdir=%{_libdir}/pkgconfig %make_install libdir=/%{_lib} pkgconfigdir=%{_libdir}/pkgconfig
%delete_la %delete_la
@ -38,9 +52,6 @@ mv %{buildroot}%{_mandir}/man3 %{buildroot}%{_mandir}/man3t
%ldconfig_scriptlets %ldconfig_scriptlets
%post devel
%postun devel
%files %files
%defattr(-,root,root) %defattr(-,root,root)
%doc AUTHORS %doc AUTHORS
@ -63,5 +74,26 @@ mv %{buildroot}%{_mandir}/man3 %{buildroot}%{_mandir}/man3t
%{_mandir}/*/* %{_mandir}/*/*
%changelog %changelog
* Tue Sep 10 2024 gaihuiying <eaglegai@163.com> - 1.2.6-5
- backport to fix rpc_gss_seccreate passed in credential
* Wed Nov 16 2022 gaihuiying <eaglegai@163.com> - 1.2.6-4
- backport to fix "mount.nfs: Cannot allocate memory" error
* Tue Oct 11 2022 zhangshaoning <zhangshaoning@uniontech.com> - 1.2.6-3
- Fix obsoletes bug when both new and old versions existed
* Fri Jul 22 2022 gaihuiying <eaglegai@163.com> - 1.2.6-2
- Type:CVE
- ID:CVE-2021-46828
- SUG:NA
- DESC:fix CVE-2021-46828
* Mon Apr 20 2020 openEuler Buildteam <buildteam@openeuler.org> - 1.2.6-1
- update to 1.2.6
* Mon Mar 23 2020 openEuler Buildteam <buildteam@openeuler.org> - 1.1.4-2
- Remove devel in iso
* Thu Aug 29 2019 openEuler Buildteam <buildteam@openeuler.org> - 1.1.4-1 * Thu Aug 29 2019 openEuler Buildteam <buildteam@openeuler.org> - 1.1.4-1
- Package init - Package init