Subject: | not actually thread-safe |
# here's a short test program that should crash on pretty much every
# platform where ithreads are enabled (but details of my OS/Perl
# are down at the bottom)
use threads;
use Crypt::GCrypt;
# uncommenting this line is *one* way to fix things:
# { package Crypt::GCrypt; sub CLONE_SKIP { return 1; }}
# create some Crypt::GCrypt object with nontrivial state
$cph = Crypt::GCrypt->new(qw(type cipher algorithm twofish mode ecb
padding none));
$cph->start('encrypting');
$cph->setkey('1'x32);
# start some threads
$n = 3;
@ths = ();
for ($i = 0; $i < $n; ++$i) {
push @ths, threads->create(sub {
# does not matter
return 1;
},());
};
# collect the threads
@results = ();
push @results, $_->join foreach(@ths);
# clean up
$cph->finish;
print join(',',@results),"\n";
__END__
------------- Output of test.pl --------------------------
WARNING: the ->finish() method was not called after
encryption/decryption. during global destruction.
WARNING: the ->finish() method was not called after
encryption/decryption. during global destruction.
*** glibc detected *** perl: corrupted double-linked list: 0x0993ae88 ***
Show quoted text
------------- discussion --------------------------
Since there is no sub CLONE {} defined for Crypt::GCrypt, a reference
to the same Crypt_GCrypt_s struct is copied into every new thread that
gets started after the object is created, ALL threads call DESTROY on
the object, and so we get a big memory smash.
Having done a cursory look through the libgcrypt sources, it appears to
me that the ONLY places where mutexes are used at all is in the
initialization of the library and in reading/writing of secure memory.
So libgcrypt itself is ONLY thread-safe in the sense that it can
initialize itself properly in a multithreaded environment, and is thus
at least safe to load and initialize -- which is no small thing -- but...
... outside the case of secure memory, there appears to be NO ATTEMPT
WHATSOEVER to protect the various handles the library gives out (i.e.,
ciphers, public keys, digests, etc...) from being accessed by
concurrently by different threads -- the problem being that these
handles are evidently to objects with mutable state, so the library user
is free to hang him/herself in arbitrary ways... which may be
intentional since adding those mutex calls could slow things down a bit.
I see three ways of making this perl module thread-safe
(1) maintain reference counts on all library handles that
involve mutable state. Put mutex around all of the
calls that access the handles. Only close handles when the
reference counts drop to zero. Create a weak hash to hold all
Crypt::GCrypt objects that ever get created (as described in
perlmod); sub CLONE then has to walk all objects bumping reference
counts on all of the non-null handles.
(2) have CLONE sub { .. } create new instances of the handles looks
like h_md can be cloned with gcry_md_copy but for the other
handles you will need to have something that remembers the
construction recipe, e.g., S-Exps containing the parameters
for gcry_cipher_setkey and so on. (security folks will probably
hate this option...)
(3) Define CLONE_SKIP as above. When a new thread starts up all
existing Crypt::GCrypt objects turn into undefs for that thread.
This guarantees thread safety since no Crypt::GCrypt object will
then be crossing thread boundaries without lots of work on the
user's part. (security folks will probably LIKE this option)
Not sure which is the Right Thing.
------------- Configuration --------------------------
OS: Debian Linux 5.0 (lenny) kernel 2.6.26-2-686 #1 SMP
Perl -V says:
Summary of my perl5 (revision 5 version 10 subversion 0) configuration:
Platform:
osname=linux, osvers=2.6.26-2-amd64,
archname=i486-linux-gnu-thread-multi
uname='linux puccini 2.6.26-2-amd64 #1 smp fri aug 14 07:12:04 utc
2009 i686 gnulinux '
config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN
-Dcccdlflags=-fPIC -Darchname=i486-linux-gnu -Dprefix=/usr
-Dprivlib=/usr/share/perl/5.10 -Darchlib=/usr/lib/perl/5.10
-Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5
-Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local
-Dsitelib=/usr/local/share/perl/5.10.0
-Dsitearch=/usr/local/lib/perl/5.10.0 -Dman1dir=/usr/share/man/man1
-Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1
-Dsiteman3dir=/usr/local/man/man3 -Dman1ext=1 -Dman3ext=3perl
-Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Ud_ualarm -Uusesfio
-Uusenm -DDEBUGGING=-g -Doptimize=-O2 -Duseshrplib
-Dlibperl=libperl.so.5.10.0 -Dd_dosuid -des'
hint=recommended, useposix=true, d_sigaction=define
useithreads=define, usemultiplicity=define
useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
use64bitint=undef, use64bitall=undef, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DDEBIAN
-fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64',
optimize='-O2 -g',
cppflags='-D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fno-strict-aliasing
-pipe -I/usr/local/include'
ccversion='', gccversion='4.3.2', gccosandvers=''
intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t',
lseeksize=8
alignbytes=4, prototype=define
Linker and Libraries:
ld='cc', ldflags =' -L/usr/local/lib'
libpth=/usr/local/lib /lib /usr/lib /usr/lib64
libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt
perllibs=-ldl -lm -lpthread -lc -lcrypt
libc=/lib/libc-2.7.so, so=so, useshrplib=true, libperl=libperl.so.5.10.0
gnulibc_version='2.7'
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
cccdlflags='-fPIC', lddlflags='-shared -O2 -g -L/usr/local/lib'
Characteristics of this binary (from libperl):
Compile-time options: MULTIPLICITY PERL_DONT_CREATE_GVSV
PERL_IMPLICIT_CONTEXT PERL_MALLOC_WRAP USE_ITHREADS
USE_LARGE_FILES USE_PERLIO USE_REENTRANT_API
Built under linux
Compiled at Aug 28 2009 22:15:29
@INC:
/etc/perl
/usr/local/lib/perl/5.10.0
/usr/local/share/perl/5.10.0
/usr/lib/perl5
/usr/share/perl5
/usr/lib/perl/5.10
/usr/share/perl/5.10
/usr/local/lib/site_perl
.
Subject: | test.out |
Message body not shown because it is not plain text.