Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34751702
run-threaded.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
14 KB
Subscribers
None
run-threaded.c
View Options
/* 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://gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
/* 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
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Jan 24, 10:59 PM (19 h, 36 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
53/c0/4d789d7d14f37cbcf9b221ae5ffe
Attached To
rM GPGME
Event Timeline
Log In to Comment