[PATCH] scsi: sg: Normalize scatter element size before get_order()
"Jiacheng Xu" <[email protected]>
| Newsgroups | org.kernel.vger.linux-scsi |
|---|---|
| Message-ID | <[email protected]> |
When scatter_elem_sz is set to a value smaller than PAGE_SIZE through
its writable sysfs module parameter, sg_build_indirect() updates the
global scatter element size variables but leaves the local `num`
variable unchanged.
If the parameter is set to zero, get_order(num) is called with zero.
On 64-bit systems, get_order(0) underflows internally and returns an
invalid order. This causes:
ret_sz = 1 << (PAGE_SHIFT + order);
to shift a 32-bit integer by 64 bits, triggering:
UBSAN: shift-out-of-bounds in sg_build_indirect
Update `num` together with the global values when clamping it to
PAGE_SIZE.
The reproducer and UBSAN crash report are attached:
- repro.c
- repro.report: UBSAN crash report
Fixes: 6460e75a104d ("[SCSI] sg: fixes for large page_size")
Signed-off-by: m0ck1ng <[email protected]>
---
drivers/scsi/sg.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 5408f002e6c0..69013335e433 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1876,6 +1876,7 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size)
num = scatter_elem_sz;
if (unlikely(num != scatter_elem_sz_prev)) {
if (num < PAGE_SIZE) {
+ num = PAGE_SIZE;
scatter_elem_sz = PAGE_SIZE;
scatter_elem_sz_prev = PAGE_SIZE;
} else
scatter_elem_sz_prev = num;
repro.c
(text/plain, 621 B)
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("/sys/module/sg/parameters/scatter_elem_sz", O_WRONLY);
if (fd < 0) {
perror("open scatter_elem_sz");
return 1;
}
if (write(fd, "0", 1) != 1) {
perror("write scatter_elem_sz");
close(fd);
return 1;
}
close(fd);
fd = open("/dev/sg0", O_RDONLY);
if (fd < 0) {
perror("open /dev/sg0");
return 1;
}
close(fd);
return 0;
}
repro.report
(application/octet-stream, 4.6 KB) - not displayed