pipewire: add pw_random()

Make pw_getrandom() more usable by handling the EINTR case and returning
< 0 when there was an error or not enough random data was available.

Make a new pw_random() function that uses pw_getrandom() but falls back
to a pseudo random number generator otherwise. This pseudo random number
generator is seeded with either data from the urandom source or from the
current time when pipewire is initialized.

In most cases where crytographic security is not required pw_random()
should be easier to use.
This commit is contained in:
Wim Taymans 2023-02-22 15:21:24 +01:00
parent 0b69f37a7c
commit 4e298f2fe7
6 changed files with 70 additions and 25 deletions

View file

@ -400,10 +400,8 @@ struct avb_maap *avb_maap_register(struct server *server)
maap->server = server;
pw_log_info("0x%"PRIx64" %d", server->entity_id, server->ifindex);
if (pw_getrandom(maap->xsubi, sizeof(maap->xsubi), 0) != sizeof(maap->xsubi)) {
res = -errno;
goto error_free;
}
pw_random(maap->xsubi, sizeof(maap->xsubi));
load_state(maap);
maap->source = pw_loop_add_io(server->impl->loop, fd, SPA_IO_IN, true, on_socket_data, maap);

View file

@ -413,17 +413,7 @@ struct pw_impl_core *pw_context_create_core(struct pw_context *context,
this->info.user_name = pw_get_user_name();
this->info.host_name = pw_get_host_name();
this->info.version = pw_get_library_version();
do {
res = pw_getrandom(&this->info.cookie,
sizeof(this->info.cookie), 0);
} while ((res == -1) && (errno == EINTR));
if (res == -1) {
res = -errno;
goto error_exit;
} else if (res != sizeof(this->info.cookie)) {
res = -ENODATA;
goto error_exit;
}
this->info.cookie = pw_rand32();
this->info.name = name;
spa_hook_list_init(&this->listener_list);

View file

@ -569,6 +569,8 @@ void pw_init(int *argc, char **argv[])
if (support->init_count > 0)
goto done;
pw_random_init();
pthread_mutex_lock(&support_lock);
support->in_valgrind = RUNNING_ON_VALGRIND;

View file

@ -1276,6 +1276,8 @@ bool pw_log_is_default(void);
void pw_log_init(void);
void pw_log_deinit(void);
void pw_random_init();
void pw_settings_init(struct pw_context *context);
int pw_settings_expose(struct pw_context *context);
void pw_settings_clean(struct pw_context *context);

View file

@ -11,6 +11,7 @@
#include <sys/random.h>
#endif
#include <string.h>
#include <time.h>
#include <pipewire/array.h>
#include <pipewire/log.h>
@ -153,16 +154,7 @@ char *pw_strip(char *str, const char *whitespace)
return str;
}
/** Fill a buffer with random data
* \param buf a buffer to fill
* \param buflen the number of bytes to fill
* \param flags optional flags
* \return the number of bytes filled
*
* Fill \a buf with \a buflen random bytes.
*/
SPA_EXPORT
ssize_t pw_getrandom(void *buf, size_t buflen, unsigned int flags)
static inline ssize_t make_random(void *buf, size_t buflen, unsigned int flags)
{
ssize_t bytes;
int read_errno;
@ -183,6 +175,63 @@ ssize_t pw_getrandom(void *buf, size_t buflen, unsigned int flags)
return bytes;
}
/** Fill a buffer with random data
* \param buf a buffer to fill
* \param buflen the number of bytes to fill
* \param flags optional flags
* \return the number of bytes filled
*
* Fill \a buf with \a buflen random bytes.
*/
SPA_EXPORT
ssize_t pw_getrandom(void *buf, size_t buflen, unsigned int flags)
{
ssize_t res;
do {
res = make_random(buf, buflen, flags);
} while ((res == -1) && (errno == EINTR));
if (res == -1)
return -errno;
if ((size_t)res != buflen)
return -ENODATA;
return res;
}
static char statebuf[256];
static struct random_data random_state;
/** Fill a buffer with random data
* \param buf a buffer to fill
* \param buflen the number of bytes to fill
*
* Fill \a buf with \a buflen random bytes. This functions uses
* pw_getrandom() but falls back to a pseudo random number
* generator in case of failure.
*/
SPA_EXPORT
void pw_random(void *buf, size_t buflen)
{
if (pw_getrandom(buf, buflen, 0) < 0) {
uint8_t *p = buf;
while (buflen-- > 0) {
int32_t val;
random_r(&random_state, &val);
*p++ = (uint8_t) val;;
}
}
}
void pw_random_init()
{
unsigned int seed;
if (pw_getrandom(&seed, sizeof(seed), 0) < 0) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
seed = (unsigned int) SPA_TIMESPEC_TO_NSEC(&ts);
}
initstate_r(seed, statebuf, sizeof(statebuf), &random_state);
}
SPA_EXPORT
void* pw_reallocarray(void *ptr, size_t nmemb, size_t size)
{

View file

@ -72,6 +72,10 @@ pw_strip(char *str, const char *whitespace);
SPA_WARN_UNUSED_RESULT
ssize_t pw_getrandom(void *buf, size_t buflen, unsigned int flags);
void pw_random(void *buf, size_t buflen);
#define pw_rand32() ({ uint32_t val; pw_random(&val, sizeof(val)); val; })
void* pw_reallocarray(void *ptr, size_t nmemb, size_t size);
#ifdef PW_ENABLE_DEPRECATED