KEYS: load key flags and expiry time atomically in proc_keys_show()
authorEric Biggers <ebiggers@google.com>
Wed, 27 Sep 2017 19:50:46 +0000 (12:50 -0700)
committerDavid Howells <dhowells@redhat.com>
Wed, 18 Oct 2017 08:12:41 +0000 (09:12 +0100)
In proc_keys_show(), the key semaphore is not held, so the key ->flags
and ->expiry can be changed concurrently.  We therefore should read them
atomically just once.

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

index 4089ce1f7757d3590eebbe365a1a932bfe8bd25d..6d1fcbba1e0961927c9b3a6027fcd8084a21f80a 100644 (file)
@@ -179,7 +179,9 @@ static int proc_keys_show(struct seq_file *m, void *v)
        struct rb_node *_p = v;
        struct key *key = rb_entry(_p, struct key, serial_node);
        struct timespec now;
+       time_t expiry;
        unsigned long timo;
+       unsigned long flags;
        key_ref_t key_ref, skey_ref;
        char xbuf[16];
        short state;
@@ -218,12 +220,13 @@ static int proc_keys_show(struct seq_file *m, void *v)
        rcu_read_lock();
 
        /* come up with a suitable timeout value */
-       if (key->expiry == 0) {
+       expiry = READ_ONCE(key->expiry);
+       if (expiry == 0) {
                memcpy(xbuf, "perm", 5);
-       } else if (now.tv_sec >= key->expiry) {
+       } else if (now.tv_sec >= expiry) {
                memcpy(xbuf, "expd", 5);
        } else {
-               timo = key->expiry - now.tv_sec;
+               timo = expiry - now.tv_sec;
 
                if (timo < 60)
                        sprintf(xbuf, "%lus", timo);
@@ -239,18 +242,19 @@ static int proc_keys_show(struct seq_file *m, void *v)
 
        state = key_read_state(key);
 
-#define showflag(KEY, LETTER, FLAG) \
-       (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
+#define showflag(FLAGS, LETTER, FLAG) \
+       ((FLAGS & (1 << FLAG)) ? LETTER : '-')
 
+       flags = READ_ONCE(key->flags);
        seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
                   key->serial,
                   state != KEY_IS_UNINSTANTIATED ? 'I' : '-',
-                  showflag(key, 'R', KEY_FLAG_REVOKED),
-                  showflag(key, 'D', KEY_FLAG_DEAD),
-                  showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
-                  showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
+                  showflag(flags, 'R', KEY_FLAG_REVOKED),
+                  showflag(flags, 'D', KEY_FLAG_DEAD),
+                  showflag(flags, 'Q', KEY_FLAG_IN_QUOTA),
+                  showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT),
                   state < 0 ? 'N' : '-',
-                  showflag(key, 'i', KEY_FLAG_INVALIDATED),
+                  showflag(flags, 'i', KEY_FLAG_INVALIDATED),
                   refcount_read(&key->usage),
                   xbuf,
                   key->perm,