2021-06-02 13:51:06

by Tian Tao

[permalink] [raw]
Subject: [PATCH v2 0/3] use bin_attribute to avoid buff overflow

patch #1 adds a new function cpumap_print_to_buf and patch #2 uses
this function in drivers/base/topology.c, and patch #3 uses this new
function in drivers/base/node.c.

v2:
split the original patch #1 into two patches and use kasprintf() in
patch #1 to simplify the code. do some minor formatting adjustments.

Tian Tao (3):
lib: bitmap: introduce bitmap_print_to_buf
topology: use bin_attribute to avoid buff overflow
drivers/base/node.c: use bin_attribute to avoid buff overflow

drivers/base/node.c | 52 ++++++++++++++--------
drivers/base/topology.c | 115 ++++++++++++++++++++++++++----------------------
include/linux/bitmap.h | 3 ++
include/linux/cpumask.h | 21 +++++++++
lib/bitmap.c | 38 +++++++++++++++-
5 files changed, 157 insertions(+), 72 deletions(-)

--
2.7.4


2021-06-02 13:51:56

by Tian Tao

[permalink] [raw]
Subject: [PATCH v2 1/3] lib: bitmap: introduce bitmap_print_to_buf

New API bitmap_print_to_buf() with bin_attribute to avoid maskp
exceeding PAGE_SIZE. bitmap_print_to_pagebuf() is a special case
of bitmap_print_to_buf(), so in bitmap_print_to_pagebuf() call
bitmap_print_to_buf().

Signed-off-by: Tian Tao <[email protected]>
---
include/linux/bitmap.h | 3 +++
include/linux/cpumask.h | 21 +++++++++++++++++++++
lib/bitmap.c | 38 ++++++++++++++++++++++++++++++++++++--
3 files changed, 60 insertions(+), 2 deletions(-)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 70a9324..bc401bd9b 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -219,6 +219,9 @@ extern unsigned int bitmap_ord_to_pos(const unsigned long *bitmap, unsigned int
extern int bitmap_print_to_pagebuf(bool list, char *buf,
const unsigned long *maskp, int nmaskbits);

+extern int bitmap_print_to_buf(bool list, char *buf,
+ const unsigned long *maskp, int nmaskbits, loff_t off, size_t count);
+
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))

diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index 383684e..56852f2 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -928,6 +928,27 @@ cpumap_print_to_pagebuf(bool list, char *buf, const struct cpumask *mask)
nr_cpu_ids);
}

