Page MenuHome GnuPG

keybox/keydb locking issue in 2.6
Testing, HighPublic

Description

We observe a file is still open when unlock operation is done.
This may cause troubles.

IIUC, I identified the bug.

Related Objects

Event Timeline

gniibe triaged this task as High priority.EditedThu, Oct 9, 10:11 AM
gniibe created this task.
gniibe created this object with edit policy "Contributor (Project)".

Here are places where I found problems.

diff --git a/g10/keydb.c b/g10/keydb.c
index 590b45713..3dbfbbc82 100644
--- a/g10/keydb.c
+++ b/g10/keydb.c
@@ -766,10 +766,11 @@ keydb_add_resource (const char *url, unsigned int flags)
                         if (!keybox_lock (kbxhd, 1, 0))
                           {
                             keybox_compress (kbxhd);
+                            keybox_release (kbxhd);
                             keybox_lock (kbxhd, 0, 0);
                           }
-
-                        keybox_release (kbxhd);
+                        else
+                          keybox_release (kbxhd);
                       }
                   }
                 used_resources++;
@@ -907,7 +908,6 @@ internal_keydb_deinit (KEYDB_HANDLE hd)
   active_handles--;
 
   hd->keep_lock = 0;
-  unlock_all (hd);
   for (i=0; i < hd->used; i++)
     {
       switch (hd->active[i].type)
@@ -922,7 +922,7 @@ internal_keydb_deinit (KEYDB_HANDLE hd)
           break;
         }
     }
-
+  unlock_all (hd);
   keyblock_cache_clear (hd);
 }
 
diff --git a/sm/keydb.c b/sm/keydb.c
index 1f93268a8..0aa5c27e8 100644
--- a/sm/keydb.c
+++ b/sm/keydb.c
@@ -460,10 +460,11 @@ keydb_add_resource (ctrl_t ctrl, const char *url, int force, int *auto_created)
                 if (!keybox_lock (kbxhd, 1, 0))
                   {
                     keybox_compress (kbxhd);
+                    keybox_release (kbxhd);
                     keybox_lock (kbxhd, 0, 0);
                   }
-
-                keybox_release (kbxhd);
+                else
+                  keybox_release (kbxhd);
               }
 
             used_resources++;
@@ -695,7 +696,6 @@ keydb_release (KEYDB_HANDLE hd)
   else
     {
       hd->keep_lock = 0;
-      unlock_all (hd);
       for (i=0; i < hd->used; i++)
         {
           switch (hd->active[i].type)
@@ -707,6 +707,7 @@ keydb_release (KEYDB_HANDLE hd)
               break;
             }
         }
+      unlock_all (hd);
     }
 
   xfree (hd);

The patch itself is incorrect, doesn't work well because it releases the structure.
My point is that keybox_fp_release or something like that is needed to close FP.

werner added a subscriber: werner.

Shall we merge this with T2196 ? BTW, I have some unpushed commit and a test installer.

Except for the release/unlock thing after keybox_compress I already have the other fixes in my 2.2 commits. I noticed that the gpgsm keydb lock/release stuff differes from the one for gpg: For gpg we use the keybox_lock function but that is bot used at all by gpgsm. In theory this should be unified but I fear a regression risk and thus for 2.2 we better don't touch it.

Let us keep this ticket for things in gnupg26 and later with it additional support of keyboxd. And use the old ticket for 2.2 which has anyway a different code layout in both keydb.c modules.

werner renamed this task from keybox/keydb locking issue to keybox/keydb locking issue in 2.6 .Thu, Oct 9, 5:54 PM

I understand that this is for 2.6.

I had misunderstood that the way of gpg and the way of gpgsm were same for the locking/unlocking. No, it's different.

For gpg, it locks (by dotlock) pubring.kbx.lock both for read access and write access.
For gpgsm, it only locks only for write access.

gniibe mentioned this in Unknown Object (Maniphest Task).Mon, Oct 13, 7:51 AM
werner mentioned this in Unknown Object (Maniphest Task).Mon, Oct 13, 3:33 PM

I pushed further changes into gniibe/t7855 branch.
rG2fe62809014e: gpg,gpgsm: Serialize write access to keybox/keyring to protect.

The way of keydb access is serialized for write access, so that write access always results consistent data (never corrupts data).
(Note that there is no guarantee that for read access with consistent data; when it's under write access, it may see inconsistent data.)

Before the changes of this commit, the places for write access is somehow "locked", but not correctly thoroughly;
The access pattern should be: new+open, lock, search (locating the offset), write, and close+unlock+release

If the search (locating the offset) is not locked, it may result corrupted data, because it may call keybox_compress between search and write, for example.

Lastly, pushed a change into gniibe/t7855 branch.
rGf861b2a33f96: gpg,gpgsm: Fix thinko for FP closing under no lock.

The change:
rG2fe62809014e: gpg,gpgsm: Serialize write access to keybox/keyring to protect.
This introduces behavior change; When multiple gpg --key-edit are invoked, only one get a lock, others have to wait the lock.
But this means that: before the changes, it might result corrupted pubring.kbx/pubring.gpg.

gniibe mentioned this in Unknown Object (Maniphest Task).Mon, Oct 20, 3:44 AM
gniibe changed the task status from Open to Testing.Wed, Oct 22, 4:38 AM

All changes in gniibe/t7855 are pushed into master.

Still, there is a fundamental problem with keydb locking.

  • It only assures no-data-corruption.
  • When a process doing write access, another process reading the resource may encounter a problem (inconsistent data read), since data could be changed while accessing.
    • Currently, write access may occur with keybox compress, this means that users are not safe to invoke multiple gpg/gpgsm simultaneously (to be sure).
      • It would be: only keybox compress when users explicitly ask.
    • We could introduce a lock to read access... BUT naively adding a lock (both for read and write or read-multiple-write-one) results possible deadlock in gpgsm
      • in gpgsm, gpgsm_walk_cert_chain and gpgsm_validate_chain access the resource of keydb in a way of:
        • While it has a handle kh, by find_up routine, it may call keydb_store_cert by callback routine; The callback does write access to the resource opening another handle.
        • Currently, it works because of no lock for read access and keydb_store_cert appends data at the end.

I'd sad we keep it as it is now (unless we see a regression). The real and only correct solution is the use of a daemon to serialize access.