big_keys: Use struct for internal payload

The randstruct GCC plugin gets upset when it sees struct path (which is
randomized) being assigned from a "void *" (which it cannot type-check).

There's no need for these casts, as the entire internal payload use is
following a normal struct layout. Convert the enum-based void * offset
dereferencing to the new big_key_payload struct. No meaningful machine
code changes result after this change, and source readability is improved.

Drop the randstruct exception now that there is no "confusing" cross-type
assignment.

Cc: David Howells <dhowells@redhat.com>
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Jarkko Sakkinen <jarkko@kernel.org>
Cc: James Morris <jmorris@namei.org>
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: linux-hardening@vger.kernel.org
Cc: keyrings@vger.kernel.org
Cc: linux-security-module@vger.kernel.org
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Kees Cook 2022-05-08 09:15:53 -07:00
parent 61f60bac8c
commit c1298a3a11
2 changed files with 36 additions and 39 deletions

View File

@ -50,8 +50,6 @@ static const struct whitelist_entry whitelist[] = {
{ "drivers/net/ethernet/sun/niu.c", "page", "address_space" }, { "drivers/net/ethernet/sun/niu.c", "page", "address_space" },
/* unix_skb_parms via UNIXCB() buffer */ /* unix_skb_parms via UNIXCB() buffer */
{ "net/unix/af_unix.c", "unix_skb_parms", "char" }, { "net/unix/af_unix.c", "unix_skb_parms", "char" },
/* big_key payload.data struct splashing */
{ "security/keys/big_key.c", "path", "void *" },
{ } { }
}; };

View File

@ -20,12 +20,13 @@
/* /*
* Layout of key payload words. * Layout of key payload words.
*/ */
enum { struct big_key_payload {
big_key_data, u8 *data;
big_key_path, struct path path;
big_key_path_2nd_part, size_t length;
big_key_len,
}; };
#define to_big_key_payload(payload) \
(struct big_key_payload *)((payload).data)
/* /*
* If the data is under this limit, there's no point creating a shm file to * If the data is under this limit, there's no point creating a shm file to
@ -55,7 +56,7 @@ struct key_type key_type_big_key = {
*/ */
int big_key_preparse(struct key_preparsed_payload *prep) int big_key_preparse(struct key_preparsed_payload *prep)
{ {
struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct big_key_payload *payload = to_big_key_payload(prep->payload);
struct file *file; struct file *file;
u8 *buf, *enckey; u8 *buf, *enckey;
ssize_t written; ssize_t written;
@ -63,13 +64,15 @@ int big_key_preparse(struct key_preparsed_payload *prep)
size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
int ret; int ret;
BUILD_BUG_ON(sizeof(*payload) != sizeof(prep->payload.data));
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
return -EINVAL; return -EINVAL;
/* Set an arbitrary quota */ /* Set an arbitrary quota */
prep->quotalen = 16; prep->quotalen = 16;
prep->payload.data[big_key_len] = (void *)(unsigned long)datalen; payload->length = datalen;
if (datalen > BIG_KEY_FILE_THRESHOLD) { if (datalen > BIG_KEY_FILE_THRESHOLD) {
/* Create a shmem file to store the data in. This will permit the data /* Create a shmem file to store the data in. This will permit the data
@ -117,9 +120,9 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Pin the mount and dentry to the key so that we can open it again /* Pin the mount and dentry to the key so that we can open it again
* later * later
*/ */
prep->payload.data[big_key_data] = enckey; payload->data = enckey;
*path = file->f_path; payload->path = file->f_path;
path_get(path); path_get(&payload->path);
fput(file); fput(file);
kvfree_sensitive(buf, enclen); kvfree_sensitive(buf, enclen);
} else { } else {
@ -129,7 +132,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
prep->payload.data[big_key_data] = data; payload->data = data;
memcpy(data, prep->data, prep->datalen); memcpy(data, prep->data, prep->datalen);
} }
return 0; return 0;
@ -148,12 +151,11 @@ error:
*/ */
void big_key_free_preparse(struct key_preparsed_payload *prep) void big_key_free_preparse(struct key_preparsed_payload *prep)
{ {
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { struct big_key_payload *payload = to_big_key_payload(prep->payload);
struct path *path = (struct path *)&prep->payload.data[big_key_path];
path_put(path); if (prep->datalen > BIG_KEY_FILE_THRESHOLD)
} path_put(&payload->path);
kfree_sensitive(prep->payload.data[big_key_data]); kfree_sensitive(payload->data);
} }
/* /*
@ -162,13 +164,12 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
*/ */
void big_key_revoke(struct key *key) void big_key_revoke(struct key *key)
{ {
struct path *path = (struct path *)&key->payload.data[big_key_path]; struct big_key_payload *payload = to_big_key_payload(key->payload);
/* clear the quota */ /* clear the quota */
key_payload_reserve(key, 0); key_payload_reserve(key, 0);
if (key_is_positive(key) && if (key_is_positive(key) && payload->length > BIG_KEY_FILE_THRESHOLD)
(size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) vfs_truncate(&payload->path, 0);
vfs_truncate(path, 0);
} }
/* /*
@ -176,17 +177,15 @@ void big_key_revoke(struct key *key)
*/ */
void big_key_destroy(struct key *key) void big_key_destroy(struct key *key)
{ {
size_t datalen = (size_t)key->payload.data[big_key_len]; struct big_key_payload *payload = to_big_key_payload(key->payload);
if (datalen > BIG_KEY_FILE_THRESHOLD) { if (payload->length > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path]; path_put(&payload->path);
payload->path.mnt = NULL;
path_put(path); payload->path.dentry = NULL;
path->mnt = NULL;
path->dentry = NULL;
} }
kfree_sensitive(key->payload.data[big_key_data]); kfree_sensitive(payload->data);
key->payload.data[big_key_data] = NULL; payload->data = NULL;
} }
/* /*
@ -211,14 +210,14 @@ int big_key_update(struct key *key, struct key_preparsed_payload *prep)
*/ */
void big_key_describe(const struct key *key, struct seq_file *m) void big_key_describe(const struct key *key, struct seq_file *m)
{ {
size_t datalen = (size_t)key->payload.data[big_key_len]; struct big_key_payload *payload = to_big_key_payload(key->payload);
seq_puts(m, key->description); seq_puts(m, key->description);
if (key_is_positive(key)) if (key_is_positive(key))
seq_printf(m, ": %zu [%s]", seq_printf(m, ": %zu [%s]",
datalen, payload->length,
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); payload->length > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
} }
/* /*
@ -227,16 +226,16 @@ void big_key_describe(const struct key *key, struct seq_file *m)
*/ */
long big_key_read(const struct key *key, char *buffer, size_t buflen) long big_key_read(const struct key *key, char *buffer, size_t buflen)
{ {
size_t datalen = (size_t)key->payload.data[big_key_len]; struct big_key_payload *payload = to_big_key_payload(key->payload);
size_t datalen = payload->length;
long ret; long ret;
if (!buffer || buflen < datalen) if (!buffer || buflen < datalen)
return datalen; return datalen;
if (datalen > BIG_KEY_FILE_THRESHOLD) { if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path];
struct file *file; struct file *file;
u8 *buf, *enckey = (u8 *)key->payload.data[big_key_data]; u8 *buf, *enckey = payload->data;
size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
loff_t pos = 0; loff_t pos = 0;
@ -244,7 +243,7 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
file = dentry_open(path, O_RDONLY, current_cred()); file = dentry_open(&payload->path, O_RDONLY, current_cred());
if (IS_ERR(file)) { if (IS_ERR(file)) {
ret = PTR_ERR(file); ret = PTR_ERR(file);
goto error; goto error;
@ -274,7 +273,7 @@ error:
kvfree_sensitive(buf, enclen); kvfree_sensitive(buf, enclen);
} else { } else {
ret = datalen; ret = datalen;
memcpy(buffer, key->payload.data[big_key_data], datalen); memcpy(buffer, payload->data, datalen);
} }
return ret; return ret;