[PATCH v1 1/4] powerpc/perf: hv-gpci: bound sysfs hex formatting to PAGE_SIZE

From: Xixin Liu

Date: Thu Aug 06 2026 - 23:32:40 EST


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 <liuxixin@xxxxxxxxxx>
---
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