KEYS: load key flags and expiry time atomically in key_validate()
authorEric Biggers <ebiggers@google.com>
Wed, 27 Sep 2017 19:50:44 +0000 (12:50 -0700)
committerDavid Howells <dhowells@redhat.com>
Wed, 18 Oct 2017 08:12:41 +0000 (09:12 +0100)
In key_validate(), load the flags and expiry time once atomically, since
these can change concurrently if key_validate() is called without the
key semaphore held.  And we don't want to get inconsistent results if a
variable is referenced multiple times.  For example, key->expiry was
referenced in both 'if (key->expiry)' and in 'if (now.tv_sec >=
key->expiry)', making it theoretically possible to see a spurious
EKEYEXPIRED while the expiration time was being removed, i.e. set to 0.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
security/keys/permission.c

index 732cc0beffdfc74b74eddbf173ee30bd9d0455db..a72b4dd70c8abfab5d05503415c4a5fcef7b6df3 100644 (file)
@@ -88,7 +88,8 @@ EXPORT_SYMBOL(key_task_permission);
  */
 int key_validate(const struct key *key)
 {
-       unsigned long flags = key->flags;
+       unsigned long flags = READ_ONCE(key->flags);
+       time_t expiry = READ_ONCE(key->expiry);
 
        if (flags & (1 << KEY_FLAG_INVALIDATED))
                return -ENOKEY;
@@ -99,9 +100,9 @@ int key_validate(const struct key *key)
                return -EKEYREVOKED;
 
        /* check it hasn't expired */
-       if (key->expiry) {
+       if (expiry) {
                struct timespec now = current_kernel_time();
-               if (now.tv_sec >= key->expiry)
+               if (now.tv_sec >= expiry)
                        return -EKEYEXPIRED;
        }