[git] GPGME - branch, master, updated. gpgme-1.12.0-69-gd0402f8

by Andre Heinecke cvs at cvs.gnupg.org
Thu Nov 15 12:00:29 CET 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG Made Easy".

The branch, master has been updated
       via  d0402f886b720d03369887c807581bd980ee70cf (commit)
      from  004e2cad2f26250cd010684f11dc187f08e6f9e1 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit d0402f886b720d03369887c807581bd980ee70cf
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Thu Nov 15 11:57:27 2018 +0100

    tests: Add run-threaded for multithread tests
    
    * tests/Makefile.am (run-threaded): Add.
    * tests/run-threaded.c: New.
    
    --
    This test is intended to help detect race conditions
    or other multithread problems. It can also be used
    to put the whole GnuPG system under extreme load.

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 29e0c42..1b990e6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -34,8 +34,9 @@ noinst_HEADERS = run-support.h
 
 noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign \
 		  run-verify run-encrypt run-identify run-decrypt run-genkey \
-		  run-keysign run-tofu run-swdb
+		  run-keysign run-tofu run-swdb run-threaded
 
+run_threaded_LDADD = ../src/libgpgme.la -lpthread @GPG_ERROR_LIBS@
 
 if RUN_GPG_TESTS
 gpgtests = gpg json
