From: Christian Marangi <ansuelsmth@xxxxxxxxx> Fix buffer overflow in trans_stat_show(). Convert simple snprintf to the more secure scnprintf with size of PAGE_SIZE. Add condition checking if we are exceeding PAGE_SIZE and exit early from loop. Also add at the end a warning that we exceeded PAGE_SIZE and that stats is disabled. Return -EFBIG in the case where we don't have enough space to write the full transition table. Also document in the ABI that this function can return -EFBIG error. Link: https://lore.kernel.org/all/20231024183016.14648-2-ansuelsmth@xxxxxxxxx/ Cc: stable@xxxxxxxxxxxxxxx Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218041 Fixes: e552bbaf5b98 ("PM / devfreq: Add sysfs node for representing frequency transition information.") Signed-off-by: Christian Marangi <ansuelsmth@xxxxxxxxx> Signed-off-by: Chanwoo Choi <cw00.choi@xxxxxxxxxxx> (backported from commit 08e23d05fa6dc4fc13da0ccf09defdd4bbc92ff4) [koichiroden: Adjusted context due to missing commits: commit b5d281f6c16d ("PM / devfreq: Rework freq_table to be local to devfreq struct") commit a03dacb0316f ("PM / devfreq: Add cpu based scaling support to passive governor") commit 483d557ee9a3 ("PM / devfreq: Clean up the devfreq instance name in sysfs attr") commit 1ebd0bc0e8ad ("PM / devfreq: Move statistics to separate struct devfreq_stats") commit 14a343968199 ("PM / devfreq: Add clearing transitions stats") commit b76b3479dab9 ("PM / devfreq: Change time stats to 64-bit") commit 5c0f6c795957 ("PM / devfreq: Add new interrupt_driven flag for governors")] CVE-2023-52614 Signed-off-by: Koichiro Den <koichiro.den@xxxxxxxxxxxxx> --- Documentation/ABI/testing/sysfs-class-devfreq | 2 + drivers/devfreq/devfreq.c | 60 +++++++++++++------ 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 75897e2fde43..f95b69551b60 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -61,6 +61,8 @@ Description: In order to activate this ABI, the devfreq target device driver should provide the list of available frequencies with its profile. + If the transition table is bigger than PAGE_SIZE, reading + this will return an -EFBIG error. What: /sys/class/devfreq/.../userspace/set_freq Date: September 2011 diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 31e6cb5211bc..7a6115c23ec8 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1403,12 +1403,12 @@ static ssize_t trans_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct devfreq *devfreq = to_devfreq(dev); - ssize_t len; + ssize_t len = 0; int i, j; unsigned int max_state = devfreq->profile->max_state; if (max_state == 0) - return sprintf(buf, "Not Supported.\n"); + return scnprintf(buf, PAGE_SIZE, "Not Supported.\n"); mutex_lock(&devfreq->lock); if (!devfreq->stop_polling && @@ -1418,32 +1418,54 @@ static ssize_t trans_stat_show(struct device *dev, } mutex_unlock(&devfreq->lock); - len = sprintf(buf, " From : To\n"); - len += sprintf(buf + len, " :"); - for (i = 0; i < max_state; i++) - len += sprintf(buf + len, "%10lu", - devfreq->profile->freq_table[i]); + len += scnprintf(buf + len, PAGE_SIZE - len, " From : To\n"); + len += scnprintf(buf + len, PAGE_SIZE - len, " :"); + for (i = 0; i < max_state; i++) { + if (len >= PAGE_SIZE - 1) + break; + len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu", + devfreq->profile->freq_table[i]); + } + if (len >= PAGE_SIZE - 1) + return PAGE_SIZE - 1; - len += sprintf(buf + len, " time(ms)\n"); + len += scnprintf(buf + len, PAGE_SIZE - len, " time(ms)\n"); for (i = 0; i < max_state; i++) { + if (len >= PAGE_SIZE - 1) + break; if (devfreq->profile->freq_table[i] == devfreq->previous_freq) { - len += sprintf(buf + len, "*"); + len += scnprintf(buf + len, PAGE_SIZE - len, "*"); } else { - len += sprintf(buf + len, " "); + len += scnprintf(buf + len, PAGE_SIZE - len, " "); + } + if (len >= PAGE_SIZE - 1) + break; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu:", + devfreq->profile->freq_table[i]); + for (j = 0; j < max_state; j++) { + if (len >= PAGE_SIZE - 1) + break; + len += scnprintf(buf + len, PAGE_SIZE - len, "%10u", + devfreq->trans_table[(i * max_state) + j]); } - len += sprintf(buf + len, "%10lu:", - devfreq->profile->freq_table[i]); - for (j = 0; j < max_state; j++) - len += sprintf(buf + len, "%10u", - devfreq->trans_table[(i * max_state) + j]); - len += sprintf(buf + len, "%10u\n", - jiffies_to_msecs(devfreq->time_in_state[i])); + if (len >= PAGE_SIZE - 1) + break; + len += scnprintf(buf + len, PAGE_SIZE - len, "%10u\n", + jiffies_to_msecs(devfreq->time_in_state[i])); + } + + if (len < PAGE_SIZE - 1) + len += scnprintf(buf + len, PAGE_SIZE - len, "Total transition : %u\n", + devfreq->total_trans); + + if (len >= PAGE_SIZE - 1) { + pr_warn_once("devfreq transition table exceeds PAGE_SIZE. Disabling\n"); + return -EFBIG; } - len += sprintf(buf + len, "Total transition : %u\n", - devfreq->total_trans); return len; } static DEVICE_ATTR_RO(trans_stat); -- 2.43.0