+/**
+ * cpumap_print_to_buf - copies the cpumask into the buffer either
+ * as comma-separated list of cpus or hex values of cpumask
+ * @list: indicates whether the cpumap must be list
+ * @mask: the cpumask to copy
+ * @buf: the buffer to copy into
+ * @off: in the string from which we are copying, We copy to @buf + off
+ * @count: the maximum number of bytes to print
+ *
+ * The role of cpumap_print_to_buf and cpumap_print_to_pagebuf is
+ * the same, the difference is that buf of bitmap_print_to_buf
+ * can be more than one pagesize.
+ */
+static inline ssize_t
+cpumap_print_to_buf(bool list, char *buf, const struct cpumask *mask,
+ loff_t off, size_t count)
+{
+ return bitmap_print_to_buf(list, buf, cpumask_bits(mask),
+ nr_cpu_ids, off, count);
+}
+
#if NR_CPUS <= BITS_PER_LONG
#define CPU_MASK_ALL \
(cpumask_t) { { \
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 75006c4..cb64e66 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -460,6 +460,40 @@ int bitmap_parse_user(const char __user *ubuf,
EXPORT_SYMBOL(bitmap_parse_user);

/**
+ * bitmap_print_to_buf - convert bitmap to list or hex format ASCII string
+ * @list: indicates whether the bitmap must be list
+ * @buf: the kernel space buffer to read to
+ * @maskp: pointer to bitmap to convert
+ * @nmaskbits: size of bitmap, in bits
+ * @off: offset in data buffer below
+ * @count: the maximum number of bytes to print
+ *
+ * The role of bitmap_print_to_buf and bitmap_print_to_pagebuf() is
+ * the same, the difference is that buf of bitmap_print_to_buf()
+ * can be more than one pagesize.
+ */
+int bitmap_print_to_buf(bool list, char *buf, const unsigned long *maskp,
+ int nmaskbits, loff_t off, size_t count)
+{
+ int size;
+ void *data;
+ const char *fmt = list ? "%*pbl\n" : "%*pb\n";
+
+ if (off == LLONG_MAX && count == PAGE_SIZE - offset_in_page(buf))
+ return scnprintf(buf, count, fmt, nmaskbits, maskp);
+
+ data = kasprintf(GFP_KERNEL, fmt, nmaskbits, maskp);
+ if (!data)
+ return -ENOMEM;
+
+ size = memory_read_from_buffer(buf, count, &off, data, strlen(data));
+ kfree(data);
+
+ return size;
+}
+EXPORT_SYMBOL(bitmap_print_to_buf);
+
+/**
* bitmap_print_to_pagebuf - convert bitmap to list or hex format ASCII string
* @list: indicates whether the bitmap must be list
* @buf: page aligned buffer into which string is placed
@@ -480,8 +514,8 @@ int bitmap_print_to_pagebuf(bool list, char *buf, const unsigned long *maskp,
{
ptrdiff_t len = PAGE_SIZE - offset_in_page(buf);

- return list ? scnprintf(buf, len, "%*pbl\n", nmaskbits, maskp) :
- scnprintf(buf, len, "%*pb\n", nmaskbits, maskp);
+ return bitmap_print_to_buf(list, buf, maskp, nmaskbits,
+ LLONG_MAX, len);
}
EXPORT_SYMBOL(bitmap_print_to_pagebuf);

--
2.7.4

2021-06-02 13:53:03

by Tian Tao

[permalink] [raw]
Subject: [PATCH v2 3/3] drivers/base/node.c: use bin_attribute to avoid buff overflow

Reading sys/devices/system/cpu/cpuX/nodeX/ returns cpumap and cpulist.
However, the size of this file is limited to PAGE_SIZE because of the
limitation for sysfs attribute. so we use bin_attribute instead of
attribute to avoid NR_CPUS too big to cause buff overflow.

Signed-off-by: Tian Tao <[email protected]>
---
drivers/base/node.c | 52 ++++++++++++++++++++++++++++++++++------------------
1 file changed, 34 insertions(+), 18 deletions(-)

diff --git a/drivers/base/node.c b/drivers/base/node.c
index f449dbb..2659bb44 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -27,42 +27,45 @@ static struct bus_type node_subsys = {
};


-static ssize_t node_read_cpumap(struct device *dev, bool list, char *buf)
+static ssize_t node_read_cpumap(struct device *dev, bool list,
+ char *buf, loff_t off, size_t count)
{
ssize_t n;
cpumask_var_t mask;
struct node *node_dev = to_node(dev);

- /* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */
- BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1));
-
if (!alloc_cpumask_var(&mask, GFP_KERNEL))
return 0;

cpumask_and(mask, cpumask_of_node(node_dev->dev.id), cpu_online_mask);
- n = cpumap_print_to_pagebuf(list, buf, mask);
+ n = cpumap_print_to_buf(list, buf, mask, off, count);
free_cpumask_var(mask);

return n;
}

-static inline ssize_t cpumap_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
{
- return node_read_cpumap(dev, false, buf);
+ struct device *dev = kobj_to_dev(kobj);
+
+ return node_read_cpumap(dev, false, buf, off, count);
}

-static DEVICE_ATTR_RO(cpumap);

-static inline ssize_t cpulist_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static BIN_ATTR_RO(cpumap, 0);
+
+static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
{
- return node_read_cpumap(dev, true, buf);
+ struct device *dev = kobj_to_dev(kobj);
+
+ return node_read_cpumap(dev, true, buf, off, count);
}

-static DEVICE_ATTR_RO(cpulist);
+static BIN_ATTR_RO(cpulist, 0);

