The only-root-readable /sys/module/$module/sections/$section files
did not truncate their output to the available buffer size. While most
paths into the kernfs read handlers end up using PAGE_SIZE buffers,
it's possible to get there through other paths (e.g. splice, sendfile).
Actually limit the output to the "count" passed into the read function,
and report it back correctly. *sigh*
Reported-by: kernel test robot <[email protected]>
Link: https://lore.kernel.org/lkml/20200805002015.GE23458@shao2-debian
Fixes: ed66f991bb19 ("module: Refactor section attr into bin attribute")
Cc: [email protected]
Cc: Jessica Yu <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
---
kernel/module.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/kernel/module.c b/kernel/module.c
index aa183c9ac0a2..08c46084d8cc 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1520,18 +1520,34 @@ struct module_sect_attrs {
struct module_sect_attr attrs[];
};
+#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))
static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
struct bin_attribute *battr,
char *buf, loff_t pos, size_t count)
{
struct module_sect_attr *sattr =
container_of(battr, struct module_sect_attr, battr);
+ char bounce[MODULE_SECT_READ_SIZE + 1];
+ size_t wrote;
if (pos != 0)
return -EINVAL;
- return sprintf(buf, "0x%px\n",
- kallsyms_show_value(file->f_cred) ? (void *)sattr->address : NULL);
+ /*
+ * Since we're a binary read handler, we must account for the
+ * trailing NUL byte that sprintf will write: if "buf" is
+ * too small to hold the NUL, or the NUL is exactly the last
+ * byte, the read will look like it got truncated by one byte.
+ * Since there is no way to ask sprintf nicely to not write
+ * the NUL, we have to use a bounce buffer.
+ */
+ wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n",
+ kallsyms_show_value(file->f_cred)
+ ? (void *)sattr->address : NULL);
+ count = min(count, wrote);
+ memcpy(buf, bounce, count);
+
+ return count;
}
static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
@@ -1580,7 +1596,7 @@ static void add_sect_attrs(struct module *mod, const struct load_info *info)
goto out;
sect_attrs->nsections++;
sattr->battr.read = module_sect_read;
- sattr->battr.size = 3 /* "0x", "\n" */ + (BITS_PER_LONG / 4);
+ sattr->battr.size = MODULE_SECT_READ_SIZE;
sattr->battr.attr.mode = 0400;
*(gattr++) = &(sattr++)->battr;
}
--
2.25.1
On Thu, Aug 06, 2020 at 11:35:38PM -0700, Kees Cook wrote:
> The only-root-readable /sys/module/$module/sections/$section files
> did not truncate their output to the available buffer size. While most
> paths into the kernfs read handlers end up using PAGE_SIZE buffers,
> it's possible to get there through other paths (e.g. splice, sendfile).
> Actually limit the output to the "count" passed into the read function,
> and report it back correctly. *sigh*
Ugh, never thought about that...
Reviewed-by: Greg Kroah-Hartman <[email protected]>
+++ Kees Cook [06/08/20 23:35 -0700]:
>The only-root-readable /sys/module/$module/sections/$section files
>did not truncate their output to the available buffer size. While most
>paths into the kernfs read handlers end up using PAGE_SIZE buffers,
>it's possible to get there through other paths (e.g. splice, sendfile).
>Actually limit the output to the "count" passed into the read function,
>and report it back correctly. *sigh*
>
>Reported-by: kernel test robot <[email protected]>
>Link: https://lore.kernel.org/lkml/20200805002015.GE23458@shao2-debian
>Fixes: ed66f991bb19 ("module: Refactor section attr into bin attribute")
>Cc: [email protected]
>Cc: Jessica Yu <[email protected]>
>Signed-off-by: Kees Cook <[email protected]>
Oof, thanks for fixing this!
Acked-by: Jessica Yu <[email protected]>
>---
> kernel/module.c | 22 +++++++++++++++++++---
> 1 file changed, 19 insertions(+), 3 deletions(-)
>
>diff --git a/kernel/module.c b/kernel/module.c
>index aa183c9ac0a2..08c46084d8cc 100644
>--- a/kernel/module.c
>+++ b/kernel/module.c
>@@ -1520,18 +1520,34 @@ struct module_sect_attrs {
> struct module_sect_attr attrs[];
> };
>
>+#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))
> static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
> struct bin_attribute *battr,
> char *buf, loff_t pos, size_t count)
> {
> struct module_sect_attr *sattr =
> container_of(battr, struct module_sect_attr, battr);
>+ char bounce[MODULE_SECT_READ_SIZE + 1];
>+ size_t wrote;
>
> if (pos != 0)
> return -EINVAL;
>
>- return sprintf(buf, "0x%px\n",
>- kallsyms_show_value(file->f_cred) ? (void *)sattr->address : NULL);
>+ /*
>+ * Since we're a binary read handler, we must account for the
>+ * trailing NUL byte that sprintf will write: if "buf" is
>+ * too small to hold the NUL, or the NUL is exactly the last
>+ * byte, the read will look like it got truncated by one byte.
>+ * Since there is no way to ask sprintf nicely to not write
>+ * the NUL, we have to use a bounce buffer.
>+ */
>+ wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n",
>+ kallsyms_show_value(file->f_cred)
>+ ? (void *)sattr->address : NULL);
>+ count = min(count, wrote);
>+ memcpy(buf, bounce, count);
>+
>+ return count;
> }
>
> static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
>@@ -1580,7 +1596,7 @@ static void add_sect_attrs(struct module *mod, const struct load_info *info)
> goto out;
> sect_attrs->nsections++;
> sattr->battr.read = module_sect_read;
>- sattr->battr.size = 3 /* "0x", "\n" */ + (BITS_PER_LONG / 4);
>+ sattr->battr.size = MODULE_SECT_READ_SIZE;
> sattr->battr.attr.mode = 0400;
> *(gattr++) = &(sattr++)->battr;
> }
>--
>2.25.1
>