]> wimlib.net Git - wimlib/blobdiff - src/util.c
v1.14.4
[wimlib] / src / util.c
index f22d451e6e8f4b4fd08c5a023dbaecb90ea62017..52af147d3f122fe5240cced3ea3484885017234b 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2012, 2013, 2014 Eric Biggers
+ * Copyright 2012-2023 Eric Biggers
  *
  * This file is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
- * along with this file; if not, see http://www.gnu.org/licenses/.
+ * along with this file; if not, see https://www.gnu.org/licenses/.
  */
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
+#include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #ifdef HAVE_SYS_SYSCTL_H
+#  include <sys/types.h>
 #  include <sys/sysctl.h>
 #endif
+#ifdef HAVE_SYS_SYSCALL_H
+#  include <sys/syscall.h>
+#endif
 #include <unistd.h>
 
 #include "wimlib.h"
@@ -37,7 +43,6 @@
 #include "wimlib/error.h"
 #include "wimlib/timestamp.h"
 #include "wimlib/util.h"
-#include "wimlib/xml.h"
 
 /*******************
  * Memory allocation
@@ -81,7 +86,14 @@ void *
 wimlib_calloc(size_t nmemb, size_t size)
 {
        size_t total_size = nmemb * size;
-       void *p = MALLOC(total_size);
+       void *p;
+
+       if (size != 0 && nmemb > SIZE_MAX / size) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       p = MALLOC(total_size);
        if (p)
                p = memset(p, 0, total_size);
        return p;
@@ -90,46 +102,27 @@ wimlib_calloc(size_t nmemb, size_t size)
 char *
 wimlib_strdup(const char *str)
 {
-       size_t size;
-       char *p;
-
-       size = strlen(str);
-       p = MALLOC(size + 1);
-       if (p)
-               p = memcpy(p, str, size + 1);
-       return p;
+       return memdup(str, strlen(str) + 1);
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 wchar_t *
 wimlib_wcsdup(const wchar_t *str)
 {
-       size_t size;
-       wchar_t *p;
-
-       size = wcslen(str);
-       p = MALLOC((size + 1) * sizeof(wchar_t));
-       if (p)
-               p = wmemcpy(p, str, size + 1);
-       return p;
+       return memdup(str, (wcslen(str) + 1) * sizeof(wchar_t));
 }
 #endif
 
 void *
 wimlib_aligned_malloc(size_t size, size_t alignment)
 {
-       wimlib_assert(alignment != 0 && is_power_of_2(alignment) &&
-                     alignment <= 4096);
-
-       const uintptr_t mask = alignment - 1;
-       char *ptr = NULL;
-       char *raw_ptr;
-
-       raw_ptr = MALLOC(mask + sizeof(size_t) + size);
-       if (raw_ptr) {
-               ptr = (char *)raw_ptr + sizeof(size_t);
-               ptr = (void *)(((uintptr_t)ptr + mask) & ~mask);
-               *((size_t *)ptr - 1) = ptr - raw_ptr;
+       wimlib_assert(is_power_of_2(alignment));
+
+       void *ptr = MALLOC(sizeof(void *) + alignment - 1 + size);
+       if (ptr) {
+               void *orig_ptr = ptr;
+               ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment);
+               ((void **)ptr)[-1] = orig_ptr;
        }
        return ptr;
 }
@@ -138,7 +131,7 @@ void
 wimlib_aligned_free(void *ptr)
 {
        if (ptr)
-               FREE((char *)ptr - *((size_t *)ptr - 1));
+               FREE(((void **)ptr)[-1]);
 }
 
 void *
@@ -159,9 +152,6 @@ wimlib_set_memory_allocator(void *(*malloc_func)(size_t),
        wimlib_malloc_func  = malloc_func  ? malloc_func  : malloc;
        wimlib_free_func    = free_func    ? free_func    : free;
        wimlib_realloc_func = realloc_func ? realloc_func : realloc;
-
-       xml_set_memory_allocator(wimlib_malloc_func, wimlib_free_func,
-                                wimlib_realloc_func);
        return 0;
 }
 
@@ -176,43 +166,112 @@ void *mempcpy(void *dst, const void *src, size_t n)
 }
 #endif
 
-static bool seeded = false;
+/**************************
+ * Random number generation
+ **************************/
 
