Hi,

On Mon, Mar 8, 2021 at 2:11 AM Gert Doering <g...@greenie.muc.de> wrote:

> If --mlock is used, the amount of memory OpenVPN can use is guarded
> by the RLIMIT_MEMLOCK value (see mlockall(2)).  The OS default for this
> is usually 64 Kbyte, which is enough for OpenVPN to initialize, but
> as soon as the first TLS handshake comes it, OpenVPN will crash due
> to "ouf of memory", and might even end up in a crash loop.
>
> Steady-state OpenVPN requires between 8 MB and 30-50 MB (servers with
> many concurrent clients) of memory.  TLS renegotiation with EC keys
> requires up to 90 MB of transient memory.
>
> So: with this patch, we check if getrlimit() is available, and if yes,
> log the amount of mlock'able memory.  If the amount is below 100 MB,
> which is an arbitrary value "large enough for most smaller deployments",
> we try to increase the limits to 100 MB, and abort if this fails.
>
> v2:
>   change arbitrary number to 100 MB, introduce #define for it
>   not only check but also increase with setrlimit()
>   uncrustify fixes
>
> Trac: #1390
>
> Signed-off-by: Gert Doering <g...@greenie.muc.de>
> ---
>  configure.ac                         |  2 +-
>  doc/man-sections/generic-options.rst |  7 +++++++
>  src/openvpn/platform.c               | 29 ++++++++++++++++++++++++++++
>  src/openvpn/platform.h               |  4 ++++
>  4 files changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/configure.ac b/configure.ac
> index 1ab8fe59..c65df3e2 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -645,7 +645,7 @@ AC_FUNC_FORK
>
>  AC_CHECK_FUNCS([ \
>         daemon chroot getpwnam setuid nice system getpid dup dup2 \
> -       getpass syslog openlog mlockall getgrnam setgid \
> +       getpass syslog openlog mlockall getrlimit getgrnam setgid \
>         setgroups stat flock readv writev time gettimeofday \
>         ctime memset vsnprintf strdup \
>         setsid chdir putenv getpeername unlink \
> diff --git a/doc/man-sections/generic-options.rst
> b/doc/man-sections/generic-options.rst
> index d5f08839..c026529e 100644
> --- a/doc/man-sections/generic-options.rst
> +++ b/doc/man-sections/generic-options.rst
> @@ -237,6 +237,13 @@ which mode OpenVPN is configured as.
>    likely fail. The limit can be increased using ulimit or systemd
>    directives depending on how OpenVPN is started.
>
> +  If the platform has the getrlimit(2) system call, OpenVPN will check
> +  for the amount of mlock-able memory before calling mlockall(2), and
> +  tries to increase the limit to 100 MB if less than this is configured.
> +  100 Mb is somewhat arbitrary - it is enough for a moderately-sized
> +  OpenVPN deployment, but the memory usage might go beyond that if the
> +  number of concurrent clients is high.
> +
>  --nice n
>    Change process priority after initialization (``n`` greater than 0 is
>    lower priority, ``n`` less than zero is higher priority).
> diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c
> index ef688c23..dfbf50c0 100644
> --- a/src/openvpn/platform.c
> +++ b/src/openvpn/platform.c
> @@ -193,6 +193,35 @@ void
>  platform_mlockall(bool print_msg)
>  {
>  #ifdef HAVE_MLOCKALL
> +
> +#ifdef HAVE_GETRLIMIT
> +#define MIN_LOCKED_MEM_MB 100
> +    struct rlimit rl;
> +    if (getrlimit(RLIMIT_MEMLOCK, &rl) < 0)
> +    {
> +        msg(M_WARN | M_ERRNO, "WARNING: getrlimit(RLIMIT_MEMLOCK)
> failed");
> +    }
> +    else
> +    {
> +        msg(M_INFO, "mlock: MEMLOCK limit: soft=%ld KB, hard=%ld KB",
> +            ((long int) rl.rlim_cur) / 1024, ((long int) rl.rlim_max) /
> 1024);
> +        if (rl.rlim_cur < MIN_LOCKED_MEM_MB*1024*1024)
> +        {
> +            msg(M_INFO, "mlock: RLIMIT_MEMLOCK < %d MB, increase limit",
> +                MIN_LOCKED_MEM_MB);
> +            rl.rlim_cur = MIN_LOCKED_MEM_MB*1024*1024;
> +            if (rl.rlim_max < rl.rlim_cur)
> +            {
> +                rl.rlim_max = rl.rlim_cur;
> +            }
> +            if (setrlimit(RLIMIT_MEMLOCK, &rl) < 0)
> +            {
> +                msg(M_FATAL | M_ERRNO, "ERROR: setrlimit() failed");
> +            }
> +        }
> +    }
> +#endif
> +
>      if (mlockall(MCL_CURRENT | MCL_FUTURE))
>      {
>          msg(M_WARN | M_ERRNO, "WARNING: mlockall call failed");
> diff --git a/src/openvpn/platform.h b/src/openvpn/platform.h
> index 01f3200c..02c23e38 100644
> --- a/src/openvpn/platform.h
> +++ b/src/openvpn/platform.h
> @@ -48,6 +48,10 @@
>  #include <stdio.h>
>  #endif
>
> +#ifdef HAVE_GETRLIMIT
> +#include <sys/resource.h>
> +#endif
> +
>  #include "basic.h"
>  #include "buffer.h"
>
> --
> 2.26.2
>

Looks good to me. We call mlockall (and now setrlimit) early before
dropping privileges, so this should take care of at least the naive user
error of running with "--mlock --user foo" and the default low memlock
limit.

Acked-by: selva.n...@gmail.com

Selva
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to