hv-gpci sysfs show paths format hypervisor counter bytes with sprintf()
into a PAGE_SIZE buffer, and only sometimes check the length afterwards.

Each byte becomes two hex digits plus newlines, so the output can grow
past PAGE_SIZE. Checking after sprintf() is too late: the write already
overflowed the sysfs buffer. One path had no size check at all.

Use sysfs_emit_at() so formatting stays within the sysfs buffer. If a
field cannot fit completely, return -EFBIG rather than silently
truncating.

Signed-off-by: Xixin Liu <[email protected]>
---
 arch/powerpc/perf/hv-gpci.c | 77 +++++++++++++++++++++++++------------
 1 file changed, 52 insertions(+), 25 deletions(-)

diff --git a/arch/powerpc/perf/hv-gpci.c b/arch/powerpc/perf/hv-gpci.c
index 76495744f..14a4f414b 100644
--- a/arch/powerpc/perf/hv-gpci.c
+++ b/arch/powerpc/perf/hv-gpci.c
@@ -136,6 +136,7 @@ static unsigned long systeminfo_gpci_request(u32 req, u32 
starting_index,
 {
        unsigned long ret;
        size_t i, j;
+       int len;
 
        arg->params.counter_request = cpu_to_be32(req);
        arg->params.starting_index = cpu_to_be32(starting_index);
@@ -177,17 +178,23 @@ static unsigned long systeminfo_gpci_request(u32 req, u32 
starting_index,
        for (i = 0; i < be16_to_cpu(arg->params.returned_values); i++) {
                j = i * be16_to_cpu(arg->params.cv_element_size);
 
-               for (; j < (i + 1) * be16_to_cpu(arg->params.cv_element_size); 
j++)
-                       *n += sprintf(buf + *n,  "%02x", (u8)arg->bytes[j]);
-               *n += sprintf(buf + *n,  "\n");
-       }
-
-       if (*n >= PAGE_SIZE) {
-               pr_info("System information exceeds PAGE_SIZE\n");
-               return -EFBIG;
+               for (; j < (i + 1) * be16_to_cpu(arg->params.cv_element_size); 
j++) {
+                       len = sysfs_emit_at(buf, *n, "%02x", (u8)arg->bytes[j]);
+                       if (len != 2)
+                               goto emit_failed;
+                       *n += len;
+               }
+               len = sysfs_emit_at(buf, *n, "\n");
+               if (len != 1)
+                       goto emit_failed;
+               *n += len;
        }
 
        return ret;
+
+emit_failed:
+       pr_info("System information does not fit in sysfs buffer\n");
+       return -EFBIG;
 }
 
 static ssize_t processor_bus_topology_show(struct device *dev, struct 
device_attribute *attr,
@@ -470,12 +477,13 @@ static ssize_t affinity_domain_via_domain_show(struct 
device *dev, struct device
        return ret;
 }
 
-static void affinity_domain_via_partition_result_parse(int returned_values,
+static int affinity_domain_via_partition_result_parse(int returned_values,
                        int element_size, char *buf, size_t *last_element,
                        size_t *n, struct hv_gpci_request_buffer *arg)
 {
        size_t i = 0, j = 0;
        size_t k, l, m;
+       int len;
        uint16_t total_affinity_domain_ele, size_of_each_affinity_domain_ele;
 
        /*
@@ -492,27 +500,44 @@ static void 
affinity_domain_via_partition_result_parse(int returned_values,
         */
        while (i < returned_values) {
                k = j;
-               for (; k < j + element_size; k++)
-                       *n += sprintf(buf + *n,  "%02x", (u8)arg->bytes[k]);
-               *n += sprintf(buf + *n,  "\n");
+               for (; k < j + element_size; k++) {
+                       len = sysfs_emit_at(buf, *n, "%02x", (u8)arg->bytes[k]);
+                       if (len != 2)
+                               return -EFBIG;
+                       *n += len;
+               }
+               len = sysfs_emit_at(buf, *n, "\n");
+               if (len != 1)
+                       return -EFBIG;
+               *n += len;
 
                total_affinity_domain_ele = (u8)arg->bytes[k - 2] << 8 | 
(u8)arg->bytes[k - 3];
                size_of_each_affinity_domain_ele = (u8)arg->bytes[k] << 8 | 
(u8)arg->bytes[k - 1];
 
                for (l = 0; l < total_affinity_domain_ele; l++) {
                        for (m = 0; m < size_of_each_affinity_domain_ele; m++) {
-                               *n += sprintf(buf + *n,  "%02x", 
(u8)arg->bytes[k]);
+                               len = sysfs_emit_at(buf, *n, "%02x", 
(u8)arg->bytes[k]);
+                               if (len != 2)
+                                       return -EFBIG;
+                               *n += len;
                                k++;
                        }
-                       *n += sprintf(buf + *n,  "\n");
+                       len = sysfs_emit_at(buf, *n, "\n");
+                       if (len != 1)
+                               return -EFBIG;
+                       *n += len;
                }
 
-               *n += sprintf(buf + *n,  "\n");
+               len = sysfs_emit_at(buf, *n, "\n");
+               if (len != 1)
+                       return -EFBIG;
+               *n += len;
                i++;
                j = k;
        }
 
        *last_element = k;
+       return 0;
 }
 
 static ssize_t affinity_domain_via_partition_show(struct device *dev, struct 
device_attribute *attr,
@@ -555,12 +580,10 @@ static ssize_t affinity_domain_via_partition_show(struct 
device *dev, struct dev
         * to buffer util we get all the information.
         */
        while (ret == H_PARAMETER) {
-               affinity_domain_via_partition_result_parse(
-                       be16_to_cpu(arg->params.returned_values) - 1,
-                       be16_to_cpu(arg->params.cv_element_size), buf,
-                       &last_element, &n, arg);
-
-               if (n >= PAGE_SIZE) {
+               if (affinity_domain_via_partition_result_parse(
+                               be16_to_cpu(arg->params.returned_values) - 1,
+                               be16_to_cpu(arg->params.cv_element_size), buf,
+                               &last_element, &n, arg)) {
                        put_cpu_var(hv_gpci_reqb);
                        pr_debug("System information does not fit in sysfs 
buffer\n");
                        return -EFBIG;
@@ -587,10 +610,14 @@ static ssize_t affinity_domain_via_partition_show(struct 
device *dev, struct dev
        }
 
 parse_result:
-       affinity_domain_via_partition_result_parse(
-               be16_to_cpu(arg->params.returned_values),
-               be16_to_cpu(arg->params.cv_element_size),
-               buf, &last_element, &n, arg);
+       if (affinity_domain_via_partition_result_parse(
+                       be16_to_cpu(arg->params.returned_values),
+                       be16_to_cpu(arg->params.cv_element_size),
+                       buf, &last_element, &n, arg)) {
+               put_cpu_var(hv_gpci_reqb);
+               pr_debug("System information does not fit in sysfs buffer\n");
+               return -EFBIG;
+       }
 
        put_cpu_var(hv_gpci_reqb);
        return n;
-- 
2.43.0


Reply via email to