Re: scsi: memory leak in sg_start_req

From: Douglas Gilbert
Date: Thu Jan 11 2018 - 01:13:27 EST


On 2018-01-09 11:05 AM, Dmitry Vyukov wrote:
Hello,

syzkaller has found the following memory leak:

unreferenced object 0xffff88004c190000 (size 8328):
comm "syz-executor", pid 4627, jiffies 4294749150 (age 45.507s)
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
20 00 00 00 22 01 00 00 00 00 00 00 04 00 00 00 ..."...........
backtrace:
[<000000005955b5a9>] kmalloc_order+0x59/0x80 mm/slab_common.c:1124
[<0000000043ae006e>] kmalloc_order_trace+0x1f/0x160 mm/slab_common.c:1133
[<00000000d33b2e16>] kmalloc_large include/linux/slab.h:433 [inline]
[<00000000d33b2e16>] __kmalloc+0x2c4/0x340 mm/slub.c:3751
[<00000000e7430040>] kmalloc include/linux/slab.h:504 [inline]
[<00000000e7430040>] bio_alloc_bioset+0x4d5/0x7e0 block/bio.c:450
[<00000000f370e717>] bio_kmalloc include/linux/bio.h:410 [inline]
[<00000000f370e717>] bio_copy_user_iov+0x2be/0xcb0 block/bio.c:1226
[<000000001d0b79ed>] __blk_rq_map_user_iov block/blk-map.c:67 [inline]
[<000000001d0b79ed>] blk_rq_map_user_iov+0x2b6/0x7d0 block/blk-map.c:136
[<000000004200a869>] blk_rq_map_user+0x11e/0x170 block/blk-map.c:166
[<000000008f21739e>] sg_start_req drivers/scsi/sg.c:1794 [inline]
[<000000008f21739e>] sg_common_write.isra.16+0x14df/0x1ed0
drivers/scsi/sg.c:777
[<00000000093f61e3>] sg_write+0x8a7/0xd7b drivers/scsi/sg.c:677
[<00000000b67dafdc>] __vfs_write+0x10d/0x8f0 fs/read_write.c:480
[<000000000638f16f>] vfs_write+0x1fd/0x570 fs/read_write.c:544
[<000000006a7e6867>] SYSC_write fs/read_write.c:589 [inline]
[<000000006a7e6867>] SyS_write+0xfa/0x250 fs/read_write.c:581

can be reproduced with the following program:

// autogenerated by syzkaller (http://github.com/google/syzkaller)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
int fd = open("/dev/sg1", O_RDWR);
const char *data =
"\xb6\x3d\xb8\x5e\x1e\x8d\x22\x00\x00\x00\x00\x00\x00\x08\xaf\xd6\x1d"
"\xcc\x43\x6a\xed\x5e\xd2\xbc\x70\x18\xce\xbc\x9b\x97\xae\x21\x91\x4d"
"\x87\x2c\x67\x8c\xe2\x2c\x9b\x16\x0e\x96\xaa\x1f\xae\x1a";
write(fd, data, 0x30);
return 0;
}

if executed in a loop, memory consumption grows infinitely.

on upstream commit b2cd1df66037e7c4697c7e40496bf7e4a5e16a2d

The seemingly random data that program is sending is asking for
a buffer of 2,264,314 bytes which the sg driver procures and waits
for the caller to either issue a read() or close() the file
or shutdown the program. The test program does none of those
expected operations, it simply asks for the same resources again.

In my version of your test code (attached), that happens 1,021 times
at which point the file handles in that process are exhausted and
all subsequent open()s fail with EBADF (as do the write()s). The
output from my program was this on one run:

# ./sg_syzk_grow
First errno=9 [Bad file descriptor] index=1021
done_count=50000, err_count=48979, last_errno=9 [Bad file descriptor]

# lsscsi -gs
[0:0:0:0] disk Linux scsi_debug 0186 /dev/sda /dev/sg0 2.14GB

Monitoring that program with 'free' from another terminal I see
about 2.5 GBytes of ram "swallowed" almost immediately when the test
program runs. When the program exits (about 50 seconds later) as far
as I can see all that ram is given back.


If you used the same program and wrote to a regular file rather than
a sg device, then that program would eventually fill any file system,
at the rate of 48 bytes per iteration (given enough file descriptor
resources). The sg driver, using its original 1994 interface,
deprecated for around 18 years, just gets a system to resource
exhaustion quicker.

Doug Gilbert




// autogenerated by syzkaller (http://github.com/google/syzkaller)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>

static const char * sg_dev = "/dev/sg0";

int main()
{
const char *data =
"\xb6\x3d\xb8\x5e\x1e\x8d\x22\x00\x00\x00\x00\x00\x00\x08\xaf\xd6\x1d"
"\xcc\x43\x6a\xed\x5e\xd2\xbc\x70\x18\xce\xbc\x9b\x97\xae\x21\x91\x4d"
"\x87\x2c\x67\x8c\xe2\x2c\x9b\x16\x0e\x96\xaa\x1f\xae\x1a";
int k, res, first_errno, prev_errno, first_err_index;
int err_count = 0;
int done_count = 0;
bool got_1st_errno = false;

for (k = 0; k < 10000; ++k) {
int fd = open(sg_dev, O_RDWR);

res = write(fd, data, 0x30);
if (res < 0) {
if (! got_1st_errno) {
got_1st_errno = true;
first_errno = errno;
first_err_index = done_count + k;
printf("First errno=%d [%s] index=%d\n", first_errno,
strerror(first_errno), first_err_index);
}
++err_count;
}
}
done_count += k;
sleep(10);
for (k = 0; k < 10000; ++k) {
int fd = open(sg_dev, O_RDWR);

res = write(fd, data, 0x30);
if (res < 0) {
if (! got_1st_errno) {
got_1st_errno = true;
first_errno = errno;
printf("First errno=%d [%s] index=%d\n", first_errno,
strerror(first_errno), first_err_index);
first_err_index = done_count + k;
}
++err_count;
}
}
done_count += k;
sleep(10);
for (k = 0; k < 10000; ++k) {
int fd = open(sg_dev, O_RDWR);

res = write(fd, data, 0x30);
if (res < 0)
++err_count;
}
done_count += k;
sleep(10);
for (k = 0; k < 10000; ++k) {
int fd = open(sg_dev, O_RDWR);

res = write(fd, data, 0x30);
if (res < 0)
++err_count;
}
done_count += k;
sleep(10);
for (k = 0; k < 10000; ++k) {
int fd = open(sg_dev, O_RDWR);

res = write(fd, data, 0x30);
if (res < 0) {
prev_errno = errno;
++err_count;
}
}
done_count += k;
sleep(10);
printf("done_count=%d, err_count=%d, last_errno=%d [%s]\n", done_count,
err_count, prev_errno, strerror(prev_errno));

return 0;

}