[dpdk-dev] [PATCH 2/2] eal: resolve getentropy at run time for random seed

Mattias Rönnblom mattias.ronnblom at ericsson.com
Wed Apr 22 22:14:30 CEST 2020


On 2020-04-22 19:44, Dan Gora wrote:
> On Wed, Apr 22, 2020 at 5:28 AM Mattias Rönnblom
> <mattias.ronnblom at ericsson.com> wrote:
>> On 2020-04-21 21:54, Dan Gora wrote:
>>> The getentropy() function was introduced into glibc v2.25 and so is
>>> not available on all supported platforms.  Previously, if DPDK was
>>> compiled (using meson) on a system which has getentropy(), it would
>>> introduce a dependency on glibc v2.25 which would prevent that binary
>>> from running on a system with an older glibc.  Similarly if DPDK was
>>> compiled on a system which did not have getentropy(), getentropy()
>>> could not be used even if the execution system supported it.
>>>
>>> Introduce a new static function, __rte_getentropy() which will try to
>>> resolve the getentropy() function dynamically using dlopen()/dlsym(),
>>> returning failure if the getentropy() function cannot be resolved or
>>> if it fails.
>>
>> Two other options: providing a DPDK-native syscall wrapper for
>> getrandom(), or falling back to reading /dev/urandom. Have you
>> considered any of those two options? If so, why do you prefer
>> dlopen()/dlsym()?
> I didn't give any thought at all to using /dev/urandom.  The goal was
> not really to change how the thing worked, just to remove the
> dependency on glibc 2.25.


/dev/urandom is basically only a different interface to the same 
underlying mechanism.

Such an alternative would look something like:

static int
getentropy(void *buffer, size_t length)
{
         int rc = -1;
         int old_errno = errno;
         int fd;

         fd = open("/dev/urandom", O_RDONLY);

         if (fd < 0)
                 goto out;

         if (read(fd, buffer, length) != length)
                 goto out_close;

         rc = 0;

out_close:
         close(fd);
out:
         errno = old_errno;

         return rc;
}


> Using a syscall wrapper is not really going to be any easier, and in
> fact probably more complicated.  Here is the code in glibc for
> getentropy():
>
> int
> getentropy (void *buffer, size_t length)
> {
>    /* The interface is documented to return EIO for buffer lengths
>       longer than 256 bytes.  */
>    if (length > 256)
>      {
>        __set_errno (EIO);
>        return -1;
>      }
>
>    /* Try to fill the buffer completely.  Even with the 256 byte limit
>       above, we might still receive an EINTR error (when blocking
>       during boot).  */
>    void *end = buffer + length;
>    while (buffer < end)
>      {
>        /* NB: No cancellation point.  */
>        ssize_t bytes = INLINE_SYSCALL_CALL (getrandom, buffer, end - buffer, 0);
>        if (bytes < 0)
>          {
>            if (errno == EINTR)
>              /* Try again if interrupted by a signal.  */
>              continue;
>            else
>              return -1;
>          }
>        if (bytes == 0)
>          {
>            /* No more bytes available.  This should not happen under
>               normal circumstances.  */
>            __set_errno (EIO);
>            return -1;
>          }
>        /* Try again in case of a short read.  */
>        buffer += bytes;
>      }
>    return 0;
> }
>
> __rte_getentropy() is easier than this.  Plus the syscall interface
> does not solve the problem of being able to compile on a system with
> the syscall and run it on a system without it or vice versa, which is
> what I was trying to solve.
>
>> Failure to run on old libc seems like a non-issue to me.
> Well, again, it's a new dependency that didn't exist before.. We sell
> to telco customers, so we have to support 10s of different target
> platforms of various ages.  If they update their system, we'd have to
> recompile our code to be able to use getentropy().  Similarly, if we
> compiled on a system which has getentropy(), but the target system
> doesn't, then they cannot run our binary because of the glibc 2.25
> dependency.  That means that we have to have separate versions with
> and without getentropy().  It's a maintenance headache for no real
> benefit.


I'm not sure I follow. Why would you need to recompile DPDK in case they 
upgrade their system? It sounds like you care about initial seeding, 
since you want getentropy() if it exists, but then in the next paragraph 
you want to throw it out, so I'm a little confused.


Why doesn't the standard practice of compiling against the oldest 
supported libc work for you?


> To my mind, since getentropy() can block it seems like it would
> probably be better to just remove it entirely, but I suppose that's up
> to the person(s) who put it in in the first place.


Maybe I'm wrong, but I found it unlikely that a DPDK application would 
start before the entropy pool was initialized. After this point, 
getentropy() will not block. Do you consider this a real problem?


>>> This also allows getentropy() to be used as the random seed source
>>> when the traditional Makefile build for DPDK is used.
>>>
>>> Signed-off-by: Dan Gora <dg at adax.com>
>>> ---
>>>    lib/librte_eal/common/rte_random.c | 33 ++++++++++++++++++++++++------
>>>    lib/librte_eal/meson.build         |  3 ---
>>>    2 files changed, 27 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/lib/librte_eal/common/rte_random.c b/lib/librte_eal/common/rte_random.c
>>> index df02f1307..f8203f4c7 100644
>>> --- a/lib/librte_eal/common/rte_random.c
>>> +++ b/lib/librte_eal/common/rte_random.c
>>> @@ -7,6 +7,7 @@
>>>    #endif
>>>    #include <stdlib.h>
>>>    #include <unistd.h>
>>> +#include <dlfcn.h>
>>>
>>>    #include <rte_branch_prediction.h>
>>>    #include <rte_cycles.h>
>>> @@ -176,18 +177,38 @@ rte_rand_max(uint64_t upper_bound)
>>>        return res;
>>>    }
>>>
>>> +/* Try to use the getentropy() function from glibc >= 2.25 */
>>> +static int
>>> +__rte_getentropy(uint64_t *ge_seed)
>>> +{
>>> +     void *handle = NULL;
>>> +     void **sym;
>>> +     int (*getentropy_p)(void *__buffer, size_t __length);
>>> +     int gc_rc;
>>> +
>>> +     handle = dlopen("libc.so.6", RTLD_LAZY);
>>> +     if (!handle)
>> != NULL
>>> +             return -1;
>>> +
>>> +     sym = dlsym(handle, "getentropy");
>>> +     if (!sym || !*sym)
>> != NULL again
>>
>>
>> dlsym() returns a "void *". The man page says nothing about
>> de-referencing it. Is that allowed?
> This was blindly stolen from mlx5_common.c, so blame them :P
>
>>> +             /* Cannot resolve getentropy */
>>> +             return -1;
>> Missing a dlclose()
> This was fixed in version 2 of my patch...




More information about the dev mailing list