[PATCH] scsi: sg: Normalize scatter element size before get_order()

"Jiacheng Xu" <[email protected]>
Newsgroups gmane.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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.