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
1 changed files with 29 additions and 10 deletions

View File

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