/**
* struct node_access_nodes - Access class device to hold user visible
@@ -557,15 +560,28 @@ static ssize_t node_read_distance(struct device *dev,
static DEVICE_ATTR(distance, 0444, node_read_distance, NULL);

static struct attribute *node_dev_attrs[] = {
- &dev_attr_cpumap.attr,
- &dev_attr_cpulist.attr,
&dev_attr_meminfo.attr,
&dev_attr_numastat.attr,
&dev_attr_distance.attr,
&dev_attr_vmstat.attr,
NULL
};
-ATTRIBUTE_GROUPS(node_dev);
+
+static struct bin_attribute *node_dev_bin_attrs[] = {
+ &bin_attr_cpumap,
+ &bin_attr_cpulist,
+ NULL,
+};
+
+static const struct attribute_group node_dev_group = {
+ .attrs = node_dev_attrs,
+ .bin_attrs = node_dev_bin_attrs
+};
+
+static const struct attribute_group *node_dev_groups[] = {
+ &node_dev_group,
+ NULL,
+};

#ifdef CONFIG_HUGETLBFS
/*
--
2.7.4

2021-06-02 14:23:53

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 1/3] lib: bitmap: introduce bitmap_print_to_buf

On Wed, 2 Jun 2021 21:48:52 +0800
Tian Tao <[email protected]> wrote:

> New API bitmap_print_to_buf() with bin_attribute to avoid maskp
> exceeding PAGE_SIZE. bitmap_print_to_pagebuf() is a special case
> of bitmap_print_to_buf(), so in bitmap_print_to_pagebuf() call
> bitmap_print_to_buf().
>
> Signed-off-by: Tian Tao <[email protected]>
Hi,

I think you need strlen() + 1 as strlen() doesn't include the null terminator.
Also good to add () in a few more places to make the hyperlinks work in the
html docs (fairly sure it needs those)

I don't really like the fact we can't get the string length without that
extra call (as it's buried in the kasprintf() implementation) but this is
unlikely to be a high performance path so clean code is better.

Otherwise, LGTM

> ---
> include/linux/bitmap.h | 3 +++
> include/linux/cpumask.h | 21 +++++++++++++++++++++
> lib/bitmap.c | 38 ++++++++++++++++++++++++++++++++++++--
> 3 files changed, 60 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
> index 70a9324..bc401bd9b 100644
> --- a/include/linux/bitmap.h
> +++ b/include/linux/bitmap.h
> @@ -219,6 +219,9 @@ extern unsigned int bitmap_ord_to_pos(const unsigned long *bitmap, unsigned int
> extern int bitmap_print_to_pagebuf(bool list, char *buf,
> const unsigned long *maskp, int nmaskbits);
>
> +extern int bitmap_print_to_buf(bool list, char *buf,
> + const unsigned long *maskp, int nmaskbits, loff_t off, size_t count);
> +
> #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
> #define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
>
> diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
> index 383684e..56852f2 100644
> --- a/include/linux/cpumask.h
> +++ b/include/linux/cpumask.h
> @@ -928,6 +928,27 @@ cpumap_print_to_pagebuf(bool list, char *buf, const struct cpumask *mask)
> nr_cpu_ids);
> }
>
> +/**
> + * cpumap_print_to_buf - copies the cpumask into the buffer either
> + * as comma-separated list of cpus or hex values of cpumask
> + * @list: indicates whether the cpumap must be list
> + * @mask: the cpumask to copy
> + * @buf: the buffer to copy into
> + * @off: in the string from which we are copying, We copy to @buf + off
> + * @count: the maximum number of bytes to print
> + *
> + * The role of cpumap_print_to_buf and cpumap_print_to_pagebuf is

cpumap_print_to_buf()
+ other cases of the same so that hyperlinks work in the html docs.


> + * the same, the difference is that buf of bitmap_print_to_buf
> + * can be more than one pagesize.
> + */
> +static inline ssize_t
> +cpumap_print_to_buf(bool list, char *buf, const struct cpumask *mask,
> + loff_t off, size_t count)
> +{
> + return bitmap_print_to_buf(list, buf, cpumask_bits(mask),
> + nr_cpu_ids, off, count);
> +}
> +
> #if NR_CPUS <= BITS_PER_LONG
> #define CPU_MASK_ALL \
> (cpumask_t) { { \
> diff --git a/lib/bitmap.c b/lib/bitmap.c
> index 75006c4..cb64e66 100644
> --- a/lib/bitmap.c
> +++ b/lib/bitmap.c
> @@ -460,6 +460,40 @@ int bitmap_parse_user(const char __user *ubuf,
> EXPORT_SYMBOL(bitmap_parse_user);
>
> /**
> + * bitmap_print_to_buf - convert bitmap to list or hex format ASCII string
> + * @list: indicates whether the bitmap must be list
> + * @buf: the kernel space buffer to read to
> + * @maskp: pointer to bitmap to convert
> + * @nmaskbits: size of bitmap, in bits
> + * @off: offset in data buffer below
> + * @count: the maximum number of bytes to print
> + *
> + * The role of bitmap_print_to_buf and bitmap_print_to_pagebuf() is
> + * the same, the difference is that buf of bitmap_print_to_buf()
> + * can be more than one pagesize.
> + */
> +int bitmap_print_to_buf(bool list, char *buf, const unsigned long *maskp,
> + int nmaskbits, loff_t off, size_t count)
> +{
> + int size;
> + void *data;
> + const char *fmt = list ? "%*pbl\n" : "%*pb\n";
> +
> + if (off == LLONG_MAX && count == PAGE_SIZE - offset_in_page(buf))
> + return scnprintf(buf, count, fmt, nmaskbits, maskp);
> +
> + data = kasprintf(GFP_KERNEL, fmt, nmaskbits, maskp);
> + if (!data)
> + return -ENOMEM;
> +
> + size = memory_read_from_buffer(buf, count, &off, data, strlen(data));

strlen(data) + 1 I think...

> + kfree(data);
> +
> + return size;
> +}
> +EXPORT_SYMBOL(bitmap_print_to_buf);
> +
> +/**
> * bitmap_print_to_pagebuf - convert bitmap to list or hex format ASCII string
> * @list: indicates whether the bitmap must be list
> * @buf: page aligned buffer into which string is placed
> @@ -480,8 +514,8 @@ int bitmap_print_to_pagebuf(bool list, char *buf, const unsigned long *maskp,
> {
> ptrdiff_t len = PAGE_SIZE - offset_in_page(buf);
>
> - return list ? scnprintf(buf, len, "%*pbl\n", nmaskbits, maskp) :
> - scnprintf(buf, len, "%*pb\n", nmaskbits, maskp);
> + return bitmap_print_to_buf(list, buf, maskp, nmaskbits,
> + LLONG_MAX, len);
> }
> EXPORT_SYMBOL(bitmap_print_to_pagebuf);
>

2021-06-02 15:45:36

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v2 0/3] use bin_attribute to avoid buff overflow

On Wed, Jun 02, 2021 at 09:48:51PM +0800, Tian Tao wrote:
> patch #1 adds a new function cpumap_print_to_buf and patch #2 uses
> this function in drivers/base/topology.c, and patch #3 uses this new
> function in drivers/base/node.c.

Somehow you missed to include BITMAP maintainers / reviewers.

--
With Best Regards,
Andy Shevchenko


2021-06-02 15:50:24

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v2 1/3] lib: bitmap: introduce bitmap_print_to_buf

On Wed, Jun 02, 2021 at 09:48:52PM +0800, Tian Tao wrote:
> New API bitmap_print_to_buf() with bin_attribute to avoid maskp
> exceeding PAGE_SIZE. bitmap_print_to_pagebuf() is a special case
> of bitmap_print_to_buf(), so in bitmap_print_to_pagebuf() call
> bitmap_print_to_buf().

...

> + * The role of cpumap_print_to_buf and cpumap_print_to_pagebuf is

* The role of cpumap_print_to_buf() and cpumap_print_to_pagebuf() is

...

> + * The role of bitmap_print_to_buf and bitmap_print_to_pagebuf() is

* The role of bitmap_print_to_buf() and bitmap_print_to_pagebuf() is

...

> + int size;

Strictly speaking it should be ssize_t.

> + void *data;
> + const char *fmt = list ? "%*pbl\n" : "%*pb\n";

Can you use reversed xmas tree order?

...

> + return bitmap_print_to_buf(list, buf, maskp, nmaskbits,
> + LLONG_MAX, len);

It fits one line.

--
With Best Regards,
Andy Shevchenko


2021-06-02 15:54:57

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v2 3/3] drivers/base/node.c: use bin_attribute to avoid buff overflow

On Wed, Jun 02, 2021 at 09:48:54PM +0800, Tian Tao wrote:
> Reading sys/devices/system/cpu/cpuX/nodeX/ returns cpumap and cpulist.
> However, the size of this file is limited to PAGE_SIZE because of the
> limitation for sysfs attribute. so we use bin_attribute instead of
> attribute to avoid NR_CPUS too big to cause buff overflow.

...

> -static ssize_t node_read_cpumap(struct device *dev, bool list, char *buf)
> +static ssize_t node_read_cpumap(struct device *dev, bool list,
> + char *buf, loff_t off, size_t count)

Why you moved char *buf to the next line? Replacing ) by , doesn't change
character count. Perhaps you need to reconfigure your editor.

...

> +static struct bin_attribute *node_dev_bin_attrs[] = {
> + &bin_attr_cpumap,
> + &bin_attr_cpulist,

> + NULL,

No comma.

> +};

...

> +static const struct attribute_group *node_dev_groups[] = {
> + &node_dev_group,
> + NULL,

Ditto.

> +};

--
With Best Regards,
Andy Shevchenko