aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--src/gpg-error.def.in2
-rw-r--r--src/gpg-error.h.in1
-rw-r--r--src/gpg-error.vers1
-rw-r--r--src/gpgrt-int.h2
-rw-r--r--src/init.c49
-rw-r--r--src/visibility.c6
-rw-r--r--src/visibility.h2
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/t-malloc.c141
10 files changed, 206 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index 9e32030..2f633e4 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ Noteworthy changes in version 1.38 (unreleased) [C28/A28/R_]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_fnameconcat NEW.
gpgrt_absfnameconcat NEW.
+ gpgrt_reallocarray NEW.
gpgrt_set_confdir NEW.
gpgrt_argparser NEW.
ARGPARSE_FLAG_SYS NEW.
diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in
index bc4b0cc..bf8773b 100644
--- a/src/gpg-error.def.in
+++ b/src/gpg-error.def.in
@@ -235,4 +235,6 @@ EXPORTS
gpgrt_fnameconcat @178
gpgrt_absfnameconcat @179
+ gpgrt_reallocarray @180
+
;; end of file with public symbols for Windows.
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in
index 5643bdf..b0b1972 100644
--- a/src/gpg-error.h.in
+++ b/src/gpg-error.h.in
@@ -466,6 +466,7 @@ gpg_error_from_syserror (void)
*/
void *gpgrt_realloc (void *a, size_t n);
+void *gpgrt_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size);
void *gpgrt_malloc (size_t n);
void *gpgrt_calloc (size_t n, size_t m);
char *gpgrt_strdup (const char *string);
diff --git a/src/gpg-error.vers b/src/gpg-error.vers
index 347235f..ab3bbb0 100644
--- a/src/gpg-error.vers
+++ b/src/gpg-error.vers
@@ -158,6 +158,7 @@ GPG_ERROR_1.0 {
_gpgrt_log_assert;
gpgrt_realloc;
+ gpgrt_reallocarray;
gpgrt_malloc;
gpgrt_calloc;
gpgrt_strdup;
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index beb55ac..cc22004 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -114,6 +114,7 @@ void _gpgrt_abort (void) GPGRT_ATTR_NORETURN;
void _gpgrt_set_alloc_func (void *(*f)(void *a, size_t n));
void *_gpgrt_realloc (void *a, size_t n);
+void *_gpgrt_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size);
void *_gpgrt_malloc (size_t n);
void *_gpgrt_calloc (size_t n, size_t m);
char *_gpgrt_strdup (const char *string);
@@ -126,6 +127,7 @@ char *_gpgrt_strconcat_core (const char *s1, va_list arg_ptr);
#define xtrymalloc(a) _gpgrt_malloc ((a))
#define xtrycalloc(a,b) _gpgrt_calloc ((a),(b))
#define xtryrealloc(a,b) _gpgrt_realloc ((a),(b))
+#define xtryreallocarray(a,b,c,d) _gpgrt_reallocarray ((a),(b),(c),(d))
#define xtrystrdup(a) _gpgrt_strdup ((a))
void _gpgrt_pre_syscall (void);
diff --git a/src/init.c b/src/init.c
index 8ddf0c0..6239682 100644
--- a/src/init.c
+++ b/src/init.c
@@ -259,6 +259,55 @@ _gpgrt_realloc (void *a, size_t n)
}
+/* This is safe version of realloc useful for reallocing a calloced
+ * array. There are two ways to call it: The first example
+ * reallocates the array A to N elements each of SIZE but does not
+ * clear the newly allocated elements:
+ *
+ * p = gpgrt_reallocarray (a, n, n, nsize);
+ *
+ * Note that when NOLD is larger than N no cleaning is needed anyway.
+ * The second example reallocates an array of size NOLD to N elements
+ * each of SIZE but clear the newly allocated elements:
+ *
+ * p = gpgrt_reallocarray (a, nold, n, nsize);
+ *
+ * Note that gpgrt_reallocarray (NULL, 0, n, nsize) is equivalent to
+ * _gpgrt_calloc (n, nsize).
+ *
+ */
+void *
+_gpgrt_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size)
+{
+ size_t oldbytes, bytes;
+ char *p;
+
+ bytes = nmemb * size; /* size_t is unsigned so the behavior on overflow
+ * is defined. */
+ if (size && bytes / size != nmemb)
+ {
+ _gpg_err_set_errno (ENOMEM);
+ return NULL;
+ }
+
+ p = _gpgrt_realloc (a, bytes);
+ if (p && oldnmemb < nmemb)
+ {
+ /* OLDNMEMBS is lower than NMEMB thus the user asked for a
+ calloc. Clear all newly allocated members. */
+ oldbytes = oldnmemb * size;
+ if (size && oldbytes / size != oldnmemb)
+ {
+ xfree (p);
+ _gpg_err_set_errno (ENOMEM);
+ return NULL;
+ }
+ memset (p + oldbytes, 0, bytes - oldbytes);
+ }
+ return p;
+}
+
+
/* The malloc to be used for data returned by the public API. */
void *
_gpgrt_malloc (size_t n)
diff --git a/src/visibility.c b/src/visibility.c
index ea55d54..2e6aed7 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -764,6 +764,12 @@ gpgrt_realloc (void *a, size_t n)
}
void *
+gpgrt_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size)
+{
+ return _gpgrt_reallocarray (a, oldnmemb, nmemb, size);
+}
+
+void *
gpgrt_malloc (size_t n)
{
return _gpgrt_malloc (n);
diff --git a/src/visibility.h b/src/visibility.h
index 0759d2f..f7d16be 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -152,6 +152,7 @@ MARK_VISIBLE (gpgrt_get_syscall_clamp)
MARK_VISIBLE (gpgrt_set_alloc_func)
MARK_VISIBLE (gpgrt_realloc)
+MARK_VISIBLE (gpgrt_reallocarray)
MARK_VISIBLE (gpgrt_malloc)
MARK_VISIBLE (gpgrt_calloc)
MARK_VISIBLE (gpgrt_strdup)
@@ -333,6 +334,7 @@ MARK_VISIBLE (gpgrt_absfnameconcat);
#define gpgrt_vsnprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_realloc _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_reallocarray _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_malloc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_calloc _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_strdup _gpgrt_USE_UNDERSCORED_FUNCTION
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2d199da..be04df3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -30,7 +30,7 @@ EXTRA_DIST = t-argparse.conf etc/t-argparse.conf
gpg_error_lib = ../src/libgpg-error.la
TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64 \
- t-argparse t-logging t-stringutils
+ t-argparse t-logging t-stringutils t-malloc
AM_CPPFLAGS = -I$(top_builddir)/src $(extra_includes)
diff --git a/tests/t-malloc.c b/tests/t-malloc.c
new file mode 100644
index 0000000..be2ec81
--- /dev/null
+++ b/tests/t-malloc.c
@@ -0,0 +1,141 @@
+/* t-malloc.c - Check some malloc functions
+ * Copyright (C) 2020 g10 Code GmbH
+ *
+ * This file is part of Libgpg-error.
+ *
+ * Libgpg-error 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 Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgpg-error is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define PGM "t-malloc"
+#include "t-common.h"
+
+
+static const char *
+my_strusage (int level)
+{
+ const char *p;
+
+ switch (level)
+ {
+ case 9: p = "LGPL-2.1-or-later"; break;
+ case 11: p = PGM; break;
+ default: p = NULL;
+ }
+ return p;
+}
+
+
+static void
+check_reallocarray (void)
+{
+ struct foo_s { const char *a; int b; } *array;
+ size_t n;
+
+ array = gpgrt_calloc (10, sizeof *array);
+ if (!array)
+ die ("%s: malloc failed\n", __func__);
+
+ for (n=0; n < 10; n++)
+ if (array[n].a || array[n].b)
+ fail ("%s: array not cleared at index %zu\n", __func__, n);
+
+ for (n=0; n < 10; n++)
+ {
+ array[n].a = "dummy string";
+ array[n].b = 100+n;
+ }
+
+ array = gpgrt_reallocarray (array, 10, 20, sizeof *array);
+ if (!array)
+ die ("%s: realloc failed\n", __func__);
+
+ for (n=0; n < 10; n++)
+ {
+ if (!array[n].a || strcmp (array[n].a, "dummy string"))
+ fail ("%s: string in realloced array changed at index %zu\n",
+ __func__, n);
+
+ if (array[n].b != 100 + n)
+ fail ("%s: number in realloced array changed at index %zu\n",
+ __func__, n);
+ }
+ for (n=10; n < 20; n++)
+ if (array[n].a || array[n].b)
+ fail ("%s: realloced array not cleared at index %zu\n", __func__, n);
+
+ /* We can't easily check whether the reallocated array does not
+ * iniitialze in the case OLDN is equal or larger to N, so we skip
+ * this. Let's do a simple shrink test instead. */
+
+ array = gpgrt_reallocarray (array, 20, 7, sizeof *array);
+ if (!array)
+ die ("%s: realloc (shrinking) failed\n", __func__);
+
+ for (n=0; n < 7; n++)
+ {
+ if (!array[n].a || strcmp (array[n].a, "dummy string"))
+ fail ("%s: string in shrunk array changed at index %zu\n",
+ __func__, n);
+
+ if (array[n].b != 100 + n)
+ fail ("%s: number in shrunk array changed at index %zu\n",
+ __func__, n);
+ }
+
+ xfree (array);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ gpgrt_opt_t opts[] = {
+ ARGPARSE_x ('v', "verbose", NONE, 0, "Print more diagnostics"),
+ ARGPARSE_s_n('d', "debug", "Flyswatter"),
+ ARGPARSE_end()
+ };
+ gpgrt_argparse_t pargs = { &argc, &argv, 0 };
+
+ gpgrt_set_strusage (my_strusage);
+ gpgrt_log_set_prefix (gpgrt_strusage (11), GPGRT_LOG_WITH_PREFIX);
+
+ while (gpgrt_argparse (NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case 'v': verbose++; break;
+ case 'd': debug++; break;
+ default : pargs.err = ARGPARSE_PRINT_ERROR; break;
+ }
+ }
+ gpgrt_argparse (NULL, &pargs, NULL);
+
+ show ("testing malloc functions\n");
+
+ check_reallocarray ();
+
+ return !!errorcount;
+}