-static void
-seed_random(void)
+#ifndef _WIN32
+/*
+ * Generate @n cryptographically secure random bytes (thread-safe)
+ *
+ * This is the UNIX version.  It uses the Linux getrandom() system call if
+ * available; otherwise, it falls back to reading from /dev/urandom.
+ */
+void
+get_random_bytes(void *p, size_t n)
 {
-       srand(now_as_wim_timestamp());
-       seeded = true;
+       if (n == 0)
+               return;
+#ifdef __NR_getrandom
+       static bool getrandom_unavailable;
+
+       if (getrandom_unavailable)
+               goto try_dev_urandom;
+       do {
+               int res = syscall(__NR_getrandom, p, n, 0);
+               if (unlikely(res < 0)) {
+                       if (errno == ENOSYS) {
+                               getrandom_unavailable = true;
+                               goto try_dev_urandom;
+                       }
+                       if (errno == EINTR)
+                               continue;
+                       ERROR_WITH_ERRNO("getrandom() failed");
+                       wimlib_assert(0);
+                       res = 0;
+               }
+               p += res;
+               n -= res;
+       } while (n != 0);
+       return;
+
+try_dev_urandom:
+       ;
+#endif /* __NR_getrandom */
+       int fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0) {
+               ERROR_WITH_ERRNO("Unable to open /dev/urandom");
+               wimlib_assert(0);
+       }
+       do {
+               int res = read(fd, p, min(n, INT_MAX));
+               if (unlikely(res < 0)) {
+                       if (errno == EINTR)
+                               continue;
+                       ERROR_WITH_ERRNO("Error reading from /dev/urandom");
+                       wimlib_assert(0);
+                       res = 0;
+               }
+               p += res;
+               n -= res;
+       } while (n != 0);
+       close(fd);
 }
+#endif /* !_WIN32 */
 
-/* Fills @n characters pointed to by @p with random alphanumeric characters. */
+/*
+ * Generate @n cryptographically secure random alphanumeric characters
+ * (thread-safe)
+ *
+ * This is implemented on top of get_random_bytes().  For efficiency the calls
+ * to get_random_bytes() are batched.
+ */
 void
-randomize_char_array_with_alnum(tchar *p, size_t n)
+get_random_alnum_chars(tchar *p, size_t n)
 {
-       if (!seeded)
-               seed_random();
-       while (n--) {
-               int r = rand() % 62;
-               if (r < 26)
-                       *p++ = r + 'a';
-               else if (r < 52)
-                       *p++ = r - 26 + 'A';
+       u32 r[64];
+       int r_idx = 0;
+       int r_end = 0;
+
+       for (; n != 0; p++, n--) {
+               tchar x;
+
+               if (r_idx >= r_end) {
+                       r_idx = 0;
+                       r_end = min(n, ARRAY_LEN(r));
+                       get_random_bytes(r, r_end * sizeof(r[0]));
+               }
+
+               STATIC_ASSERT(sizeof(r[0]) == sizeof(u32));
+               while (unlikely(r[r_idx] >= UINT32_MAX - (UINT32_MAX % 62)))
+                       get_random_bytes(&r[r_idx], sizeof(r[0]));
+
+               x = r[r_idx++] % 62;
+
+               if (x < 26)
+                       *p = 'a' + x;
+               else if (x < 52)
+                       *p = 'A' + x - 26;
                else
-                       *p++ = r - 52 + '0';
+                       *p = '0' + x - 52;
        }
 }
 
-/* Fills @n bytes pointer to by @p with random numbers. */
-void
-randomize_byte_array(u8 *p, size_t n)
-{
-       if (!seeded)
-               seed_random();
-       while (n--)
-               *p++ = rand();
-}
+/************************
+ * System information
+ ************************/
 
-#ifndef __WIN32__
+#ifndef _WIN32
 unsigned
 get_available_cpus(void)
 {
@@ -223,9 +282,9 @@ get_available_cpus(void)
        }
        return n;
 }
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
 
-#ifndef __WIN32__
+#ifndef _WIN32
 u64
 get_available_memory(void)
 {
@@ -248,4 +307,4 @@ default_size:
        WARNING("Failed to determine available memory; assuming 1 GiB");
        return (u64)1 << 30;
 }
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */