2020-07-24 21:39:48

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 00/19] Introduce partial kernel_read_file() support

v3:
- add reviews/acks
- add "IMA: Add support for file reads without contents" patch
- trim CC list, in case that's why vger ignored v2
v2: [missing from lkml archives! (CC list too long?) repeating changes here]
- fix issues in firmware test suite
- add firmware partial read patches
- various bug fixes/cleanups
v1: https://lore.kernel.org/lkml/[email protected]/

Hi,

Here's my tree for adding partial read support in kernel_read_file(),
which fixes a number of issues along the way. It's got Scott's firmware
and IMA patches ported and everything tests cleanly for me (even with
CONFIG_IMA_APPRAISE=y).

I think the intention is for this to go via Greg's tree since Scott's
driver code will depend on it?

Thanks,

-Kees


Kees Cook (15):
test_firmware: Test platform fw loading on non-EFI systems
selftest/firmware: Add selftest timeout in settings
firmware_loader: EFI firmware loader must handle pre-allocated buffer
fs/kernel_read_file: Remove FIRMWARE_PREALLOC_BUFFER enum
fs/kernel_read_file: Remove FIRMWARE_EFI_EMBEDDED enum
fs/kernel_read_file: Split into separate source file
fs/kernel_read_file: Remove redundant size argument
fs/kernel_read_file: Switch buffer size arg to size_t
fs/kernel_read_file: Add file_size output argument
LSM: Introduce kernel_post_load_data() hook
firmware_loader: Use security_post_load_data()
module: Call security_kernel_post_load_data()
LSM: Add "contents" flag to kernel_read_file hook
fs/kernel_file_read: Add "offset" arg for partial reads
firmware: Store opt_flags in fw_priv

Scott Branden (4):
fs/kernel_read_file: Split into separate include file
IMA: Add support for file reads without contents
firmware: Add request_partial_firmware_into_buf()
test_firmware: Test partial read support

drivers/base/firmware_loader/fallback.c | 19 +-
drivers/base/firmware_loader/fallback.h | 5 +-
.../base/firmware_loader/fallback_platform.c | 16 +-
drivers/base/firmware_loader/firmware.h | 7 +-
drivers/base/firmware_loader/main.c | 143 ++++++++++---
drivers/firmware/efi/embedded-firmware.c | 21 +-
drivers/firmware/efi/embedded-firmware.h | 19 ++
fs/Makefile | 3 +-
fs/exec.c | 132 +-----------
fs/kernel_read_file.c | 189 ++++++++++++++++++
include/linux/efi_embedded_fw.h | 13 --
include/linux/firmware.h | 12 ++
include/linux/fs.h | 39 ----
include/linux/ima.h | 19 +-
include/linux/kernel_read_file.h | 55 +++++
include/linux/lsm_hook_defs.h | 6 +-
include/linux/lsm_hooks.h | 12 ++
include/linux/security.h | 19 +-
kernel/kexec.c | 2 +-
kernel/kexec_file.c | 19 +-
kernel/module.c | 24 ++-
lib/test_firmware.c | 159 +++++++++++++--
security/integrity/digsig.c | 8 +-
security/integrity/ima/ima_fs.c | 10 +-
security/integrity/ima/ima_main.c | 70 +++++--
security/integrity/ima/ima_policy.c | 1 +
security/loadpin/loadpin.c | 17 +-
security/security.c | 26 ++-
security/selinux/hooks.c | 8 +-
.../selftests/firmware/fw_filesystem.sh | 91 +++++++++
tools/testing/selftests/firmware/settings | 8 +
tools/testing/selftests/kselftest/runner.sh | 6 +-
32 files changed, 860 insertions(+), 318 deletions(-)
create mode 100644 drivers/firmware/efi/embedded-firmware.h
create mode 100644 fs/kernel_read_file.c
create mode 100644 include/linux/kernel_read_file.h
create mode 100644 tools/testing/selftests/firmware/settings

--
2.25.1


2020-07-24 21:39:51

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 15/19] IMA: Add support for file reads without contents

From: Scott Branden <[email protected]>

When the kernel_read_file LSM hook is called with contents=false, IMA
can appraise the file directly, without requiring a filled buffer. When
such a buffer is available, though, IMA can continue to use it instead
of forcing a double read here.

Signed-off-by: Scott Branden <[email protected]>
Link: https://lore.kernel.org/lkml/[email protected]/
Signed-off-by: Kees Cook <[email protected]>
---
security/integrity/ima/ima_main.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index dc4f90660aa6..459e50526a12 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -613,11 +613,8 @@ void ima_post_path_mknod(struct dentry *dentry)
int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
bool contents)
{
- /* Reject all partial reads during appraisal. */
- if (!contents) {
- if (ima_appraise & IMA_APPRAISE_ENFORCE)
- return -EACCES;
- }
+ enum ima_hooks func;
+ u32 secid;

/*
* Do devices using pre-allocated memory run the risk of the
@@ -626,7 +623,20 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
* buffers? It may be desirable to include the buffer address
* in this API and walk all the dma_map_single() mappings to check.
*/
- return 0;
+
+ /*
+ * There will be a call made to ima_post_read_file() with
+ * a filled buffer, so we don't need to perform an extra
+ * read early here.
+ */
+ if (contents)
+ return 0;
+
+ /* Read entire file for all partial reads during appraisal. */
+ func = read_idmap[read_id] ?: FILE_CHECK;
+ security_task_getsecid(current, &secid);
+ return process_measurement(file, current_cred(), secid, NULL,
+ 0, MAY_READ, func);
}

const int read_idmap[READING_MAX_ID] = {
--
2.25.1

2020-07-24 21:41:59

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 03/19] firmware_loader: EFI firmware loader must handle pre-allocated buffer

The EFI platform firmware fallback would clobber any pre-allocated
buffers. Instead, correctly refuse to reallocate when too small (as
already done in the sysfs fallback), or perform allocation normally
when needed.

Fixes: e4c2c0ff00ec ("firmware: Add new platform fallback mechanism and firm ware_request_platform()")
Cc: [email protected]
Acked-by: Scott Branden <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
---
To aid in backporting, this change is made before moving
kernel_read_file() to separate header/source files.
---
drivers/base/firmware_loader/fallback_platform.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/base/firmware_loader/fallback_platform.c b/drivers/base/firmware_loader/fallback_platform.c
index cdd2c9a9f38a..685edb7dd05a 100644
--- a/drivers/base/firmware_loader/fallback_platform.c
+++ b/drivers/base/firmware_loader/fallback_platform.c
@@ -25,7 +25,10 @@ int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags)
if (rc)
return rc; /* rc == -ENOENT when the fw was not found */

- fw_priv->data = vmalloc(size);
+ if (fw_priv->data && size > fw_priv->allocated_size)
+ return -ENOMEM;
+ if (!fw_priv->data)
+ fw_priv->data = vmalloc(size);
if (!fw_priv->data)
return -ENOMEM;

--
2.25.1

2020-07-24 21:43:57

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 19/19] test_firmware: Test partial read support

From: Scott Branden <[email protected]>

Add additional hooks to test_firmware to pass in support
for partial file read using request_firmware_into_buf():

buf_size: size of buffer to request firmware into
partial: indicates that a partial file request is being made
file_offset: to indicate offset into file to request

Also update firmware selftests to use the new partial read test API.

Signed-off-by: Scott Branden <[email protected]>
Co-developed-by: Kees Cook <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
---
lib/test_firmware.c | 154 ++++++++++++++++--
.../selftests/firmware/fw_filesystem.sh | 91 +++++++++++
2 files changed, 233 insertions(+), 12 deletions(-)

diff --git a/lib/test_firmware.c b/lib/test_firmware.c
index 62af792e151c..387acb94eeea 100644
--- a/lib/test_firmware.c
+++ b/lib/test_firmware.c
@@ -50,6 +50,9 @@ struct test_batched_req {
* @name: the name of the firmware file to look for
* @into_buf: when the into_buf is used if this is true
* request_firmware_into_buf() will be used instead.
+ * @buf_size: size of buf to allocate when into_buf is true
+ * @file_offset: file offset to request when calling request_firmware_into_buf
+ * @partial: partial read opt when calling request_firmware_into_buf
* @sync_direct: when the sync trigger is used if this is true
* request_firmware_direct() will be used instead.
* @send_uevent: whether or not to send a uevent for async requests
@@ -89,6 +92,9 @@ struct test_batched_req {
struct test_config {
char *name;
bool into_buf;
+ size_t buf_size;
+ size_t file_offset;
+ bool partial;
bool sync_direct;
bool send_uevent;
u8 num_requests;
@@ -183,6 +189,9 @@ static int __test_firmware_config_init(void)
test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
test_fw_config->send_uevent = true;
test_fw_config->into_buf = false;
+ test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
+ test_fw_config->file_offset = 0;
+ test_fw_config->partial = false;
test_fw_config->sync_direct = false;
test_fw_config->req_firmware = request_firmware;
test_fw_config->test_result = 0;
@@ -236,28 +245,35 @@ static ssize_t config_show(struct device *dev,
dev_name(dev));

if (test_fw_config->name)
- len += scnprintf(buf+len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"name:\t%s\n",
test_fw_config->name);
else
- len += scnprintf(buf+len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"name:\tEMTPY\n");

- len += scnprintf(buf+len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"num_requests:\t%u\n", test_fw_config->num_requests);

- len += scnprintf(buf+len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"send_uevent:\t\t%s\n",
test_fw_config->send_uevent ?
"FW_ACTION_HOTPLUG" :
"FW_ACTION_NOHOTPLUG");
- len += scnprintf(buf+len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"into_buf:\t\t%s\n",
test_fw_config->into_buf ? "true" : "false");
- len += scnprintf(buf+len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "buf_size:\t%zu\n", test_fw_config->buf_size);
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "file_offset:\t%zu\n", test_fw_config->file_offset);
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "partial:\t\t%s\n",
+ test_fw_config->partial ? "true" : "false");
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"sync_direct:\t\t%s\n",
test_fw_config->sync_direct ? "true" : "false");
- len += scnprintf(buf+len, PAGE_SIZE - len,
+ len += scnprintf(buf + len, PAGE_SIZE - len,
"read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);

mutex_unlock(&test_fw_mutex);
@@ -315,6 +331,30 @@ static ssize_t test_dev_config_show_bool(char *buf, bool val)
return snprintf(buf, PAGE_SIZE, "%d\n", val);
}

+static int test_dev_config_update_size_t(const char *buf,
+ size_t size,
+ size_t *cfg)
+{
+ int ret;
+ long new;
+
+ ret = kstrtol(buf, 10, &new);
+ if (ret)
+ return ret;
+
+ mutex_lock(&test_fw_mutex);
+ *(size_t *)cfg = new;
+ mutex_unlock(&test_fw_mutex);
+
+ /* Always return full write size even if we didn't consume all */
+ return size;
+}
+
+static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
+{
+ return snprintf(buf, PAGE_SIZE, "%zu\n", val);
+}
+
static ssize_t test_dev_config_show_int(char *buf, int val)
{
return snprintf(buf, PAGE_SIZE, "%d\n", val);
@@ -400,6 +440,83 @@ static ssize_t config_into_buf_show(struct device *dev,
}
static DEVICE_ATTR_RW(config_into_buf);

+static ssize_t config_buf_size_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+
+ mutex_lock(&test_fw_mutex);
+ if (test_fw_config->reqs) {
+ pr_err("Must call release_all_firmware prior to changing config\n");
+ rc = -EINVAL;
+ mutex_unlock(&test_fw_mutex);
+ goto out;
+ }
+ mutex_unlock(&test_fw_mutex);
+
+ rc = test_dev_config_update_size_t(buf, count,
+ &test_fw_config->buf_size);
+
+out:
+ return rc;
+}
+
+static ssize_t config_buf_size_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
+}
+static DEVICE_ATTR_RW(config_buf_size);
+
+static ssize_t config_file_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+
+ mutex_lock(&test_fw_mutex);
+ if (test_fw_config->reqs) {
+ pr_err("Must call release_all_firmware prior to changing config\n");
+ rc = -EINVAL;
+ mutex_unlock(&test_fw_mutex);
+ goto out;
+ }
+ mutex_unlock(&test_fw_mutex);
+
+ rc = test_dev_config_update_size_t(buf, count,
+ &test_fw_config->file_offset);
+
+out:
+ return rc;
+}
+
+static ssize_t config_file_offset_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
+}
+static DEVICE_ATTR_RW(config_file_offset);
+
+static ssize_t config_partial_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return test_dev_config_update_bool(buf,
+ count,
+ &test_fw_config->partial);
+}
+
+static ssize_t config_partial_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return test_dev_config_show_bool(buf, test_fw_config->partial);
+}
+static DEVICE_ATTR_RW(config_partial);
+
static ssize_t config_sync_direct_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -655,11 +772,21 @@ static int test_fw_run_batch_request(void *data)
if (!test_buf)
return -ENOSPC;

- req->rc = request_firmware_into_buf(&req->fw,
- req->name,
- req->dev,
- test_buf,
- TEST_FIRMWARE_BUF_SIZE);
+ if (test_fw_config->partial)
+ req->rc = request_partial_firmware_into_buf
+ (&req->fw,
+ req->name,
+ req->dev,
+ test_buf,
+ test_fw_config->buf_size,
+ test_fw_config->file_offset);
+ else
+ req->rc = request_firmware_into_buf
+ (&req->fw,
+ req->name,
+ req->dev,
+ test_buf,
+ test_fw_config->buf_size);
if (!req->fw)
kfree(test_buf);
} else {
@@ -932,6 +1059,9 @@ static struct attribute *test_dev_attrs[] = {
TEST_FW_DEV_ATTR(config_name),
TEST_FW_DEV_ATTR(config_num_requests),
TEST_FW_DEV_ATTR(config_into_buf),
+ TEST_FW_DEV_ATTR(config_buf_size),
+ TEST_FW_DEV_ATTR(config_file_offset),
+ TEST_FW_DEV_ATTR(config_partial),
TEST_FW_DEV_ATTR(config_sync_direct),
TEST_FW_DEV_ATTR(config_send_uevent),
TEST_FW_DEV_ATTR(config_read_fw_idx),
diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh
index fcc281373b4d..c2a2a100114b 100755
--- a/tools/testing/selftests/firmware/fw_filesystem.sh
+++ b/tools/testing/selftests/firmware/fw_filesystem.sh
@@ -149,6 +149,26 @@ config_unset_into_buf()
echo 0 > $DIR/config_into_buf
}

+config_set_buf_size()
+{
+ echo $1 > $DIR/config_buf_size
+}
+
+config_set_file_offset()
+{
+ echo $1 > $DIR/config_file_offset
+}
+
+config_set_partial()
+{
+ echo 1 > $DIR/config_partial
+}
+
+config_unset_partial()
+{
+ echo 0 > $DIR/config_partial
+}
+
config_set_sync_direct()
{
echo 1 > $DIR/config_sync_direct
@@ -207,6 +227,35 @@ read_firmwares()
done
}

+read_partial_firmwares()
+{
+ if [ "$(cat $DIR/config_into_buf)" == "1" ]; then
+ fwfile="${FW_INTO_BUF}"
+ else
+ fwfile="${FW}"
+ fi
+
+ if [ "$1" = "xzonly" ]; then
+ fwfile="${fwfile}-orig"
+ fi
+
+ # Strip fwfile down to match partial offset and length
+ partial_data="$(cat $fwfile)"
+ partial_data="${partial_data:$2:$3}"
+
+ for i in $(seq 0 3); do
+ config_set_read_fw_idx $i
+
+ read_firmware="$(cat $DIR/read_firmware)"
+
+ # Verify the contents are what we expect.
+ if [ $read_firmware != $partial_data ]; then
+ echo "request #$i: partial firmware was not loaded" >&2
+ exit 1
+ fi
+ done
+}
+
read_firmwares_expect_nofile()
{
for i in $(seq 0 3); do
@@ -242,6 +291,21 @@ test_batched_request_firmware_into_buf_nofile()
echo "OK"
}

+test_request_partial_firmware_into_buf_nofile()
+{
+ echo -n "Test request_partial_firmware_into_buf() off=$1 size=$2 nofile: "
+ config_reset
+ config_set_name nope-test-firmware.bin
+ config_set_into_buf
+ config_set_partial
+ config_set_buf_size $2
+ config_set_file_offset $1
+ config_trigger_sync
+ read_firmwares_expect_nofile
+ release_all_firmware
+ echo "OK"
+}
+
test_batched_request_firmware_direct_nofile()
{
echo -n "Batched request_firmware_direct() nofile try #$1: "
@@ -356,6 +420,21 @@ test_request_firmware_nowait_custom()
echo "OK"
}

+test_request_partial_firmware_into_buf()
+{
+ echo -n "Test request_partial_firmware_into_buf() off=$1 size=$2: "
+ config_reset
+ config_set_name $TEST_FIRMWARE_INTO_BUF_FILENAME
+ config_set_into_buf
+ config_set_partial
+ config_set_buf_size $2
+ config_set_file_offset $1
+ config_trigger_sync
+ read_partial_firmwares normal $1 $2
+ release_all_firmware
+ echo "OK"
+}
+
# Only continue if batched request triggers are present on the
# test-firmware driver
test_config_present
@@ -383,6 +462,12 @@ for i in $(seq 1 5); do
test_request_firmware_nowait_custom $i normal
done

+# Partial loads cannot use fallback, so do not repeat tests.
+test_request_partial_firmware_into_buf 0 10
+test_request_partial_firmware_into_buf 0 5
+test_request_partial_firmware_into_buf 1 6
+test_request_partial_firmware_into_buf 2 10
+
# Test for file not found, errors are expected, the failure would be
# a hung task, which would require a hard reset.
echo
@@ -407,6 +492,12 @@ for i in $(seq 1 5); do
test_request_firmware_nowait_custom_nofile $i
done

+# Partial loads cannot use fallback, so do not repeat tests.
+test_request_partial_firmware_into_buf_nofile 0 10
+test_request_partial_firmware_into_buf_nofile 0 5
+test_request_partial_firmware_into_buf_nofile 1 6
+test_request_partial_firmware_into_buf_nofile 2 10
+
test "$HAS_FW_LOADER_COMPRESS" != "yes" && exit 0

# test with both files present
--
2.25.1

2020-07-24 21:44:20

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 16/19] fs/kernel_file_read: Add "offset" arg for partial reads

To perform partial reads, callers of kernel_read_file*() must have a
non-NULL file_size argument and a preallocated buffer. The new "offset"
argument can then be used to seek to specific locations in the file to
fill the buffer to, at most, "buf_size" per call.

Where possible, the LSM hooks can report whether a full file has been
read or not so that the contents can be reasoned about.

Signed-off-by: Kees Cook <[email protected]>
---
drivers/base/firmware_loader/main.c | 2 +-
fs/kernel_read_file.c | 78 ++++++++++++++++++++---------
include/linux/kernel_read_file.h | 8 +--
kernel/kexec_file.c | 4 +-
kernel/module.c | 2 +-
security/integrity/digsig.c | 2 +-
security/integrity/ima/ima_fs.c | 3 +-
7 files changed, 65 insertions(+), 34 deletions(-)

diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index bd199404935f..d95249b5284e 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -494,7 +494,7 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
fw_priv->size = 0;

/* load firmware files from the mount namespace of init */
- rc = kernel_read_file_from_path_initns(path, &buffer, msize,
+ rc = kernel_read_file_from_path_initns(path, 0, &buffer, msize,
NULL,
READING_FIRMWARE);
if (rc < 0) {
diff --git a/fs/kernel_read_file.c b/fs/kernel_read_file.c
index d73bc3fa710a..90d255fbdd9b 100644
--- a/fs/kernel_read_file.c
+++ b/fs/kernel_read_file.c
@@ -9,6 +9,7 @@
* kernel_read_file() - read file contents into a kernel buffer
*
* @file file to read from
+ * @offset where to start reading from (see below).
* @buf pointer to a "void *" buffer for reading into (if
* *@buf is NULL, a buffer will be allocated, and
* @buf_size will be ignored)
@@ -19,19 +20,31 @@
* @id the kernel_read_file_id identifying the type of
* file contents being read (for LSMs to examine)
*
+ * @offset must be 0 unless both @buf and @file_size are non-NULL
+ * (i.e. the caller must be expecting to read partial file contents
+ * via an already-allocated @buf, in at most @buf_size chunks, and
+ * will be able to determine when the entire file was read by
+ * checking @file_size). This isn't a recommended way to read a
+ * file, though, since it is possible that the contents might
+ * change between calls to kernel_read_file().
+ *
* Returns number of bytes read (no single read will be bigger
* than INT_MAX), or negative on error.
*
*/
-int kernel_read_file(struct file *file, void **buf,
+int kernel_read_file(struct file *file, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
{
loff_t i_size, pos;
- ssize_t bytes = 0;
+ size_t copied;
void *allocated = NULL;
+ bool whole_file;
int ret;

+ if (offset != 0 && (!*buf || !file_size))
+ return -EINVAL;
+
if (!S_ISREG(file_inode(file)->i_mode))
return -EINVAL;

@@ -39,19 +52,27 @@ int kernel_read_file(struct file *file, void **buf,
if (ret)
return ret;

- ret = security_kernel_read_file(file, id, true);
- if (ret)
- goto out;
-
i_size = i_size_read(file_inode(file));
if (i_size <= 0) {
ret = -EINVAL;
goto out;
}
- if (i_size > INT_MAX || i_size > buf_size) {
+ /* The file is too big for sane activities. */
+ if (i_size > INT_MAX) {
+ ret = -EFBIG;
+ goto out;
+ }
+ /* The entire file cannot be read in one buffer. */
+ if (!file_size && offset == 0 && i_size > buf_size) {
ret = -EFBIG;
goto out;
}
+
+ whole_file = (offset == 0 && i_size <= buf_size);
+ ret = security_kernel_read_file(file, id, whole_file);
+ if (ret)
+ goto out;
+
if (file_size)
*file_size = i_size;

@@ -62,9 +83,14 @@ int kernel_read_file(struct file *file, void **buf,
goto out;
}

- pos = 0;
- while (pos < i_size) {
- bytes = kernel_read(file, *buf + pos, i_size - pos, &pos);
+ pos = offset;
+ copied = 0;
+ while (copied < buf_size) {
+ ssize_t bytes;
+ size_t wanted = min_t(size_t, buf_size - copied,
+ i_size - pos);
+
+ bytes = kernel_read(file, *buf + copied, wanted, &pos);
if (bytes < 0) {
ret = bytes;
goto out_free;
@@ -72,14 +98,17 @@ int kernel_read_file(struct file *file, void **buf,

if (bytes == 0)
break;
+ copied += bytes;
}

- if (pos != i_size) {
- ret = -EIO;
- goto out_free;
- }
+ if (whole_file) {
+ if (pos != i_size) {
+ ret = -EIO;
+ goto out_free;
+ }

- ret = security_kernel_post_read_file(file, *buf, i_size, id);
+ ret = security_kernel_post_read_file(file, *buf, i_size, id);
+ }

out_free:
if (ret < 0) {
@@ -91,11 +120,11 @@ int kernel_read_file(struct file *file, void **buf,

out:
allow_write_access(file);
- return ret == 0 ? pos : ret;
+ return ret == 0 ? copied : ret;
}
EXPORT_SYMBOL_GPL(kernel_read_file);

-int kernel_read_file_from_path(const char *path, void **buf,
+int kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
{
@@ -109,14 +138,15 @@ int kernel_read_file_from_path(const char *path, void **buf,
if (IS_ERR(file))
return PTR_ERR(file);

- ret = kernel_read_file(file, buf, buf_size, file_size, id);
+ ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
fput(file);
return ret;
}
EXPORT_SYMBOL_GPL(kernel_read_file_from_path);

-int kernel_read_file_from_path_initns(const char *path, void **buf,
- size_t buf_size, size_t *file_size,
+int kernel_read_file_from_path_initns(const char *path, loff_t offset,
+ void **buf, size_t buf_size,
+ size_t *file_size,
enum kernel_read_file_id id)
{
struct file *file;
@@ -135,14 +165,14 @@ int kernel_read_file_from_path_initns(const char *path, void **buf,
if (IS_ERR(file))
return PTR_ERR(file);

- ret = kernel_read_file(file, buf, buf_size, file_size, id);
+ ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
fput(file);
return ret;
}
EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);

-int kernel_read_file_from_fd(int fd, void **buf, size_t buf_size,
- size_t *file_size,
+int kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
+ size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
{
struct fd f = fdget(fd);
@@ -151,7 +181,7 @@ int kernel_read_file_from_fd(int fd, void **buf, size_t buf_size,
if (!f.file)
goto out;

- ret = kernel_read_file(f.file, buf, buf_size, file_size, id);
+ ret = kernel_read_file(f.file, offset, buf, buf_size, file_size, id);
out:
fdput(f);
return ret;
diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h
index 023293eaf948..575ffa1031d3 100644
--- a/include/linux/kernel_read_file.h
+++ b/include/linux/kernel_read_file.h
@@ -35,19 +35,19 @@ static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id)
return kernel_read_file_str[id];
}

-int kernel_read_file(struct file *file,
+int kernel_read_file(struct file *file, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
-int kernel_read_file_from_path(const char *path,
+int kernel_read_file_from_path(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
-int kernel_read_file_from_path_initns(const char *path,
+int kernel_read_file_from_path_initns(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
-int kernel_read_file_from_fd(int fd,
+int kernel_read_file_from_fd(int fd, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 878ca684a3a1..45726bc8f6ce 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -221,7 +221,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
int ret;
void *ldata;

- ret = kernel_read_file_from_fd(kernel_fd, &image->kernel_buf,
+ ret = kernel_read_file_from_fd(kernel_fd, 0, &image->kernel_buf,
INT_MAX, NULL, READING_KEXEC_IMAGE);
if (ret < 0)
return ret;
@@ -241,7 +241,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
#endif
/* It is possible that there no initramfs is being loaded */
if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
- ret = kernel_read_file_from_fd(initrd_fd, &image->initrd_buf,
+ ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
INT_MAX, NULL,
READING_KEXEC_INITRAMFS);
if (ret < 0)
diff --git a/kernel/module.c b/kernel/module.c
index 90a4788dff9d..d353d1f67681 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -4007,7 +4007,7 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
|MODULE_INIT_IGNORE_VERMAGIC))
return -EINVAL;

- err = kernel_read_file_from_fd(fd, &hdr, INT_MAX, NULL,
+ err = kernel_read_file_from_fd(fd, 0, &hdr, INT_MAX, NULL,
READING_MODULE);
if (err < 0)
return err;
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 8a523dfd7fd7..0f518dcfde05 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -175,7 +175,7 @@ int __init integrity_load_x509(const unsigned int id, const char *path)
int rc;
key_perm_t perm;

- rc = kernel_read_file_from_path(path, &data, INT_MAX, NULL,
+ rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL,
READING_X509_CERTIFICATE);
if (rc < 0) {
pr_err("Unable to open file: %s (%d)", path, rc);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 5fc56ccb6678..ea8ff8a07b36 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -284,7 +284,8 @@ static ssize_t ima_read_policy(char *path)
datap = path;
strsep(&datap, "\n");

- rc = kernel_read_file_from_path(path, &data, INT_MAX, NULL, READING_POLICY);
+ rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL,
+ READING_POLICY);
if (rc < 0) {
pr_err("Unable to open file: %s (%d)", path, rc);
return rc;
--
2.25.1

2020-07-24 21:45:57

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 17/19] firmware: Store opt_flags in fw_priv

Instead of passing opt_flags around so much, store it in the private
structure so it can be examined by internals without needing to add more
arguments to functions.

Co-developed-by: Scott Branden <[email protected]>
Signed-off-by: Scott Branden <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
---
drivers/base/firmware_loader/fallback.c | 11 +++-----
drivers/base/firmware_loader/fallback.h | 5 ++--
.../base/firmware_loader/fallback_platform.c | 4 +--
drivers/base/firmware_loader/firmware.h | 3 ++-
drivers/base/firmware_loader/main.c | 25 +++++++++++--------
5 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c
index 7cfdfdcb819c..0a94c8739959 100644
--- a/drivers/base/firmware_loader/fallback.c
+++ b/drivers/base/firmware_loader/fallback.c
@@ -490,13 +490,11 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
/**
* fw_load_sysfs_fallback() - load a firmware via the sysfs fallback mechanism
* @fw_sysfs: firmware sysfs information for the firmware to load
- * @opt_flags: flags of options, FW_OPT_*
* @timeout: timeout to wait for the load
*
* In charge of constructing a sysfs fallback interface for firmware loading.
**/
-static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
- u32 opt_flags, long timeout)
+static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
{
int retval = 0;
struct device *f_dev = &fw_sysfs->dev;
@@ -518,7 +516,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
list_add(&fw_priv->pending_list, &pending_fw_head);
mutex_unlock(&fw_lock);

- if (opt_flags & FW_OPT_UEVENT) {
+ if (fw_priv->opt_flags & FW_OPT_UEVENT) {
fw_priv->need_uevent = true;
dev_set_uevent_suppress(f_dev, false);
dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
@@ -580,10 +578,10 @@ static int fw_load_from_user_helper(struct firmware *firmware,
}

fw_sysfs->fw_priv = firmware->priv;
- ret = fw_load_sysfs_fallback(fw_sysfs, opt_flags, timeout);
+ ret = fw_load_sysfs_fallback(fw_sysfs, timeout);

if (!ret)
- ret = assign_fw(firmware, device, opt_flags);
+ ret = assign_fw(firmware, device);

out_unlock:
usermodehelper_read_unlock();
@@ -625,7 +623,6 @@ static bool fw_run_sysfs_fallback(u32 opt_flags)
* @fw: pointer to firmware image
* @name: name of firmware file to look for
* @device: device for which firmware is being loaded
- * @opt_flags: options to control firmware loading behaviour
* @ret: return value from direct lookup which triggered the fallback mechanism
*
* This function is called if direct lookup for the firmware failed, it enables
diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h
index 2afdb6adb23f..3af7205b302f 100644
--- a/drivers/base/firmware_loader/fallback.h
+++ b/drivers/base/firmware_loader/fallback.h
@@ -67,10 +67,9 @@ static inline void unregister_sysfs_loader(void)
#endif /* CONFIG_FW_LOADER_USER_HELPER */

#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
-int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags);
+int firmware_fallback_platform(struct fw_priv *fw_priv);
#else
-static inline int firmware_fallback_platform(struct fw_priv *fw_priv,
- u32 opt_flags)
+static inline int firmware_fallback_platform(struct fw_priv *fw_priv)
{
return -ENOENT;
}
diff --git a/drivers/base/firmware_loader/fallback_platform.c b/drivers/base/firmware_loader/fallback_platform.c
index 4d1157af0e86..38de68d7e973 100644
--- a/drivers/base/firmware_loader/fallback_platform.c
+++ b/drivers/base/firmware_loader/fallback_platform.c
@@ -8,13 +8,13 @@
#include "fallback.h"
#include "firmware.h"

-int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags)
+int firmware_fallback_platform(struct fw_priv *fw_priv)
{
const u8 *data;
size_t size;
int rc;

- if (!(opt_flags & FW_OPT_FALLBACK_PLATFORM))
+ if (!(fw_priv->opt_flags & FW_OPT_FALLBACK_PLATFORM))
return -ENOENT;

rc = security_kernel_load_data(LOADING_FIRMWARE, true);
diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h
index 933e2192fbe8..7ad5fe52bc72 100644
--- a/drivers/base/firmware_loader/firmware.h
+++ b/drivers/base/firmware_loader/firmware.h
@@ -68,6 +68,7 @@ struct fw_priv {
void *data;
size_t size;
size_t allocated_size;
+ u32 opt_flags;
#ifdef CONFIG_FW_LOADER_PAGED_BUF
bool is_paged_buf;
struct page **pages;
@@ -136,7 +137,7 @@ static inline void fw_state_done(struct fw_priv *fw_priv)
__fw_state_set(fw_priv, FW_STATUS_DONE);
}

-int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags);
+int assign_fw(struct firmware *fw, struct device *device);

#ifdef CONFIG_FW_LOADER_PAGED_BUF
void fw_free_paged_buf(struct fw_priv *fw_priv);
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index d95249b5284e..814a18cc51bd 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -168,7 +168,9 @@ static int fw_cache_piggyback_on_request(const char *name);

static struct fw_priv *__allocate_fw_priv(const char *fw_name,
struct firmware_cache *fwc,
- void *dbuf, size_t size)
+ void *dbuf,
+ size_t size,
+ u32 opt_flags)
{
struct fw_priv *fw_priv;

@@ -186,6 +188,7 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
fw_priv->fwc = fwc;
fw_priv->data = dbuf;
fw_priv->allocated_size = size;
+ fw_priv->opt_flags = opt_flags;
fw_state_init(fw_priv);
#ifdef CONFIG_FW_LOADER_USER_HELPER
INIT_LIST_HEAD(&fw_priv->pending_list);
@@ -210,8 +213,10 @@ static struct fw_priv *__lookup_fw_priv(const char *fw_name)
/* Returns 1 for batching firmware requests with the same name */
static int alloc_lookup_fw_priv(const char *fw_name,
struct firmware_cache *fwc,
- struct fw_priv **fw_priv, void *dbuf,
- size_t size, u32 opt_flags)
+ struct fw_priv **fw_priv,
+ void *dbuf,
+ size_t size,
+ u32 opt_flags)
{
struct fw_priv *tmp;

@@ -227,7 +232,7 @@ static int alloc_lookup_fw_priv(const char *fw_name,
}
}

- tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size);
+ tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, opt_flags);
if (tmp) {
INIT_LIST_HEAD(&tmp->list);
if (!(opt_flags & FW_OPT_NOCACHE))
@@ -635,7 +640,7 @@ static int fw_add_devm_name(struct device *dev, const char *name)
}
#endif

-int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
+int assign_fw(struct firmware *fw, struct device *device)
{
struct fw_priv *fw_priv = fw->priv;
int ret;
@@ -654,8 +659,8 @@ int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
* should be fixed in devres or driver core.
*/
/* don't cache firmware handled without uevent */
- if (device && (opt_flags & FW_OPT_UEVENT) &&
- !(opt_flags & FW_OPT_NOCACHE)) {
+ if (device && (fw_priv->opt_flags & FW_OPT_UEVENT) &&
+ !(fw_priv->opt_flags & FW_OPT_NOCACHE)) {
ret = fw_add_devm_name(device, fw_priv->fw_name);
if (ret) {
mutex_unlock(&fw_lock);
@@ -667,7 +672,7 @@ int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
* After caching firmware image is started, let it piggyback
* on request firmware.
*/
- if (!(opt_flags & FW_OPT_NOCACHE) &&
+ if (!(fw_priv->opt_flags & FW_OPT_NOCACHE) &&
fw_priv->fwc->state == FW_LOADER_START_CACHE) {
if (fw_cache_piggyback_on_request(fw_priv->fw_name))
kref_get(&fw_priv->ref);
@@ -778,7 +783,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
#endif

if (ret == -ENOENT)
- ret = firmware_fallback_platform(fw->priv, opt_flags);
+ ret = firmware_fallback_platform(fw->priv);

if (ret) {
if (!(opt_flags & FW_OPT_NO_WARN))
@@ -787,7 +792,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
name, ret);
ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret);
} else
- ret = assign_fw(fw, device, opt_flags);
+ ret = assign_fw(fw, device);

out:
if (ret < 0) {
--
2.25.1

2020-07-24 21:46:17

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 18/19] firmware: Add request_partial_firmware_into_buf()

From: Scott Branden <[email protected]>

Add request_partial_firmware_into_buf() to allow for portions of a
firmware file to be read into a buffer. This is needed when large firmware
must be loaded in portions from a file on memory constrained systems.

Signed-off-by: Scott Branden <[email protected]>
Co-developed-by: Kees Cook <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
---
drivers/base/firmware_loader/firmware.h | 4 +
drivers/base/firmware_loader/main.c | 109 +++++++++++++++++++-----
include/linux/firmware.h | 12 +++
3 files changed, 105 insertions(+), 20 deletions(-)

diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h
index 7ad5fe52bc72..3f6eda46b3a2 100644
--- a/drivers/base/firmware_loader/firmware.h
+++ b/drivers/base/firmware_loader/firmware.h
@@ -32,6 +32,8 @@
* @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in
* the platform's main firmware. If both this fallback and the sysfs
* fallback are enabled, then this fallback will be tried first.
+ * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read
+ * entire file.
*/
enum fw_opt {
FW_OPT_UEVENT = BIT(0),
@@ -41,6 +43,7 @@ enum fw_opt {
FW_OPT_NOCACHE = BIT(4),
FW_OPT_NOFALLBACK_SYSFS = BIT(5),
FW_OPT_FALLBACK_PLATFORM = BIT(6),
+ FW_OPT_PARTIAL = BIT(7),
};

enum fw_status {
@@ -68,6 +71,7 @@ struct fw_priv {
void *data;
size_t size;
size_t allocated_size;
+ size_t offset;
u32 opt_flags;
#ifdef CONFIG_FW_LOADER_PAGED_BUF
bool is_paged_buf;
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index 814a18cc51bd..7aa22bdc2f60 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -170,10 +170,19 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
struct firmware_cache *fwc,
void *dbuf,
size_t size,
+ size_t offset,
u32 opt_flags)
{
struct fw_priv *fw_priv;

+ /* For a partial read, the buffer must be preallocated. */
+ if ((opt_flags & FW_OPT_PARTIAL) && !dbuf)
+ return NULL;
+
+ /* Only partial reads are allowed to use an offset. */
+ if (offset != 0 && !(opt_flags & FW_OPT_PARTIAL))
+ return NULL;
+
fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
if (!fw_priv)
return NULL;
@@ -188,6 +197,7 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
fw_priv->fwc = fwc;
fw_priv->data = dbuf;
fw_priv->allocated_size = size;
+ fw_priv->offset = offset;
fw_priv->opt_flags = opt_flags;
fw_state_init(fw_priv);
#ifdef CONFIG_FW_LOADER_USER_HELPER
@@ -216,12 +226,17 @@ static int alloc_lookup_fw_priv(const char *fw_name,
struct fw_priv **fw_priv,
void *dbuf,
size_t size,
+ size_t offset,
u32 opt_flags)
{
struct fw_priv *tmp;

spin_lock(&fwc->lock);
- if (!(opt_flags & FW_OPT_NOCACHE)) {
+ /*
+ * Do not merge requests that are marked to be non-cached or
+ * are performing partial reads.
+ */
+ if (!(opt_flags & (FW_OPT_NOCACHE | FW_OPT_PARTIAL))) {
tmp = __lookup_fw_priv(fw_name);
if (tmp) {
kref_get(&tmp->ref);
@@ -232,7 +247,7 @@ static int alloc_lookup_fw_priv(const char *fw_name,
}
}

- tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, opt_flags);
+ tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags);
if (tmp) {
INIT_LIST_HEAD(&tmp->list);
if (!(opt_flags & FW_OPT_NOCACHE))
@@ -439,6 +454,12 @@ static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
else
return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer);
}
+#else
+static inline int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
+ size_t in_size, const void *in_buffer)
+{
+ return -ENOENT;
+}
#endif /* CONFIG_FW_LOADER_COMPRESS */

/* direct firmware loading support */
@@ -485,6 +506,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
return -ENOMEM;

for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
+ size_t file_size = 0;
+ size_t *file_size_ptr = NULL;
+
/* skip the unset customized path */
if (!fw_path[i][0])
continue;
@@ -498,9 +522,18 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,

fw_priv->size = 0;

+ /*
+ * The total file size is only examined when doing a partial
+ * read; the "full read" case needs to fail if the whole
+ * firmware was not completely loaded.
+ */
+ if ((fw_priv->opt_flags & FW_OPT_PARTIAL) && buffer)
+ file_size_ptr = &file_size;
+
/* load firmware files from the mount namespace of init */
- rc = kernel_read_file_from_path_initns(path, 0, &buffer, msize,
- NULL,
+ rc = kernel_read_file_from_path_initns(path, fw_priv->offset,
+ &buffer, msize,
+ file_size_ptr,
READING_FIRMWARE);
if (rc < 0) {
if (rc != -ENOENT)
@@ -691,7 +724,7 @@ int assign_fw(struct firmware *fw, struct device *device)
static int
_request_firmware_prepare(struct firmware **firmware_p, const char *name,
struct device *device, void *dbuf, size_t size,
- u32 opt_flags)
+ size_t offset, u32 opt_flags)
{
struct firmware *firmware;
struct fw_priv *fw_priv;
@@ -710,7 +743,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
}

ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size,
- opt_flags);
+ offset, opt_flags);

/*
* bind with 'priv' now to avoid warning in failure path
@@ -757,9 +790,10 @@ static void fw_abort_batch_reqs(struct firmware *fw)
static int
_request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device, void *buf, size_t size,
- u32 opt_flags)
+ size_t offset, u32 opt_flags)
{
struct firmware *fw = NULL;
+ bool nondirect = false;
int ret;

if (!firmware_p)
@@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
}

ret = _request_firmware_prepare(&fw, name, device, buf, size,
- opt_flags);
+ offset, opt_flags);
if (ret <= 0) /* error or already assigned */
goto out;

ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
-#ifdef CONFIG_FW_LOADER_COMPRESS
- if (ret == -ENOENT)
+
+ /* Only full reads can support decompression, platform, and sysfs. */
+ if (!(opt_flags & FW_OPT_PARTIAL))
+ nondirect = true;
+
+ if (ret == -ENOENT && nondirect)
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
fw_decompress_xz);
-#endif
-
- if (ret == -ENOENT)
+ if (ret == -ENOENT && nondirect)
ret = firmware_fallback_platform(fw->priv);

if (ret) {
@@ -790,7 +826,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
dev_warn(device,
"Direct firmware load for %s failed with error %d\n",
name, ret);
- ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret);
+ if (nondirect)
+ ret = firmware_fallback_sysfs(fw, name, device,
+ opt_flags, ret);
} else
ret = assign_fw(fw, device);

@@ -833,7 +871,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,

/* Need to pin this module until return */
__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, NULL, 0,
+ ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
FW_OPT_UEVENT);
module_put(THIS_MODULE);
return ret;
@@ -860,7 +898,7 @@ int firmware_request_nowarn(const struct firmware **firmware, const char *name,

/* Need to pin this module until return */
__module_get(THIS_MODULE);
- ret = _request_firmware(firmware, name, device, NULL, 0,
+ ret = _request_firmware(firmware, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_NO_WARN);
module_put(THIS_MODULE);
return ret;
@@ -884,7 +922,7 @@ int request_firmware_direct(const struct firmware **firmware_p,
int ret;

__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, NULL, 0,
+ ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_NO_WARN |
FW_OPT_NOFALLBACK_SYSFS);
module_put(THIS_MODULE);
@@ -909,7 +947,7 @@ int firmware_request_platform(const struct firmware **firmware,

/* Need to pin this module until return */
__module_get(THIS_MODULE);
- ret = _request_firmware(firmware, name, device, NULL, 0,
+ ret = _request_firmware(firmware, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM);
module_put(THIS_MODULE);
return ret;
@@ -965,13 +1003,44 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
return -EOPNOTSUPP;

__module_get(THIS_MODULE);
- ret = _request_firmware(firmware_p, name, device, buf, size,
+ ret = _request_firmware(firmware_p, name, device, buf, size, 0,
FW_OPT_UEVENT | FW_OPT_NOCACHE);
module_put(THIS_MODULE);
return ret;
}
EXPORT_SYMBOL(request_firmware_into_buf);

+/**
+ * request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded and DMA region allocated
+ * @buf: address of buffer to load firmware into
+ * @size: size of buffer
+ * @offset: offset into file to read
+ *
+ * This function works pretty much like request_firmware_into_buf except
+ * it allows a partial read of the file.
+ */
+int
+request_partial_firmware_into_buf(const struct firmware **firmware_p,
+ const char *name, struct device *device,
+ void *buf, size_t size, size_t offset)
+{
+ int ret;
+
+ if (fw_cache_is_setup(device, name))
+ return -EOPNOTSUPP;
+
+ __module_get(THIS_MODULE);
+ ret = _request_firmware(firmware_p, name, device, buf, size, offset,
+ FW_OPT_UEVENT | FW_OPT_NOCACHE |
+ FW_OPT_PARTIAL);
+ module_put(THIS_MODULE);
+ return ret;
+}
+EXPORT_SYMBOL(request_partial_firmware_into_buf);
+
/**
* release_firmware() - release the resource associated with a firmware image
* @fw: firmware resource to release
@@ -1004,7 +1073,7 @@ static void request_firmware_work_func(struct work_struct *work)

fw_work = container_of(work, struct firmware_work, work);

- _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
+ _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
fw_work->opt_flags);
fw_work->cont(fw, fw_work->context);
put_device(fw_work->device); /* taken in request_firmware_nowait() */
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index cb3e2c06ed8a..c15acadc6cf4 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -53,6 +53,9 @@ int request_firmware_direct(const struct firmware **fw, const char *name,
struct device *device);
int request_firmware_into_buf(const struct firmware **firmware_p,
const char *name, struct device *device, void *buf, size_t size);
+int request_partial_firmware_into_buf(const struct firmware **firmware_p,
+ const char *name, struct device *device,
+ void *buf, size_t size, size_t offset);

void release_firmware(const struct firmware *fw);
#else
@@ -102,6 +105,15 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p,
return -EINVAL;
}

+static inline int request_partial_firmware_into_buf
+ (const struct firmware **firmware_p,
+ const char *name,
+ struct device *device,
+ void *buf, size_t size, size_t offset)
+{
+ return -EINVAL;
+}
+
#endif

int firmware_request_cache(struct device *device, const char *name);
--
2.25.1

2020-07-25 05:17:18

by Scott Branden

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

On 2020-07-24 2:36 p.m., Kees Cook wrote:
> v3:
> - add reviews/acks
> - add "IMA: Add support for file reads without contents" patch
> - trim CC list, in case that's why vger ignored v2
> v2: [missing from lkml archives! (CC list too long?) repeating changes here]
> - fix issues in firmware test suite
> - add firmware partial read patches
> - various bug fixes/cleanups
> v1: https://lore.kernel.org/lkml/[email protected]/
>
> Hi,
>
> Here's my tree for adding partial read support in kernel_read_file(),
> which fixes a number of issues along the way. It's got Scott's firmware
> and IMA patches ported and everything tests cleanly for me (even with
> CONFIG_IMA_APPRAISE=y).
>
> I think the intention is for this to go via Greg's tree since Scott's
> driver code will depend on it?
v3 of this patch series looks good and passes all of my tests.
Remaining patches
Acked-by: Scott Branden <[email protected]>

I have added latest bcm-vk driver code to Kees' patch series and added
it here:
https://github.com/sbranden/linux/tree/kernel_read_file_for_kees_v3

If everyone finds Kees' patch series acceptable then the 3 patches
adding the bcm-vk driver
need to be added to the series.  I can send the 3 patches out separately
and then
the two patch series can be combined in Greg or someone's tree if that
works?
Or if an in-kernel user beyond kernel selftest is needed for
request_partial_firmware_into_buf
in Kees' patch series then another PATCH v4 needs to be sent out
including the bcm-vk driver.
>
> Thanks,
>
> -Kees
>
>
> Kees Cook (15):

Thanks for help Kees, it's works now.
> test_firmware: Test platform fw loading on non-EFI systems
> selftest/firmware: Add selftest timeout in settings
> firmware_loader: EFI firmware loader must handle pre-allocated buffer
> fs/kernel_read_file: Remove FIRMWARE_PREALLOC_BUFFER enum
> fs/kernel_read_file: Remove FIRMWARE_EFI_EMBEDDED enum
> fs/kernel_read_file: Split into separate source file
> fs/kernel_read_file: Remove redundant size argument
> fs/kernel_read_file: Switch buffer size arg to size_t
> fs/kernel_read_file: Add file_size output argument
> LSM: Introduce kernel_post_load_data() hook
> firmware_loader: Use security_post_load_data()
> module: Call security_kernel_post_load_data()
> LSM: Add "contents" flag to kernel_read_file hook
> fs/kernel_file_read: Add "offset" arg for partial reads
> firmware: Store opt_flags in fw_priv
>
> Scott Branden (4):
> fs/kernel_read_file: Split into separate include file
> IMA: Add support for file reads without contents
> firmware: Add request_partial_firmware_into_buf()
> test_firmware: Test partial read support
>
> drivers/base/firmware_loader/fallback.c | 19 +-
> drivers/base/firmware_loader/fallback.h | 5 +-
> .../base/firmware_loader/fallback_platform.c | 16 +-
> drivers/base/firmware_loader/firmware.h | 7 +-
> drivers/base/firmware_loader/main.c | 143 ++++++++++---
> drivers/firmware/efi/embedded-firmware.c | 21 +-
> drivers/firmware/efi/embedded-firmware.h | 19 ++
> fs/Makefile | 3 +-
> fs/exec.c | 132 +-----------
> fs/kernel_read_file.c | 189 ++++++++++++++++++
> include/linux/efi_embedded_fw.h | 13 --
> include/linux/firmware.h | 12 ++
> include/linux/fs.h | 39 ----
> include/linux/ima.h | 19 +-
> include/linux/kernel_read_file.h | 55 +++++
> include/linux/lsm_hook_defs.h | 6 +-
> include/linux/lsm_hooks.h | 12 ++
> include/linux/security.h | 19 +-
> kernel/kexec.c | 2 +-
> kernel/kexec_file.c | 19 +-
> kernel/module.c | 24 ++-
> lib/test_firmware.c | 159 +++++++++++++--
> security/integrity/digsig.c | 8 +-
> security/integrity/ima/ima_fs.c | 10 +-
> security/integrity/ima/ima_main.c | 70 +++++--
> security/integrity/ima/ima_policy.c | 1 +
> security/loadpin/loadpin.c | 17 +-
> security/security.c | 26 ++-
> security/selinux/hooks.c | 8 +-
> .../selftests/firmware/fw_filesystem.sh | 91 +++++++++
> tools/testing/selftests/firmware/settings | 8 +
> tools/testing/selftests/kselftest/runner.sh | 6 +-
> 32 files changed, 860 insertions(+), 318 deletions(-)
> create mode 100644 drivers/firmware/efi/embedded-firmware.h
> create mode 100644 fs/kernel_read_file.c
> create mode 100644 include/linux/kernel_read_file.h
> create mode 100644 tools/testing/selftests/firmware/settings
>

2020-07-25 10:08:11

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

On Fri, Jul 24, 2020 at 02:36:21PM -0700, Kees Cook wrote:
> v3:
> - add reviews/acks
> - add "IMA: Add support for file reads without contents" patch
> - trim CC list, in case that's why vger ignored v2
> v2: [missing from lkml archives! (CC list too long?) repeating changes here]
> - fix issues in firmware test suite
> - add firmware partial read patches
> - various bug fixes/cleanups
> v1: https://lore.kernel.org/lkml/[email protected]/
>
> Hi,
>
> Here's my tree for adding partial read support in kernel_read_file(),
> which fixes a number of issues along the way. It's got Scott's firmware
> and IMA patches ported and everything tests cleanly for me (even with
> CONFIG_IMA_APPRAISE=y).
>
> I think the intention is for this to go via Greg's tree since Scott's
> driver code will depend on it?

I've applied the first 3 now, as I think I need some acks/reviewed-by
from the subsystem owners of the other patches before I can take them.

thanks,

greg k-h

2020-07-25 10:13:52

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v3 03/19] firmware_loader: EFI firmware loader must handle pre-allocated buffer

On Fri, Jul 24, 2020 at 02:36:24PM -0700, Kees Cook wrote:
> The EFI platform firmware fallback would clobber any pre-allocated
> buffers. Instead, correctly refuse to reallocate when too small (as
> already done in the sysfs fallback), or perform allocation normally
> when needed.
>
> Fixes: e4c2c0ff00ec ("firmware: Add new platform fallback mechanism and firm ware_request_platform()")

"firmware_request_platform()" :)

2020-07-25 15:51:07

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

On Sat, Jul 25, 2020 at 12:05:55PM +0200, Greg Kroah-Hartman wrote:
> On Fri, Jul 24, 2020 at 02:36:21PM -0700, Kees Cook wrote:
> > v3:
> > - add reviews/acks
> > - add "IMA: Add support for file reads without contents" patch
> > - trim CC list, in case that's why vger ignored v2
> > v2: [missing from lkml archives! (CC list too long?) repeating changes here]
> > - fix issues in firmware test suite
> > - add firmware partial read patches
> > - various bug fixes/cleanups
> > v1: https://lore.kernel.org/lkml/[email protected]/
> >
> > Hi,
> >
> > Here's my tree for adding partial read support in kernel_read_file(),
> > which fixes a number of issues along the way. It's got Scott's firmware
> > and IMA patches ported and everything tests cleanly for me (even with
> > CONFIG_IMA_APPRAISE=y).
> >
> > I think the intention is for this to go via Greg's tree since Scott's
> > driver code will depend on it?
>
> I've applied the first 3 now, as I think I need some acks/reviewed-by
> from the subsystem owners of the other patches before I can take them.

Sounds good; thanks!

(I would argue 4 and 5 are also bug fixes, 6 is already Acked by hch and
you, and 7 is a logical follow-up to 6, but I get your point.)

James, Luis, Mimi, and Jessica, the bulk of these patches are LSM,
firmware, IMA, and modules. How does this all look to you? And KP,
you'd mentioned privately that you were interested in being able to
use the new kernel_post_load_data LSM hook for better visibility into
non-file-backed blob loading.

Thanks!

-Kees

--
Kees Cook

2020-07-25 15:51:22

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v3 03/19] firmware_loader: EFI firmware loader must handle pre-allocated buffer

On Sat, Jul 25, 2020 at 12:07:00PM +0200, Greg Kroah-Hartman wrote:
> On Fri, Jul 24, 2020 at 02:36:24PM -0700, Kees Cook wrote:
> > The EFI platform firmware fallback would clobber any pre-allocated
> > buffers. Instead, correctly refuse to reallocate when too small (as
> > already done in the sysfs fallback), or perform allocation normally
> > when needed.
> >
> > Fixes: e4c2c0ff00ec ("firmware: Add new platform fallback mechanism and firm ware_request_platform()")
>
> "firmware_request_platform()" :)

Weird... I'm not sure where that mangling happened. Perhaps a bad
cut/paste at 80 columns? Hmpf; thanks for catching. I've updated it on
my end (I assume you fixed this manually, though?)

Thanks!

--
Kees Cook

2020-07-25 17:22:49

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v3 03/19] firmware_loader: EFI firmware loader must handle pre-allocated buffer

On Sat, Jul 25, 2020 at 08:50:33AM -0700, Kees Cook wrote:
> On Sat, Jul 25, 2020 at 12:07:00PM +0200, Greg Kroah-Hartman wrote:
> > On Fri, Jul 24, 2020 at 02:36:24PM -0700, Kees Cook wrote:
> > > The EFI platform firmware fallback would clobber any pre-allocated
> > > buffers. Instead, correctly refuse to reallocate when too small (as
> > > already done in the sysfs fallback), or perform allocation normally
> > > when needed.
> > >
> > > Fixes: e4c2c0ff00ec ("firmware: Add new platform fallback mechanism and firm ware_request_platform()")
> >
> > "firmware_request_platform()" :)
>
> Weird... I'm not sure where that mangling happened. Perhaps a bad
> cut/paste at 80 columns? Hmpf; thanks for catching. I've updated it on
> my end (I assume you fixed this manually, though?)

Yes, I fixed it up already, no worries.

greg k-h

2020-07-27 11:17:00

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
> v3:
> - add reviews/acks
> - add "IMA: Add support for file reads without contents" patch
> - trim CC list, in case that's why vger ignored v2
> v2: [missing from lkml archives! (CC list too long?) repeating changes here]
> - fix issues in firmware test suite
> - add firmware partial read patches
> - various bug fixes/cleanups
> v1: https://lore.kernel.org/lkml/[email protected]/
>
> Hi,
>
> Here's my tree for adding partial read support in kernel_read_file(),
> which fixes a number of issues along the way. It's got Scott's firmware
> and IMA patches ported and everything tests cleanly for me (even with
> CONFIG_IMA_APPRAISE=y).

Thanks, Kees.  Other than my comments on the new
security_kernel_post_load_data() hook, the patch set is really nice.

In addition to compiling with CONFIG_IMA_APPRAISE enabled, have you
booted the kernel with the ima_policy=tcb?  The tcb policy will add
measurements to the IMA measurement list and extend the TPM with the
file or buffer data digest.  Are you seeing the firmware measurements,
in particular the partial read measurement?

thanks,

Mimi

2020-07-27 13:49:42

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v3 15/19] IMA: Add support for file reads without contents

On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
> From: Scott Branden <[email protected]>
>
> When the kernel_read_file LSM hook is called with contents=false, IMA
> can appraise the file directly, without requiring a filled buffer. When
> such a buffer is available, though, IMA can continue to use it instead
> of forcing a double read here.
>
> Signed-off-by: Scott Branden <[email protected]>
> Link: https://lore.kernel.org/lkml/[email protected]/
> Signed-off-by: Kees Cook <[email protected]>

After adjusting the comment below.

Reviewed-by: Mimi Zohar <[email protected]>

> ---
> security/integrity/ima/ima_main.c | 22 ++++++++++++++++------
> 1 file changed, 16 insertions(+), 6 deletions(-)
>
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index dc4f90660aa6..459e50526a12 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -613,11 +613,8 @@ void ima_post_path_mknod(struct dentry *dentry)
> int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
> bool contents)
> {
> - /* Reject all partial reads during appraisal. */
> - if (!contents) {
> - if (ima_appraise & IMA_APPRAISE_ENFORCE)
> - return -EACCES;
> - }
> + enum ima_hooks func;
> + u32 secid;
>
> /*
> * Do devices using pre-allocated memory run the risk of the
> @@ -626,7 +623,20 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
> * buffers? It may be desirable to include the buffer address
> * in this API and walk all the dma_map_single() mappings to check.
> */
> - return 0;
> +
> + /*
> + * There will be a call made to ima_post_read_file() with
> + * a filled buffer, so we don't need to perform an extra
> + * read early here.
> + */
> + if (contents)
> + return 0;
> +
> + /* Read entire file for all partial reads during appraisal. */

In addition to verifying the file signature, the file might be
included in the IMA measurement list or the file hash may be used to
augment the audit record.  Please remove "during appraisal" from the
comment.

> + func = read_idmap[read_id] ?: FILE_CHECK;
> + security_task_getsecid(current, &secid);
> + return process_measurement(file, current_cred(), secid, NULL,
> + 0, MAY_READ, func);
> }
>
> const int read_idmap[READING_MAX_ID] = {

2020-07-27 19:35:49

by Scott Branden

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

Hi Mimi/Kees,

On 2020-07-27 4:16 a.m., Mimi Zohar wrote:
> On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
>> v3:
>> - add reviews/acks
>> - add "IMA: Add support for file reads without contents" patch
>> - trim CC list, in case that's why vger ignored v2
>> v2: [missing from lkml archives! (CC list too long?) repeating changes here]
>> - fix issues in firmware test suite
>> - add firmware partial read patches
>> - various bug fixes/cleanups
>> v1: https://lore.kernel.org/lkml/[email protected]/
>>
>> Hi,
>>
>> Here's my tree for adding partial read support in kernel_read_file(),
>> which fixes a number of issues along the way. It's got Scott's firmware
>> and IMA patches ported and everything tests cleanly for me (even with
>> CONFIG_IMA_APPRAISE=y).
> Thanks, Kees.  Other than my comments on the new
> security_kernel_post_load_data() hook, the patch set is really nice.
>
> In addition to compiling with CONFIG_IMA_APPRAISE enabled, have you
> booted the kernel with the ima_policy=tcb?  The tcb policy will add
> measurements to the IMA measurement list and extend the TPM with the
> file or buffer data digest.  Are you seeing the firmware measurements,
> in particular the partial read measurement?
I booted the kernel with ima_policy=tcb.

Unfortunately after enabling the following, fw_run_tests.sh does not run.

mkdir /sys/kernel/security
mount -t securityfs securityfs /sys/kernel/security
echo "measure func=FIRMWARE_CHECK" > /sys/kernel/security/ima/policy
echo "appraise func=FIRMWARE_CHECK appraise_type=imasig" > /sys/kernel/security/ima/policy
./fw_run_tests.sh

[ 1296.258052] test_firmware: loading 'test-firmware.bin'
[ 1296.263903] misc test_firmware: loading /lib/firmware/test-firmware.bin failed with error -13
[ 1296.263905] audit: type=1800 audit(1595905754.266:9): pid=5696 uid=0 auid=4294967295 ses=4294967295 subj=kernel op=appraise_data cause=IMA-signature-required comm="fw_namespace" name="/lib/firmware/test-firmware.bin" dev="tmpfs" ino=4592 res=0
[ 1296.297085] misc test_firmware: Direct firmware load for test-firmware.bin failed with error -13
[ 1296.305947] test_firmware: load of 'test-firmware.bin' failed: -13

>
> thanks,
>
> Mimi

2020-07-28 20:04:21

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

On Mon, 2020-07-27 at 12:18 -0700, Scott Branden wrote:
> Hi Mimi/Kees,
>
> On 2020-07-27 4:16 a.m., Mimi Zohar wrote:
> > On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
> >> v3:
> >> - add reviews/acks
> >> - add "IMA: Add support for file reads without contents" patch
> >> - trim CC list, in case that's why vger ignored v2
> >> v2: [missing from lkml archives! (CC list too long?) repeating changes
> here]
> >> - fix issues in firmware test suite
> >> - add firmware partial read patches
> >> - various bug fixes/cleanups
> >> v1:
> https://lore.kernel.org/lkml/[email protected]/
> >>
> >> Hi,
> >>
> >> Here's my tree for adding partial read support in kernel_read_file(),
> >> which fixes a number of issues along the way. It's got Scott's firmware
> >> and IMA patches ported and everything tests cleanly for me (even with
> >> CONFIG_IMA_APPRAISE=y).
> > Thanks, Kees. Other than my comments on the new
> > security_kernel_post_load_data() hook, the patch set is really nice.
> >
> > In addition to compiling with CONFIG_IMA_APPRAISE enabled, have you
> > booted the kernel with the ima_policy=tcb? The tcb policy will add
> > measurements to the IMA measurement list and extend the TPM with the
> > file or buffer data digest. Are you seeing the firmware measurements,
> > in particular the partial read measurement?
> I booted the kernel with ima_policy=tcb.
>
> Unfortunately after enabling the following, fw_run_tests.sh does not run.
>
> mkdir /sys/kernel/security
> mount -t securityfs securityfs /sys/kernel/security
> echo "measure func=FIRMWARE_CHECK" > /sys/kernel/security/ima/policy
> echo "appraise func=FIRMWARE_CHECK appraise_type=imasig" >
> /sys/kernel/security/ima/policy
> ./fw_run_tests.sh
>
> [ 1296.258052] test_firmware: loading 'test-firmware.bin'
> [ 1296.263903] misc test_firmware: loading /lib/firmware/test-firmware.bin
> failed with error -13
> [ 1296.263905] audit: type=1800 audit(1595905754.266:9): pid=5696 uid=0
> auid=4294967295 ses=4294967295 subj=kernel op=appraise_data cause=IMA-
> signature-required comm="fw_namespace" name="/lib/firmware/test-firmware.bin"
> dev="tmpfs" ino=4592 res=0
> [ 1296.297085] misc test_firmware: Direct firmware load for test-firmware.bin
> failed with error -13
> [ 1296.305947] test_firmware: load of 'test-firmware.bin' failed: -13

The "appraise" rule verifies the IMA signature. Unless you signed the firmware
(evmctl) and load the public key on the IMA keyring, that's to be expected. I
assume you are seeing firmware measurements in the IMA measuremenet log.

Mimi

2020-07-28 20:04:54

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v3 15/19] IMA: Add support for file reads without contents

On Mon, Jul 27, 2020 at 09:23:34AM -0400, Mimi Zohar wrote:
> On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
> > From: Scott Branden <[email protected]>
> >
> > When the kernel_read_file LSM hook is called with contents=false, IMA
> > can appraise the file directly, without requiring a filled buffer. When
> > such a buffer is available, though, IMA can continue to use it instead
> > of forcing a double read here.
> >
> > Signed-off-by: Scott Branden <[email protected]>
> > Link: https://lore.kernel.org/lkml/[email protected]/
> > Signed-off-by: Kees Cook <[email protected]>
>
> After adjusting the comment below.
>
> Reviewed-by: Mimi Zohar <[email protected]>

Sure!

Greg, shall I send a v4 with added Reviews and the comment change or is
that minor enough that you're able to do it?

Thanks for the reviews Mimi!

-Kees

>
> > ---
> > security/integrity/ima/ima_main.c | 22 ++++++++++++++++------
> > 1 file changed, 16 insertions(+), 6 deletions(-)
> >
> > diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> > index dc4f90660aa6..459e50526a12 100644
> > --- a/security/integrity/ima/ima_main.c
> > +++ b/security/integrity/ima/ima_main.c
> > @@ -613,11 +613,8 @@ void ima_post_path_mknod(struct dentry *dentry)
> > int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
> > bool contents)
> > {
> > - /* Reject all partial reads during appraisal. */
> > - if (!contents) {
> > - if (ima_appraise & IMA_APPRAISE_ENFORCE)
> > - return -EACCES;
> > - }
> > + enum ima_hooks func;
> > + u32 secid;
> >
> > /*
> > * Do devices using pre-allocated memory run the risk of the
> > @@ -626,7 +623,20 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
> > * buffers? It may be desirable to include the buffer address
> > * in this API and walk all the dma_map_single() mappings to check.
> > */
> > - return 0;
> > +
> > + /*
> > + * There will be a call made to ima_post_read_file() with
> > + * a filled buffer, so we don't need to perform an extra
> > + * read early here.
> > + */
> > + if (contents)
> > + return 0;
> > +
> > + /* Read entire file for all partial reads during appraisal. */
>
> In addition to verifying the file signature, the file might be
> included in the IMA measurement list or the file hash may be used to
> augment the audit record. ?Please remove "during appraisal" from the
> comment.
>
> > + func = read_idmap[read_id] ?: FILE_CHECK;
> > + security_task_getsecid(current, &secid);
> > + return process_measurement(file, current_cred(), secid, NULL,
> > + 0, MAY_READ, func);
> > }
> >
> > const int read_idmap[READING_MAX_ID] = {
>

--
Kees Cook

2020-07-28 20:05:26

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v3 15/19] IMA: Add support for file reads without contents

On Tue, Jul 28, 2020 at 12:44:50PM -0700, Kees Cook wrote:
> On Mon, Jul 27, 2020 at 09:23:34AM -0400, Mimi Zohar wrote:
> > On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
> > > From: Scott Branden <[email protected]>
> > >
> > > When the kernel_read_file LSM hook is called with contents=false, IMA
> > > can appraise the file directly, without requiring a filled buffer. When
> > > such a buffer is available, though, IMA can continue to use it instead
> > > of forcing a double read here.
> > >
> > > Signed-off-by: Scott Branden <[email protected]>
> > > Link: https://lore.kernel.org/lkml/[email protected]/
> > > Signed-off-by: Kees Cook <[email protected]>
> >
> > After adjusting the comment below.
> >
> > Reviewed-by: Mimi Zohar <[email protected]>
>
> Sure!
>
> Greg, shall I send a v4 with added Reviews and the comment change or is
> that minor enough that you're able to do it?

v4 is needed, as this series is a mess of reviewes and you will have to
redo at least one patch and drop some others, right?

thanks,

greg k-h

2020-07-28 20:05:32

by Scott Branden

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

Hi Mimi,

On 2020-07-28 11:48 a.m., Mimi Zohar wrote:
> On Mon, 2020-07-27 at 12:18 -0700, Scott Branden wrote:
>> Hi Mimi/Kees,
>>
>> On 2020-07-27 4:16 a.m., Mimi Zohar wrote:
>>> On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
>>>> v3:
>>>> - add reviews/acks
>>>> - add "IMA: Add support for file reads without contents" patch
>>>> - trim CC list, in case that's why vger ignored v2
>>>> v2: [missing from lkml archives! (CC list too long?) repeating changes
>> here]
>>>> - fix issues in firmware test suite
>>>> - add firmware partial read patches
>>>> - various bug fixes/cleanups
>>>> v1:
>> https://lore.kernel.org/lkml/[email protected]/
>>>> Hi,
>>>>
>>>> Here's my tree for adding partial read support in kernel_read_file(),
>>>> which fixes a number of issues along the way. It's got Scott's firmware
>>>> and IMA patches ported and everything tests cleanly for me (even with
>>>> CONFIG_IMA_APPRAISE=y).
>>> Thanks, Kees. Other than my comments on the new
>>> security_kernel_post_load_data() hook, the patch set is really nice.
>>>
>>> In addition to compiling with CONFIG_IMA_APPRAISE enabled, have you
>>> booted the kernel with the ima_policy=tcb? The tcb policy will add
>>> measurements to the IMA measurement list and extend the TPM with the
>>> file or buffer data digest. Are you seeing the firmware measurements,
>>> in particular the partial read measurement?
>> I booted the kernel with ima_policy=tcb.
>>
>> Unfortunately after enabling the following, fw_run_tests.sh does not run.
>>
>> mkdir /sys/kernel/security
>> mount -t securityfs securityfs /sys/kernel/security
>> echo "measure func=FIRMWARE_CHECK" > /sys/kernel/security/ima/policy
>> echo "appraise func=FIRMWARE_CHECK appraise_type=imasig" >
>> /sys/kernel/security/ima/policy
>> ./fw_run_tests.sh
>>
>> [ 1296.258052] test_firmware: loading 'test-firmware.bin'
>> [ 1296.263903] misc test_firmware: loading /lib/firmware/test-firmware.bin
>> failed with error -13
>> [ 1296.263905] audit: type=1800 audit(1595905754.266:9): pid=5696 uid=0
>> auid=4294967295 ses=4294967295 subj=kernel op=appraise_data cause=IMA-
>> signature-required comm="fw_namespace" name="/lib/firmware/test-firmware.bin"
>> dev="tmpfs" ino=4592 res=0
>> [ 1296.297085] misc test_firmware: Direct firmware load for test-firmware.bin
>> failed with error -13
>> [ 1296.305947] test_firmware: load of 'test-firmware.bin' failed: -13
> The "appraise" rule verifies the IMA signature. Unless you signed the firmware
> (evmctl) and load the public key on the IMA keyring, that's to be expected. I
> assume you are seeing firmware measurements in the IMA measuremenet log.
Yes, I see the firmware measurements in the IMA measurement log.
I have not signed the firmware nor loaded a public key on the IMA keyring.
Therefore everything is working as expected.
>
> Mimi
>
Thanks,
 Scott

2020-07-28 20:13:01

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v3 15/19] IMA: Add support for file reads without contents

On Tue, Jul 28, 2020 at 09:56:40PM +0200, Greg Kroah-Hartman wrote:
> On Tue, Jul 28, 2020 at 12:44:50PM -0700, Kees Cook wrote:
> > On Mon, Jul 27, 2020 at 09:23:34AM -0400, Mimi Zohar wrote:
> > > On Fri, 2020-07-24 at 14:36 -0700, Kees Cook wrote:
> > > > From: Scott Branden <[email protected]>
> > > >
> > > > When the kernel_read_file LSM hook is called with contents=false, IMA
> > > > can appraise the file directly, without requiring a filled buffer. When
> > > > such a buffer is available, though, IMA can continue to use it instead
> > > > of forcing a double read here.
> > > >
> > > > Signed-off-by: Scott Branden <[email protected]>
> > > > Link: https://lore.kernel.org/lkml/[email protected]/
> > > > Signed-off-by: Kees Cook <[email protected]>
> > >
> > > After adjusting the comment below.
> > >
> > > Reviewed-by: Mimi Zohar <[email protected]>
> >
> > Sure!
> >
> > Greg, shall I send a v4 with added Reviews and the comment change or is
> > that minor enough that you're able to do it?
>
> v4 is needed, as this series is a mess of reviewes and you will have to
> redo at least one patch and drop some others, right?

Well, I wasn't sure what your desire was, given the weirdness of taking
some and reverting others. I will do a v4 based on driver-core-next.

Thanks!

--
Kees Cook

2020-07-29 01:18:18

by Luis Chamberlain

[permalink] [raw]
Subject: Re: [PATCH v3 18/19] firmware: Add request_partial_firmware_into_buf()

Long ago Takashi had some points about this strategy breaking
compressed file use. Was that considered?

Luis

On Fri, Jul 24, 2020 at 02:36:39PM -0700, Kees Cook wrote:
> From: Scott Branden <[email protected]>
>
> Add request_partial_firmware_into_buf() to allow for portions of a
> firmware file to be read into a buffer. This is needed when large firmware
> must be loaded in portions from a file on memory constrained systems.
>
> Signed-off-by: Scott Branden <[email protected]>
> Co-developed-by: Kees Cook <[email protected]>
> Signed-off-by: Kees Cook <[email protected]>
> ---
> drivers/base/firmware_loader/firmware.h | 4 +
> drivers/base/firmware_loader/main.c | 109 +++++++++++++++++++-----
> include/linux/firmware.h | 12 +++
> 3 files changed, 105 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h
> index 7ad5fe52bc72..3f6eda46b3a2 100644
> --- a/drivers/base/firmware_loader/firmware.h
> +++ b/drivers/base/firmware_loader/firmware.h
> @@ -32,6 +32,8 @@
> * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in
> * the platform's main firmware. If both this fallback and the sysfs
> * fallback are enabled, then this fallback will be tried first.
> + * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read
> + * entire file.
> */
> enum fw_opt {
> FW_OPT_UEVENT = BIT(0),
> @@ -41,6 +43,7 @@ enum fw_opt {
> FW_OPT_NOCACHE = BIT(4),
> FW_OPT_NOFALLBACK_SYSFS = BIT(5),
> FW_OPT_FALLBACK_PLATFORM = BIT(6),
> + FW_OPT_PARTIAL = BIT(7),
> };
>
> enum fw_status {
> @@ -68,6 +71,7 @@ struct fw_priv {
> void *data;
> size_t size;
> size_t allocated_size;
> + size_t offset;
> u32 opt_flags;
> #ifdef CONFIG_FW_LOADER_PAGED_BUF
> bool is_paged_buf;
> diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
> index 814a18cc51bd..7aa22bdc2f60 100644
> --- a/drivers/base/firmware_loader/main.c
> +++ b/drivers/base/firmware_loader/main.c
> @@ -170,10 +170,19 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
> struct firmware_cache *fwc,
> void *dbuf,
> size_t size,
> + size_t offset,
> u32 opt_flags)
> {
> struct fw_priv *fw_priv;
>
> + /* For a partial read, the buffer must be preallocated. */
> + if ((opt_flags & FW_OPT_PARTIAL) && !dbuf)
> + return NULL;
> +
> + /* Only partial reads are allowed to use an offset. */
> + if (offset != 0 && !(opt_flags & FW_OPT_PARTIAL))
> + return NULL;
> +
> fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
> if (!fw_priv)
> return NULL;
> @@ -188,6 +197,7 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
> fw_priv->fwc = fwc;
> fw_priv->data = dbuf;
> fw_priv->allocated_size = size;
> + fw_priv->offset = offset;
> fw_priv->opt_flags = opt_flags;
> fw_state_init(fw_priv);
> #ifdef CONFIG_FW_LOADER_USER_HELPER
> @@ -216,12 +226,17 @@ static int alloc_lookup_fw_priv(const char *fw_name,
> struct fw_priv **fw_priv,
> void *dbuf,
> size_t size,
> + size_t offset,
> u32 opt_flags)
> {
> struct fw_priv *tmp;
>
> spin_lock(&fwc->lock);
> - if (!(opt_flags & FW_OPT_NOCACHE)) {
> + /*
> + * Do not merge requests that are marked to be non-cached or
> + * are performing partial reads.
> + */
> + if (!(opt_flags & (FW_OPT_NOCACHE | FW_OPT_PARTIAL))) {
> tmp = __lookup_fw_priv(fw_name);
> if (tmp) {
> kref_get(&tmp->ref);
> @@ -232,7 +247,7 @@ static int alloc_lookup_fw_priv(const char *fw_name,
> }
> }
>
> - tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, opt_flags);
> + tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags);
> if (tmp) {
> INIT_LIST_HEAD(&tmp->list);
> if (!(opt_flags & FW_OPT_NOCACHE))
> @@ -439,6 +454,12 @@ static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
> else
> return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer);
> }
> +#else
> +static inline int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
> + size_t in_size, const void *in_buffer)
> +{
> + return -ENOENT;
> +}
> #endif /* CONFIG_FW_LOADER_COMPRESS */
>
> /* direct firmware loading support */
> @@ -485,6 +506,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
> return -ENOMEM;
>
> for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
> + size_t file_size = 0;
> + size_t *file_size_ptr = NULL;
> +
> /* skip the unset customized path */
> if (!fw_path[i][0])
> continue;
> @@ -498,9 +522,18 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
>
> fw_priv->size = 0;
>
> + /*
> + * The total file size is only examined when doing a partial
> + * read; the "full read" case needs to fail if the whole
> + * firmware was not completely loaded.
> + */
> + if ((fw_priv->opt_flags & FW_OPT_PARTIAL) && buffer)
> + file_size_ptr = &file_size;
> +
> /* load firmware files from the mount namespace of init */
> - rc = kernel_read_file_from_path_initns(path, 0, &buffer, msize,
> - NULL,
> + rc = kernel_read_file_from_path_initns(path, fw_priv->offset,
> + &buffer, msize,
> + file_size_ptr,
> READING_FIRMWARE);
> if (rc < 0) {
> if (rc != -ENOENT)
> @@ -691,7 +724,7 @@ int assign_fw(struct firmware *fw, struct device *device)
> static int
> _request_firmware_prepare(struct firmware **firmware_p, const char *name,
> struct device *device, void *dbuf, size_t size,
> - u32 opt_flags)
> + size_t offset, u32 opt_flags)
> {
> struct firmware *firmware;
> struct fw_priv *fw_priv;
> @@ -710,7 +743,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
> }
>
> ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size,
> - opt_flags);
> + offset, opt_flags);
>
> /*
> * bind with 'priv' now to avoid warning in failure path
> @@ -757,9 +790,10 @@ static void fw_abort_batch_reqs(struct firmware *fw)
> static int
> _request_firmware(const struct firmware **firmware_p, const char *name,
> struct device *device, void *buf, size_t size,
> - u32 opt_flags)
> + size_t offset, u32 opt_flags)
> {
> struct firmware *fw = NULL;
> + bool nondirect = false;
> int ret;
>
> if (!firmware_p)
> @@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
> }
>
> ret = _request_firmware_prepare(&fw, name, device, buf, size,
> - opt_flags);
> + offset, opt_flags);
> if (ret <= 0) /* error or already assigned */
> goto out;
>
> ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
> -#ifdef CONFIG_FW_LOADER_COMPRESS
> - if (ret == -ENOENT)
> +
> + /* Only full reads can support decompression, platform, and sysfs. */
> + if (!(opt_flags & FW_OPT_PARTIAL))
> + nondirect = true;
> +
> + if (ret == -ENOENT && nondirect)
> ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
> fw_decompress_xz);
> -#endif
> -
> - if (ret == -ENOENT)
> + if (ret == -ENOENT && nondirect)
> ret = firmware_fallback_platform(fw->priv);
>
> if (ret) {
> @@ -790,7 +826,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
> dev_warn(device,
> "Direct firmware load for %s failed with error %d\n",
> name, ret);
> - ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret);
> + if (nondirect)
> + ret = firmware_fallback_sysfs(fw, name, device,
> + opt_flags, ret);
> } else
> ret = assign_fw(fw, device);
>
> @@ -833,7 +871,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,
>
> /* Need to pin this module until return */
> __module_get(THIS_MODULE);
> - ret = _request_firmware(firmware_p, name, device, NULL, 0,
> + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
> FW_OPT_UEVENT);
> module_put(THIS_MODULE);
> return ret;
> @@ -860,7 +898,7 @@ int firmware_request_nowarn(const struct firmware **firmware, const char *name,
>
> /* Need to pin this module until return */
> __module_get(THIS_MODULE);
> - ret = _request_firmware(firmware, name, device, NULL, 0,
> + ret = _request_firmware(firmware, name, device, NULL, 0, 0,
> FW_OPT_UEVENT | FW_OPT_NO_WARN);
> module_put(THIS_MODULE);
> return ret;
> @@ -884,7 +922,7 @@ int request_firmware_direct(const struct firmware **firmware_p,
> int ret;
>
> __module_get(THIS_MODULE);
> - ret = _request_firmware(firmware_p, name, device, NULL, 0,
> + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
> FW_OPT_UEVENT | FW_OPT_NO_WARN |
> FW_OPT_NOFALLBACK_SYSFS);
> module_put(THIS_MODULE);
> @@ -909,7 +947,7 @@ int firmware_request_platform(const struct firmware **firmware,
>
> /* Need to pin this module until return */
> __module_get(THIS_MODULE);
> - ret = _request_firmware(firmware, name, device, NULL, 0,
> + ret = _request_firmware(firmware, name, device, NULL, 0, 0,
> FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM);
> module_put(THIS_MODULE);
> return ret;
> @@ -965,13 +1003,44 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
> return -EOPNOTSUPP;
>
> __module_get(THIS_MODULE);
> - ret = _request_firmware(firmware_p, name, device, buf, size,
> + ret = _request_firmware(firmware_p, name, device, buf, size, 0,
> FW_OPT_UEVENT | FW_OPT_NOCACHE);
> module_put(THIS_MODULE);
> return ret;
> }
> EXPORT_SYMBOL(request_firmware_into_buf);
>
> +/**
> + * request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer
> + * @firmware_p: pointer to firmware image
> + * @name: name of firmware file
> + * @device: device for which firmware is being loaded and DMA region allocated
> + * @buf: address of buffer to load firmware into
> + * @size: size of buffer
> + * @offset: offset into file to read
> + *
> + * This function works pretty much like request_firmware_into_buf except
> + * it allows a partial read of the file.
> + */
> +int
> +request_partial_firmware_into_buf(const struct firmware **firmware_p,
> + const char *name, struct device *device,
> + void *buf, size_t size, size_t offset)
> +{
> + int ret;
> +
> + if (fw_cache_is_setup(device, name))
> + return -EOPNOTSUPP;
> +
> + __module_get(THIS_MODULE);
> + ret = _request_firmware(firmware_p, name, device, buf, size, offset,
> + FW_OPT_UEVENT | FW_OPT_NOCACHE |
> + FW_OPT_PARTIAL);
> + module_put(THIS_MODULE);
> + return ret;
> +}
> +EXPORT_SYMBOL(request_partial_firmware_into_buf);
> +
> /**
> * release_firmware() - release the resource associated with a firmware image
> * @fw: firmware resource to release
> @@ -1004,7 +1073,7 @@ static void request_firmware_work_func(struct work_struct *work)
>
> fw_work = container_of(work, struct firmware_work, work);
>
> - _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
> + _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
> fw_work->opt_flags);
> fw_work->cont(fw, fw_work->context);
> put_device(fw_work->device); /* taken in request_firmware_nowait() */
> diff --git a/include/linux/firmware.h b/include/linux/firmware.h
> index cb3e2c06ed8a..c15acadc6cf4 100644
> --- a/include/linux/firmware.h
> +++ b/include/linux/firmware.h
> @@ -53,6 +53,9 @@ int request_firmware_direct(const struct firmware **fw, const char *name,
> struct device *device);
> int request_firmware_into_buf(const struct firmware **firmware_p,
> const char *name, struct device *device, void *buf, size_t size);
> +int request_partial_firmware_into_buf(const struct firmware **firmware_p,
> + const char *name, struct device *device,
> + void *buf, size_t size, size_t offset);
>
> void release_firmware(const struct firmware *fw);
> #else
> @@ -102,6 +105,15 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p,
> return -EINVAL;
> }
>
> +static inline int request_partial_firmware_into_buf
> + (const struct firmware **firmware_p,
> + const char *name,
> + struct device *device,
> + void *buf, size_t size, size_t offset)
> +{
> + return -EINVAL;
> +}
> +
> #endif
>
> int firmware_request_cache(struct device *device, const char *name);
> --
> 2.25.1
>

2020-07-29 01:20:13

by Luis Chamberlain

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] Introduce partial kernel_read_file() support

On Fri, Jul 24, 2020 at 02:36:21PM -0700, Kees Cook wrote:
> v3:
> - add reviews/acks
> - add "IMA: Add support for file reads without contents" patch
> - trim CC list, in case that's why vger ignored v2
> v2: [missing from lkml archives! (CC list too long?) repeating changes here]
> - fix issues in firmware test suite
> - add firmware partial read patches
> - various bug fixes/cleanups
> v1: https://lore.kernel.org/lkml/[email protected]/

For patches 1-10:

Reviewed-by: Luis Chamberlain <[email protected]>

Luis

2020-07-29 06:23:16

by Takashi Iwai

[permalink] [raw]
Subject: Re: [PATCH v3 18/19] firmware: Add request_partial_firmware_into_buf()

On Wed, 29 Jul 2020 03:17:39 +0200,
Luis Chamberlain wrote:
>
> Long ago Takashi had some points about this strategy breaking
> compressed file use. Was that considered?

As long as I read the patch, it tries to skip both the compressed and
the fallback loading when FW_OPT_PARTIAL is set, which is good.

However...

> > @@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
> > }
> >
> > ret = _request_firmware_prepare(&fw, name, device, buf, size,
> > - opt_flags);
> > + offset, opt_flags);
> > if (ret <= 0) /* error or already assigned */
> > goto out;
> >
> > ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
> > -#ifdef CONFIG_FW_LOADER_COMPRESS
> > - if (ret == -ENOENT)
> > +
> > + /* Only full reads can support decompression, platform, and sysfs. */
> > + if (!(opt_flags & FW_OPT_PARTIAL))
> > + nondirect = true;
> > +
> > + if (ret == -ENOENT && nondirect)
> > ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
> > fw_decompress_xz);
> > -#endif

... by dropping this ifdef, the fw loader would try to access *.xz
file unnecessarily even if CONFIG_FW_LOADER_COMPRESS is disabled.


thanks,

Takashi

2020-07-29 17:44:45

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v3 18/19] firmware: Add request_partial_firmware_into_buf()

On Wed, Jul 29, 2020 at 08:22:17AM +0200, Takashi Iwai wrote:
> On Wed, 29 Jul 2020 03:17:39 +0200,
> Luis Chamberlain wrote:
> >
> > Long ago Takashi had some points about this strategy breaking
> > compressed file use. Was that considered?
>
> As long as I read the patch, it tries to skip both the compressed and
> the fallback loading when FW_OPT_PARTIAL is set, which is good.
>
> However...
>
> > > @@ -771,18 +805,20 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
> > > }
> > >
> > > ret = _request_firmware_prepare(&fw, name, device, buf, size,
> > > - opt_flags);
> > > + offset, opt_flags);
> > > if (ret <= 0) /* error or already assigned */
> > > goto out;
> > >
> > > ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
> > > -#ifdef CONFIG_FW_LOADER_COMPRESS
> > > - if (ret == -ENOENT)
> > > +
> > > + /* Only full reads can support decompression, platform, and sysfs. */
> > > + if (!(opt_flags & FW_OPT_PARTIAL))
> > > + nondirect = true;
> > > +
> > > + if (ret == -ENOENT && nondirect)
> > > ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
> > > fw_decompress_xz);
> > > -#endif
>
> ... by dropping this ifdef, the fw loader would try to access *.xz
> file unnecessarily even if CONFIG_FW_LOADER_COMPRESS is disabled.

Ah, good point. I'd added the -ENOENT fw_decompress_xz, but I take your
point about the needless access. I will switch this back to an #ifdef.

--
Kees Cook