keys: Simplify key description management

Simplify key description management by cramming the word containing the
length with the first few chars of the description also.  This simplifies
the code that generates the index-key used by assoc_array.  It should speed
up key searching a bit too.

Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
David Howells 2019-06-26 21:02:31 +01:00
parent 3b8c4a08a4
commit f771fde820
5 changed files with 43 additions and 50 deletions

View File

@ -86,9 +86,20 @@ struct keyring_list;
struct keyring_name; struct keyring_name;
struct keyring_index_key { struct keyring_index_key {
union {
struct {
#ifdef __LITTLE_ENDIAN /* Put desc_len at the LSB of x */
u8 desc_len;
char desc[sizeof(long) - 1]; /* First few chars of description */
#else
char desc[sizeof(long) - 1]; /* First few chars of description */
u8 desc_len;
#endif
};
unsigned long x;
};
struct key_type *type; struct key_type *type;
const char *description; const char *description;
size_t desc_len;
}; };
union key_payload { union key_payload {
@ -202,6 +213,7 @@ struct key {
union { union {
struct keyring_index_key index_key; struct keyring_index_key index_key;
struct { struct {
unsigned long len_desc;
struct key_type *type; /* type of key */ struct key_type *type; /* type of key */
char *description; char *description;
}; };

View File

@ -90,6 +90,12 @@ extern struct mutex key_construction_mutex;
extern wait_queue_head_t request_key_conswq; extern wait_queue_head_t request_key_conswq;
static inline void key_set_index_key(struct keyring_index_key *index_key)
{
size_t n = min_t(size_t, index_key->desc_len, sizeof(index_key->desc));
memcpy(index_key->desc, index_key->description, n);
}
extern struct key_type *key_type_lookup(const char *type); extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype); extern void key_type_put(struct key_type *ktype);

View File

@ -285,6 +285,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL); key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
if (!key->index_key.description) if (!key->index_key.description)
goto no_memory_3; goto no_memory_3;
key_set_index_key(&key->index_key);
refcount_set(&key->usage, 1); refcount_set(&key->usage, 1);
init_rwsem(&key->sem); init_rwsem(&key->sem);
@ -868,6 +869,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
goto error_free_prep; goto error_free_prep;
} }
index_key.desc_len = strlen(index_key.description); index_key.desc_len = strlen(index_key.description);
key_set_index_key(&index_key);
ret = __key_link_lock(keyring, &index_key); ret = __key_link_lock(keyring, &index_key);
if (ret < 0) { if (ret < 0) {

View File

@ -179,9 +179,9 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde
int n, desc_len = index_key->desc_len; int n, desc_len = index_key->desc_len;
type = (unsigned long)index_key->type; type = (unsigned long)index_key->type;
acc = mult_64x32_and_fold(type, desc_len + 13); acc = mult_64x32_and_fold(type, desc_len + 13);
acc = mult_64x32_and_fold(acc, 9207); acc = mult_64x32_and_fold(acc, 9207);
for (;;) { for (;;) {
n = desc_len; n = desc_len;
if (n <= 0) if (n <= 0)
@ -215,23 +215,13 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde
/* /*
* Build the next index key chunk. * Build the next index key chunk.
* *
* On 32-bit systems the index key is laid out as:
*
* 0 4 5 9...
* hash desclen typeptr desc[]
*
* On 64-bit systems:
*
* 0 8 9 17...
* hash desclen typeptr desc[]
*
* We return it one word-sized chunk at a time. * We return it one word-sized chunk at a time.
*/ */
static unsigned long keyring_get_key_chunk(const void *data, int level) static unsigned long keyring_get_key_chunk(const void *data, int level)
{ {
const struct keyring_index_key *index_key = data; const struct keyring_index_key *index_key = data;
unsigned long chunk = 0; unsigned long chunk = 0;
long offset = 0; const u8 *d;
int desc_len = index_key->desc_len, n = sizeof(chunk); int desc_len = index_key->desc_len, n = sizeof(chunk);
level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
@ -239,33 +229,23 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
case 0: case 0:
return hash_key_type_and_desc(index_key); return hash_key_type_and_desc(index_key);
case 1: case 1:
return ((unsigned long)index_key->type << 8) | desc_len; return index_key->x;
case 2: case 2:
if (desc_len == 0) return (unsigned long)index_key->type;
return (u8)((unsigned long)index_key->type >>
(ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
n--;
offset = 1;
/* fall through */
default: default:
offset += sizeof(chunk) - 1; level -= 3;
offset += (level - 3) * sizeof(chunk); if (desc_len <= sizeof(index_key->desc))
if (offset >= desc_len)
return 0; return 0;
desc_len -= offset;
d = index_key->description + sizeof(index_key->desc);
d += level * sizeof(long);
desc_len -= sizeof(index_key->desc);
if (desc_len > n) if (desc_len > n)
desc_len = n; desc_len = n;
offset += desc_len;
do { do {
chunk <<= 8; chunk <<= 8;
chunk |= ((u8*)index_key->description)[--offset]; chunk |= *d++;
} while (--desc_len > 0); } while (--desc_len > 0);
if (level == 2) {
chunk <<= 8;
chunk |= (u8)((unsigned long)index_key->type >>
(ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
}
return chunk; return chunk;
} }
} }
@ -304,39 +284,28 @@ static int keyring_diff_objects(const void *object, const void *data)
seg_b = hash_key_type_and_desc(b); seg_b = hash_key_type_and_desc(b);
if ((seg_a ^ seg_b) != 0) if ((seg_a ^ seg_b) != 0)
goto differ; goto differ;
level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
/* The number of bits contributed by the hash is controlled by a /* The number of bits contributed by the hash is controlled by a
* constant in the assoc_array headers. Everything else thereafter we * constant in the assoc_array headers. Everything else thereafter we
* can deal with as being machine word-size dependent. * can deal with as being machine word-size dependent.
*/ */
level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8; seg_a = a->x;
seg_a = a->desc_len; seg_b = b->x;
seg_b = b->desc_len;
if ((seg_a ^ seg_b) != 0) if ((seg_a ^ seg_b) != 0)
goto differ; goto differ;
level += sizeof(unsigned long);
/* The next bit may not work on big endian */ /* The next bit may not work on big endian */
level++;
seg_a = (unsigned long)a->type; seg_a = (unsigned long)a->type;
seg_b = (unsigned long)b->type; seg_b = (unsigned long)b->type;
if ((seg_a ^ seg_b) != 0) if ((seg_a ^ seg_b) != 0)
goto differ; goto differ;
level += sizeof(unsigned long); level += sizeof(unsigned long);
if (a->desc_len == 0)
goto same;
i = 0; i = sizeof(a->desc);
if (((unsigned long)a->description | (unsigned long)b->description) & if (a->desc_len <= i)
(sizeof(unsigned long) - 1)) { goto same;
do {
seg_a = *(unsigned long *)(a->description + i);
seg_b = *(unsigned long *)(b->description + i);
if ((seg_a ^ seg_b) != 0)
goto differ_plus_i;
i += sizeof(unsigned long);
} while (i < (a->desc_len & (sizeof(unsigned long) - 1)));
}
for (; i < a->desc_len; i++) { for (; i < a->desc_len; i++) {
seg_a = *(unsigned char *)(a->description + i); seg_a = *(unsigned char *)(a->description + i);
@ -662,6 +631,9 @@ static bool search_nested_keyrings(struct key *keyring,
BUG_ON((ctx->flags & STATE_CHECKS) == 0 || BUG_ON((ctx->flags & STATE_CHECKS) == 0 ||
(ctx->flags & STATE_CHECKS) == STATE_CHECKS); (ctx->flags & STATE_CHECKS) == STATE_CHECKS);
if (ctx->index_key.description)
key_set_index_key(&ctx->index_key);
/* Check to see if this top-level keyring is what we are looking for /* Check to see if this top-level keyring is what we are looking for
* and whether it is valid or not. * and whether it is valid or not.
*/ */

View File

@ -87,6 +87,7 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
index_key.type = &key_type_keyring; index_key.type = &key_type_keyring;
index_key.description = buf; index_key.description = buf;
index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid)); index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
key_set_index_key(&index_key);
if (ns->persistent_keyring_register) { if (ns->persistent_keyring_register) {
reg_ref = make_key_ref(ns->persistent_keyring_register, true); reg_ref = make_key_ref(ns->persistent_keyring_register, true);