Skip Menu |

This queue is for tickets about the Crypt-GCrypt CPAN distribution.

Report information
The Basics
Id: 55127
Status: resolved
Priority: 0/
Queue: Crypt-GCrypt

People
Owner: aar [...] cpan.org
Requestors: crew@cs.stanford.edu (no email address)
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 1.24
Fixed in: 1.25



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
Download test.out
application/octet-stream 3.4k

Message body not shown because it is not plain text.

Il Mar 02 Mar 2010 04:09:58, crew@cs.stanford.edu ha scritto: Show quoted text
> 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.
Greetings, thank you for the bug report. CLONE_SKIP is definitely the intended design of this module. I just forgot to include it since I was focusing on other, more critical, cases (multiple instances running each one in its own thread). The module will be updated in the next release.