Merge branch 'upstream' into tizen
[platform/upstream/cryptsetup.git] / lib / utils_safe_memory.c
index 8c1fb5c..b161369 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * utils_safe_memory - safe memory helpers
  *
- * Copyright (C) 2009-2020 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2020 Milan Broz
+ * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2023 Milan Broz
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  */
 
 #include <stdlib.h>
+#include <stdbool.h>
 #include <string.h>
+#include <sys/mman.h>
 #include "libcryptsetup.h"
 
 struct safe_allocation {
-       size_t  size;
-       char    data[0];
+       size_t size;
+       bool locked;
+       char data[0] __attribute__((aligned(8)));
 };
+#define OVERHEAD offsetof(struct safe_allocation, data)
 
 /*
  * Replacement for memset(s, 0, n) on stack that can be optimized out
@@ -34,6 +38,9 @@ struct safe_allocation {
  */
 void crypt_safe_memzero(void *data, size_t size)
 {
+       if (!data)
+               return;
+
 #ifdef HAVE_EXPLICIT_BZERO
        explicit_bzero(data, size);
 #else
@@ -49,15 +56,19 @@ void *crypt_safe_alloc(size_t size)
 {
        struct safe_allocation *alloc;
 
-       if (!size || size > (SIZE_MAX - offsetof(struct safe_allocation, data)))
+       if (!size || size > (SIZE_MAX - OVERHEAD))
                return NULL;
 
-       alloc = malloc(size + offsetof(struct safe_allocation, data));
+       alloc = malloc(size + OVERHEAD);
        if (!alloc)
                return NULL;
 
+       crypt_safe_memzero(alloc, size + OVERHEAD);
        alloc->size = size;
-       crypt_safe_memzero(&alloc->data, size);
+
+       /* Ignore failure if it is over limit. */
+       if (!mlock(alloc, size + OVERHEAD))
+               alloc->locked = true;
 
        /* coverity[leaked_storage] */
        return &alloc->data;
@@ -66,16 +77,24 @@ void *crypt_safe_alloc(size_t size)
 void crypt_safe_free(void *data)
 {
        struct safe_allocation *alloc;
+       volatile size_t *s;
+       void *p;
 
        if (!data)
                return;
 
-       alloc = (struct safe_allocation *)
-               ((char *)data - offsetof(struct safe_allocation, data));
+       p = (char *)data - OVERHEAD;
+       alloc = (struct safe_allocation *)p;
 
        crypt_safe_memzero(data, alloc->size);
 
-       alloc->size = 0x55aa55aa;
+       if (alloc->locked) {
+               munlock(alloc, alloc->size + OVERHEAD);
+               alloc->locked = false;
+       }
+
+       s = (volatile size_t *)&alloc->size;
+       *s = 0x55aa55aa;
        free(alloc);
 }
 
@@ -83,13 +102,14 @@ void *crypt_safe_realloc(void *data, size_t size)
 {
        struct safe_allocation *alloc;
        void *new_data;
+       void *p;
 
        new_data = crypt_safe_alloc(size);
 
        if (new_data && data) {
 
-               alloc = (struct safe_allocation *)
-                       ((char *)data - offsetof(struct safe_allocation, data));
+               p = (char *)data - OVERHEAD;
+               alloc = (struct safe_allocation *)p;
 
                if (size > alloc->size)
                        size = alloc->size;