1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-23 15:07:03 +01:00

gpg: Fix trustdb updates without lock held.

* g10/tdbio.c (is_locked): Turn into a counter.
(take_write_lock, release_write_lock): Implement recursive locks.
--

On trustdb creation we have this call sequence:

  init_trustdb                 -> takes lock
    tdbio_set_dbname
      create_version_record
       tdbio_write_record
         put_record_into_cache -> takes lock
         put_record_into_cache -> releases lock
  init_trustdb                 -> releases lock

The second take lock does noting but the first release lock has
already released the lock and the second release lock is a thus a NOP.
This is likely the cause for the corrupted trustdb as reported in

GnuPG-bug-id: 3839
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2018-03-26 16:57:04 +02:00
parent 5f00531463
commit 456a3a8e93
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B

View File

@ -105,10 +105,11 @@ struct cmp_xdir_struct
/* The name of the trustdb file. */ /* The name of the trustdb file. */
static char *db_name; static char *db_name;
/* The handle for locking the trustdb file and a flag to record /* The handle for locking the trustdb file and a counter to record how
whether a lock has been taken. */ * often this lock has been taken. That counter is required becuase
* dotlock does not implemen recursive locks. */
static dotlock_t lockhandle; static dotlock_t lockhandle;
static int is_locked; static unsigned int is_locked;
/* The file descriptor of the trustdb. */ /* The file descriptor of the trustdb. */
static int db_fd = -1; static int db_fd = -1;
@ -135,6 +136,8 @@ static void create_hashtable (ctrl_t ctrl, TRUSTREC *vr, int type);
static int static int
take_write_lock (void) take_write_lock (void)
{ {
int rc;
if (!lockhandle) if (!lockhandle)
lockhandle = dotlock_create (db_name, 0); lockhandle = dotlock_create (db_name, 0);
if (!lockhandle) if (!lockhandle)
@ -144,12 +147,16 @@ take_write_lock (void)
{ {
if (dotlock_take (lockhandle, -1) ) if (dotlock_take (lockhandle, -1) )
log_fatal ( _("can't lock '%s'\n"), db_name ); log_fatal ( _("can't lock '%s'\n"), db_name );
else rc = 0;
is_locked = 1;
return 0;
} }
else else
return 1; rc = 1;
if (opt.lock_once)
is_locked = 1;
else
is_locked++;
return rc;
} }
@ -160,10 +167,22 @@ take_write_lock (void)
static void static void
release_write_lock (void) release_write_lock (void)
{ {
if (!opt.lock_once) if (opt.lock_once)
if (!dotlock_release (lockhandle)) return; /* Don't care; here IS_LOCKED is fixed to 1. */
is_locked = 0;
if (!is_locked)
{
log_error ("Ooops, tdbio:release_write_lock with no lock held\n");
return;
}
if (--is_locked)
return;
if (dotlock_release (lockhandle))
log_error ("Oops, tdbio:release_write_locked failed\n");
} }
/************************************* /*************************************
************* record cache ********** ************* record cache **********