diff --git a/tests/run-threaded.c b/tests/run-threaded.c
new file mode 100644
index 0000000..14a9cbe
--- /dev/null
+++ b/tests/run-threaded.c
@@ -0,0 +1,678 @@
+/* run-threaded.c  - Helper to put GPGME under multithread load.
+   Copyright (C) 2018 by Bundesamt für Sicherheit in der Informationstechnik
+                 Software engineering by Intevation GmbH
+
+   This file is part of GPGME.
+
+   GPGME 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.
+
+   GPGME 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/>.
+*/
+
+/* The idea of this test is not to be run as unit test but as part
+ * of development to find threading issues and resource leaks. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <gpgme.h>
+#include <gpg-error.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#define PGM "run-threaded"
+
+#include "run-support.h"
+
+static volatile int stop;
+static volatile int thread_cnt;
+static volatile int running_threads;
+static int verbose;
+static int data_type;
+static int mem_only;
+
+#ifdef HAVE_W32_SYSTEM
+# include <windows.h>
+# define THREAD_RET DWORD CALLBACK
+
+static void
+create_thread (THREAD_RET (*func) (void *), void *arg)
+{
+  running_threads++;
+  if (CloseHandle (CreateThread (NULL, 0, func, arg, 0, NULL)))
+    {
+      fprintf (stderr, "Failed to create thread!\n");
+      exit (1);
+    }
+}
+
+#else
+# include <pthread.h>
+# define THREAD_RET void *
+
+static void
+create_thread (THREAD_RET (func) (void *), void *arg)
+{
+  pthread_t handle;
+  running_threads++;
+  if (pthread_create (&handle, NULL, func, arg))
+    {
+      fprintf (stderr, "Failed to create thread!\n");
+      exit (1);
+    }
+}
+#endif
+
+#include <gpg-error.h>
+
+GPGRT_LOCK_DEFINE (out_lock);
+
+#define out(format, ...) \
+  { \
+    gpgrt_lock_lock (&out_lock); \
+    printf (format "\n", ##__VA_ARGS__); \
+    gpgrt_lock_unlock (&out_lock); \
+  }
+
+#define errpoint \
+{ \
+  out ("Error on %i", __LINE__); \
+  exit (1); \
+}
+
+#define log(format, ...) \
+if (verbose) \
+  { \
+    gpgrt_lock_lock (&out_lock); \
+    printf (format "\n", ##__VA_ARGS__); \
+    gpgrt_lock_unlock (&out_lock); \
+  }
+
+/* Lazy mans signal */
+GPGRT_LOCK_DEFINE (threads_lock);
+
+void del_thread (void)
+{
+  if (--running_threads == 0)
+    {
+      gpgrt_lock_unlock (&threads_lock);
+    }
+}
+
+static int
+show_usage (int ex)
+{
+  fputs ("usage: " PGM " [options] [messages]\n\n"
+         "Options:\n"
+         "  --verbose     run in verbose mode\n"
+         "  --no-list     do not do keylistings\n"
+         "  --data-type   mem function to use one of:\n"
+         "                    1: fstream\n"
+         "                    2: posix fd\n"
+         "                    3: memory\n"
+         "                    4: gpgrt_stream\n"
+         "                    default: random\n"
+/*         "  --mem-cache   read data only once and then work on memory\n"
+           "                exlusive with data-type option\n" */
+         "  --threads N   use 4+N threads (4 are used for keylisting"
+" default 1)\n"
+         "  --repeat  N   do N repeats on the messages (default 1)\n\n"
+         "Note: The test does keylistings of both S/MIME and OpenPGP\n"
+         "      in the background while running operations on the\n"
+         "      messages, depending on their type.\n"
+         "      (Currently decrypt / verify).\n\n"
+         "      Without messages only keylistings will be done.\n"
+         , stderr);
+  exit (ex);
+}
+
+
+struct msg_list_s
+{
+  const char *file_name;
+  struct msg_list_s *next;
+};
+typedef struct msg_list_s *msg_list_t;
+
+struct data_s
+{
+  int fd;
+  FILE *file;
+  gpgrt_stream_t stream;
+  unsigned char *mem;
+  gpgme_data_t dh;
+};
+typedef struct data_s *data_t;
+
+struct keylist_args_s
+{
+  gpgme_protocol_t proto;
+  int secret;
+};
+typedef struct keylist_args_s *keylist_args_t;
+
+static volatile int keylists;
+
+static THREAD_RET
+do_keylist (void *keylist_args)
+{
+  gpgme_error_t err;
+  gpgme_ctx_t ctx;
+  gpgme_key_t key;
+
+  keylist_args_t args = (keylist_args_t) keylist_args;
+
+  log ("Keylist %i, Protocol: %s, Secret %i",
+       keylists++,
+       args->proto == GPGME_PROTOCOL_CMS ? "CMS" : "OpenPGP",
+       args->secret);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+
+  err = gpgme_set_protocol (ctx, args->proto);
+  fail_if_err (err);
+
+  err = gpgme_op_keylist_start (ctx, NULL, args->secret);
+  fail_if_err (err);
+
+  while (!(err = gpgme_op_keylist_next (ctx, &key)))
+    {
+      gpgme_key_unref (key);
+    }
+
+  if (gpgme_err_code (err) != GPG_ERR_EOF)
+    {
+      fail_if_err (err);
+    }
+
+  gpgme_release (ctx);
+
+  if (!stop)
+    {
+      create_thread (do_keylist, keylist_args);
+    }
+  del_thread ();
+  return 0;
+}
+
+
+static unsigned char *
+get_file (const char *fname, size_t *r_size)
+{
+  gpg_error_t err;
+  gpgrt_stream_t fp;
+  struct stat st;
+  unsigned char *buf;
+  size_t buflen;
+
+  fp = gpgrt_fopen (fname, "r");
+  if (!fp)
+    {
+      err = gpg_error_from_syserror ();
+      fprintf (stderr, "Error: can't open '%s': %s\n", fname,
+               gpg_strerror (err));
+      return NULL;
+    }
+
+  if (fstat (gpgrt_fileno(fp), &st))
+    {
+      err = gpg_error_from_syserror ();
+      fprintf (stderr, "Error: can't stat '%s': %s\n", fname,
+               gpg_strerror (err));
+      gpgrt_fclose (fp);
+      return NULL;
+    }
+
+  buflen = st.st_size;
+  buf = malloc (buflen+1);
+  if (!buf)
+    {
+      fprintf (stderr, "Error: no mem\n");
+      gpgrt_fclose (fp);
+      return NULL;
+    }
+
+  if (gpgrt_fread (buf, buflen, 1, fp) != 1)
+    {
+      err = gpg_error_from_syserror ();
+      fprintf (stderr, "error reading '%s': %s\n", fname,
+               gpg_strerror (err));
+      gpgrt_fclose (fp);
+      free (buf);
+      return NULL;
+    }
+  buf[buflen] = 0;
+  gpgrt_fclose (fp);
+
+  if (r_size)
+    {
+      *r_size = buflen;
+    }
+
+  return buf;
+}
+
+/** Lets use random data. This should also introduce a bit
+    of randomness into the system by changing the runtimes
+    of various data functions. Esp. Mem against the file ops. */
+data_t
+random_data_new (const char *fname)
+{
+  data_t ret = calloc (1, sizeof (struct data_s));
+  int data_rand;
+  if (data_type)
+    {
+      data_rand = data_type;
+    }
+  else
+    {
+      data_rand = rand () % 3;
+    }
+
+  if (data_rand == 0) /* stream */
+    {
+      FILE *f_stream = fopen (fname, "rb");
+      if (!f_stream)
+        {
+          errpoint;
+        }
+      fail_if_err (gpgme_data_new_from_stream (&(ret->dh), f_stream));
+      ret->file = f_stream;
+      return ret;
+    }
+  if (data_rand == 1) /* fd */
+    {
+      int fd = open (fname, O_RDONLY);
+      gpgme_data_t dh;
+      if (fd == -1)
+        {
+          errpoint;
+        }
+      fail_if_err (gpgme_data_new_from_fd (&dh, fd));
+      ret->fd = fd;
+      ret->dh = dh;
+      return ret;
+    }
+  if (data_rand == 2) /* mem */
+    {
+      unsigned char *mem;
+      size_t size;
+
+      mem = get_file (fname, &size);
+      if (!mem)
+        {
+          errpoint;
+        }
+      fail_if_err (gpgme_data_new_from_mem (&(ret->dh),
+                                            (const char *)mem,
+                                            size, 0));
+      ret->mem = mem;
+      return ret;
+    }
+  if (data_rand == 3) /* estream */
+    {
+      gpgrt_stream_t stream = gpgrt_fopen (fname, "rb");
+
+      if (!stream)
+        {
+          errpoint;
+        }
+
+      fail_if_err (gpgme_data_new_from_estream (&(ret->dh), stream));
+      ret->stream = stream;
+      return ret;
+    }
+  /* notreached */
+  return ret;
+}
+
+void
+random_data_close (data_t data)
+{
+  if (data->dh)
+    {
+      gpgme_data_release (data->dh);
+    }
+  if (data->fd)
+    {
+      close (data->fd);
+    }
+  else if (data->file)
+    {
+      fclose (data->file);
+    }
+  else if (data->stream)
+    {
+      gpgrt_fclose (data->stream);
+    }
+  else if (data->mem)
+    {
+      free (data->mem);
+    }
+  free (data);
+}
+
+void
+verify (const char *fname, gpgme_protocol_t proto)
+{
+  gpgme_ctx_t ctx;
+  gpgme_error_t err;
+  gpgme_data_t out;
+  char *msg;
+  size_t msg_len;
+  data_t data = random_data_new (fname);
+
+  log ("Starting verify on: %s with protocol %s", fname,
+       proto == GPGME_PROTOCOL_CMS ? "CMS" : "OpenPGP");
+
+  gpgme_data_new (&out);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+
+  err = gpgme_set_protocol (ctx, proto);
+  fail_if_err (err);
+
+  err = gpgme_op_verify (ctx, data->dh, NULL, out);
+  out ("Data: %p, %i %p %p %p", data->dh,
+       data->fd, data->file, data->stream,
+       data->mem);
+  fail_if_err (err);
+
+  msg = gpgme_data_release_and_get_mem (out, &msg_len);
+
+  if (msg_len)
+    {
+      log ("Verify result \n'%.*s'", (int)msg_len, msg);
+    }
+
+  gpgme_release (ctx);
+
+  random_data_close (data);
+}
+
+
+/* We randomize data access to put in a bit additional
+   entropy in this test and also to check if maybe
+   some data functions might not be properly thread
+   safe. */
+void
+decrypt (const char *fname, gpgme_protocol_t proto)
+{
+  gpgme_ctx_t ctx;
+  gpgme_error_t err;
+  gpgme_data_t out;
+  char *msg;
+  size_t msg_len;
+  data_t data = random_data_new (fname);
+
+  log ("Starting decrypt on: %s", fname);
+
+  gpgme_data_new (&out);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+
+  err = gpgme_set_protocol (ctx, proto);
+  fail_if_err (err);
+
+  err = gpgme_op_decrypt (ctx, data->dh, out);
+  fail_if_err (err);
+
+  gpgme_release (ctx);
+
+  msg = gpgme_data_release_and_get_mem (out, &msg_len);
+
+  if (msg_len)
+    {
+      log ("Decrypt result \n'%.*s'", (int)msg_len, msg);
+    }
+
+  random_data_close (data);
+}
+
+
+static THREAD_RET
+do_data_op (void *file_name)
+{
+  gpgme_data_t data;
+  const char *fname = (const char *) file_name;
+  FILE *f = fopen (fname, "rb");
+  gpgme_data_type_t type;
+
+  if (!f)
+    {
+      fprintf (stderr, "Failed to open '%s'\n", fname);
+      exit (1);
+    }
+
+  fail_if_err (gpgme_data_new_from_stream (&data, f));
+
+  type = gpgme_data_identify (data, 0);
+  gpgme_data_release (data);
+  fclose (f);
+
+  switch (type)
+    {
+      case GPGME_DATA_TYPE_INVALID:
+      case GPGME_DATA_TYPE_UNKNOWN:
+        {
+          fprintf (stderr, "Failed to identify '%s'", fname);
+          exit(1);
+        }
+      case GPGME_DATA_TYPE_PGP_SIGNED:
+        {
+          verify (fname, GPGME_PROTOCOL_OpenPGP);
+          break;
+        }
+      case GPGME_DATA_TYPE_CMS_SIGNED:
+        {
+          verify (fname, GPGME_PROTOCOL_CMS);
+          break;
+        }
+      case GPGME_DATA_TYPE_PGP_ENCRYPTED:
+        {
+          decrypt (fname, GPGME_PROTOCOL_OpenPGP);
+          break;
+        }
+      case GPGME_DATA_TYPE_CMS_ENCRYPTED:
+        {
+          decrypt (fname, GPGME_PROTOCOL_CMS);
+          break;
+        }
+      default:
+        {
+          fprintf (stderr, "Unhandled data type 0x%x for '%s'", type, fname);
+          exit(1);
+        }
+    }
+
+  del_thread ();
+  return 0;
+}
+
+
+void
+start_keylistings (void)
+{
+  static struct keylist_args_s args[4];
+
+  args[0].proto = GPGME_PROTOCOL_OpenPGP;
+  args[0].secret = 0;
+
+  args[1].proto = GPGME_PROTOCOL_OpenPGP;
+  args[1].secret = 1;
+
+  args[2].proto = GPGME_PROTOCOL_CMS;
+  args[2].secret = 0;
+
+  args[3].proto = GPGME_PROTOCOL_CMS;
+  args[3].secret = 1;
+
+  for (int i = 0; i < 4; i++)
+    {
+      thread_cnt--;
+      create_thread (do_keylist, &args[i]);
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+  int repeats = 1;
+  int threads = 0;
+  int no_list = 0;
+  msg_list_t msgs = NULL;
+  msg_list_t msg_it = NULL;
+  stop = 0;
+
+  srand (1 /* Somewhat deterministic results */);
+
+  if (argc)
+    { argc--; argv++; }
+
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--help"))
+        {
+          show_usage (0);
+        }
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--no-list"))
+        {
+          no_list = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--mem-only"))
+        {
+          if (data_type)
+            {
+              show_usage (1);
+            }
+          mem_only = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--threads"))
+        {
+          argc--; argv++;
+          if (!argc)
+            {
+              show_usage (1);
+            }
+          threads = atoi (*argv);
+          if (!threads)
+            {
+              show_usage (1);
+            }
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--data-type"))
+        {
+          argc--; argv++;
+          if (!argc || mem_only)
+            {
+              show_usage (1);
+            }
+          data_type = atoi (*argv);
+          if (data_type > 4)
+            {
+              show_usage (1);
+            }
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--repeat"))
+        {
+          argc--; argv++;
+          if (!argc)
+            {
+              show_usage (1);
+            }
+          repeats = atoi (*argv);
+          if (!repeats)
+            {
+              show_usage (1);
+            }
+          argc--; argv++;
+        }
+    }
+
+  init_gpgme_basic ();
+
+  if (threads < argc)
+    {
+      /* Make sure we run once on each arg */
+      threads += argc;
+    }
+  while (argc)
+    {
+      if (!msgs)
+        {
+          msgs = calloc (1, sizeof *msgs);
+          msg_it = msgs;
+        }
+      else
+        {
+          msg_it->next = calloc (1, sizeof *msgs);
+          msg_it = msg_it->next;
+        }
+      msg_it->file_name = *argv;
+      argc--; argv++;
+    }
+
+  gpgrt_lock_lock (&threads_lock);
+  do
+    {
+      stop = 0;
+      thread_cnt = threads + 4;
+      out ("Repeats left: %i", repeats);
+
+      if (!no_list)
+        {
+          start_keylistings ();
+        }
+      else
+        {
+          thread_cnt -= 4;
+        }
+
+      while (thread_cnt)
+        {
+          log ("Thread %i", thread_cnt);
+          for (msg_it = msgs; msg_it && thread_cnt; msg_it = msg_it->next)
+            {
+              thread_cnt--;
+              create_thread (do_data_op, (void *)msg_it->file_name);
+            }
+        }
+
+      stop = 1;
+      gpgrt_lock_lock (&threads_lock);
+    }
+  while (--repeats != 0);
+
+  return 0;
+}

-----------------------------------------------------------------------

Summary of changes:
 tests/Makefile.am    |   3 +-
 tests/run-threaded.c | 678 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 680 insertions(+), 1 deletion(-)
 create mode 100644 tests/run-threaded.c


hooks/post-receive
-- 
GnuPG Made Easy
http://git.gnupg.org




More information about the Gnupg-commits mailing list