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
