KASAN: use-after-free Read in reiserfs_fill_super

Dipanjan Das <[email protected]>
Newsgroups gmane.comp.file-systems.reiserfs.general,gmane.linux.kernel
Message-ID <CANX2M5aYK8_7Ygyh=co+bXTc4-Yoc96O2Z6avPooAn3uYbvoLw@mail.gmail.com>
Hi,

We would like to report the following bug which has been found by our
modified version of syzkaller.

======================================================
description: KASAN: use-after-free Read in reiserfs_fill_super
affected file: fs/reiserfs/inode.c
kernel version: 5.19-rc6
kernel commit: 32346491ddf24599decca06190ebca03ff9de7f8
git tree: upstream
kernel config: https://syzkaller.appspot.com/text?tag=KernelConfig&x=cd73026ceaed1402
crash reproducer: attached
======================================================
Crash log:
======================================================
UG: KASAN: use-after-free in init_inode fs/reiserfs/inode.c:1325 [inline]
BUG: KASAN: use-after-free in reiserfs_read_locked_inode+0x1f9c/0x2300
fs/reiserfs/inode.c:1574
Read of size 4 at addr ffff88819f38c000 by task syz-executor.5/13593

CPU: 0 PID: 13593 Comm: syz-executor.5 Tainted: G           OE
5.19.0-rc6-g2eae0556bb9d #2
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS
1.13.0-1ubuntu1.1 04/01/2014
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:88 [inline]
 dump_stack_lvl+0xcd/0x134 lib/dump_stack.c:106
 print_address_description mm/kasan/report.c:313 [inline]
 print_report.cold+0xe5/0x659 mm/kasan/report.c:429
 kasan_report+0x8a/0x1b0 mm/kasan/report.c:491
 init_inode fs/reiserfs/inode.c:1325 [inline]
 reiserfs_read_locked_inode+0x1f9c/0x2300 fs/reiserfs/inode.c:1574
 reiserfs_fill_super+0x126c/0x2f10 fs/reiserfs/super.c:2073
 mount_bdev+0x34d/0x410 fs/super.c:1367
 legacy_get_tree+0x105/0x220 fs/fs_context.c:610
 vfs_get_tree+0x89/0x2f0 fs/super.c:1497
 do_new_mount fs/namespace.c:3040 [inline]
 path_mount+0x1228/0x1cb0 fs/namespace.c:3370
 do_mount+0xf3/0x110 fs/namespace.c:3383
 __do_sys_mount fs/namespace.c:3591 [inline]
 __se_sys_mount fs/namespace.c:3568 [inline]
 __x64_sys_mount+0x18f/0x230 fs/namespace.c:3568
 do_syscall_x64 arch/x86/entry/common.c:50 [inline]
 do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
 entry_SYSCALL_64_after_hwframe+0x46/0xb0
RIP: 0033:0x7f4fe2e8fe8e
Code: 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f
84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d
01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f4fe148b9f8 EFLAGS: 00000202 ORIG_RAX: 00000000000000a5
RAX: ffffffffffffffda RBX: 0000000020000200 RCX: 00007f4fe2e8fe8e
RDX: 0000000020000040 RSI: 0000000020000100 RDI: 00007f4fe148ba50
RBP: 00007f4fe148ba90 R08: 00007f4fe148ba90 R09: 0000000020000040
R10: 0000000000000000 R11: 0000000000000202 R12: 0000000020000040
R13: 0000000020000100 R14: 00007f4fe148ba50 R15: 0000000020011400
 </TASK>

The buggy address belongs to the physical page:
page:ffffea00067ce300 refcount:0 mapcount:-128
mapping:0000000000000000 index:0x1 pfn:0x19f38c
flags: 0x17ff00000000000(node=0|zone=2|lastcpupid=0x7ff)
raw: 017ff00000000000 ffffea000610d9c8 ffffea0006897b08 0000000000000000
raw: 0000000000000001 0000000000000000 00000000ffffff7f 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as freed
page last allocated via order 0, migratetype Movable, gfp_mask
0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), pid 2972, tgid 2972
(systemd-udevd), ts 583268397100, free_ts 649857437696
 set_page_owner include/linux/page_owner.h:31 [inline]
 post_alloc_hook mm/page_alloc.c:2449 [inline]
 prep_new_page+0x297/0x330 mm/page_alloc.c:2456
 get_page_from_freelist+0x2142/0x3c80 mm/page_alloc.c:4198
 __alloc_pages+0x321/0x710 mm/page_alloc.c:5426
 __folio_alloc+0x12/0x40 mm/page_alloc.c:5457
 vma_alloc_folio+0xf9/0x6e0 mm/mempolicy.c:2233
 alloc_page_vma include/linux/gfp.h:634 [inline]
 wp_page_copy+0x1f1/0x1d00 mm/memory.c:3104
 do_wp_page+0x52c/0x1840 mm/memory.c:3471
 handle_pte_fault mm/memory.c:4921 [inline]
 __handle_mm_fault+0x2a69/0x3ee0 mm/memory.c:5042
 handle_mm_fault+0x322/0xa80 mm/memory.c:5140
 do_user_addr_fault+0x489/0x11c0 arch/x86/mm/fault.c:1397
 handle_page_fault arch/x86/mm/fault.c:1484 [inline]
 exc_page_fault+0x9e/0x180 arch/x86/mm/fault.c:1540
 asm_exc_page_fault+0x27/0x30 arch/x86/include/asm/idtentry.h:570
page last free stack trace:
 reset_page_owner include/linux/page_owner.h:24 [inline]
 free_pages_prepare mm/page_alloc.c:1371 [inline]
 free_pcp_prepare+0x51f/0xd00 mm/page_alloc.c:1421
 free_unref_page_prepare mm/page_alloc.c:3343 [inline]
 free_unref_page_list+0x15a/0xe90 mm/page_alloc.c:3475
 release_pages+0x10ea/0x2590 mm/swap.c:980
 tlb_batch_pages_flush mm/mmu_gather.c:58 [inline]
 tlb_flush_mmu_free mm/mmu_gather.c:255 [inline]
 tlb_flush_mmu+0xef/0x690 mm/mmu_gather.c:262
 tlb_finish_mmu+0x93/0x3c0 mm/mmu_gather.c:353
 exit_mmap+0x1de/0x4b0 mm/mmap.c:3164
 __mmput kernel/fork.c:1196 [inline]
 mmput+0x16b/0x430 kernel/fork.c:1217
 exit_mm kernel/exit.c:511 [inline]
 do_exit+0xa33/0x2dd0 kernel/exit.c:784
 do_group_exit+0xd2/0x2f0 kernel/exit.c:927
 __do_sys_exit_group kernel/exit.c:938 [inline]
 __se_sys_exit_group kernel/exit.c:936 [inline]
 __x64_sys_exit_group+0x3a/0x50 kernel/exit.c:936
 do_syscall_x64 arch/x86/entry/common.c:50 [inline]
 do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
 entry_SYSCALL_64_after_hwframe+0x46/0xb0

Memory state around the buggy address:
 ffff88819f38bf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ffff88819f38bf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88819f38c000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
                   ^
 ffff88819f38c080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ffff88819f38c100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================

-- 
Thanks and Regards,

Dipanjan
repro.syz (application/octet-stream, 1 KB) - not displayed
repro.c (text/x-csrc, 8.5 KB)
// autogenerated by syzkaller (https://github.com/google/syzkaller)

#define _GNU_SOURCE 

#include <dirent.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#include <linux/loop.h>

#ifndef __NR_memfd_create
#define __NR_memfd_create 319
#endif

static unsigned long long procid;

static void sleep_ms(uint64_t ms)
{
	usleep(ms * 1000);
}

static uint64_t current_time_ms(void)
{
	struct timespec ts;
	if (clock_gettime(CLOCK_MONOTONIC, &ts))
	exit(1);
	return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}

static bool write_file(const char* file, const char* what, ...)
{
	char buf[1024];
	va_list args;
	va_start(args, what);
	vsnprintf(buf, sizeof(buf), what, args);
	va_end(args);
	buf[sizeof(buf) - 1] = 0;
	int len = strlen(buf);
	int fd = open(file, O_WRONLY | O_CLOEXEC);
	if (fd == -1)
		return false;
	if (write(fd, buf, len) != len) {
		int err = errno;
		close(fd);
		errno = err;
		return false;
	}
	close(fd);
	return true;
}

struct fs_image_segment {
	void* data;
	uintptr_t size;
	uintptr_t offset;
};

#define IMAGE_MAX_SEGMENTS 4096
#define IMAGE_MAX_SIZE (129 << 20)

static unsigned long fs_image_segment_check(unsigned long size, unsigned long nsegs, struct fs_image_segment* segs)
{
	if (nsegs > IMAGE_MAX_SEGMENTS)
		nsegs = IMAGE_MAX_SEGMENTS;
	for (size_t i = 0; i < nsegs; i++) {
		if (segs[i].size > IMAGE_MAX_SIZE)
			segs[i].size = IMAGE_MAX_SIZE;
		segs[i].offset %= IMAGE_MAX_SIZE;
		if (segs[i].offset > IMAGE_MAX_SIZE - segs[i].size)
			segs[i].offset = IMAGE_MAX_SIZE - segs[i].size;
		if (size < segs[i].offset + segs[i].offset)
			size = segs[i].offset + segs[i].offset;
	}
	if (size > IMAGE_MAX_SIZE)
		size = IMAGE_MAX_SIZE;
	return size;
}
static int setup_loop_device(long unsigned size, long unsigned nsegs, struct fs_image_segment* segs, const char* loopname, int* memfd_p, int* loopfd_p)
{
	int err = 0, loopfd = -1;
	size = fs_image_segment_check(size, nsegs, segs);
	int memfd = syscall(__NR_memfd_create, "syzkaller", 0);
	if (memfd == -1) {
		err = errno;
		goto error;
	}
	if (ftruncate(memfd, size)) {
		err = errno;
		goto error_close_memfd;
	}
	for (size_t i = 0; i < nsegs; i++) {
		if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) {
		}
	}
	loopfd = open(loopname, O_RDWR);
	if (loopfd == -1) {
		err = errno;
		goto error_close_memfd;
	}
	if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
		if (errno != EBUSY) {
			err = errno;
			goto error_close_loop;
		}
		ioctl(loopfd, LOOP_CLR_FD, 0);
		usleep(1000);
		if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
			err = errno;
			goto error_close_loop;
		}
	}
	*memfd_p = memfd;
	*loopfd_p = loopfd;
	return 0;

error_close_loop:
	close(loopfd);
error_close_memfd:
	close(memfd);
error:
	errno = err;
	return -1;
}

static long syz_mount_image(volatile long fsarg, volatile long dir, volatile unsigned long size, volatile unsigned long nsegs, volatile long segments, volatile long flags, volatile long optsarg)
{
	struct fs_image_segment* segs = (struct fs_image_segment*)segments;
	int res = -1, err = 0, loopfd = -1, memfd = -1, need_loop_device = !!segs;
	char* mount_opts = (char*)optsarg;
	char* target = (char*)dir;
	char* fs = (char*)fsarg;
	char* source = NULL;
	char loopname[64];
	if (need_loop_device) {
		memset(loopname, 0, sizeof(loopname));
		snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
		if (setup_loop_device(size, nsegs, segs, loopname, &memfd, &loopfd) == -1)
			return -1;
		source = loopname;
	}
	mkdir(target, 0777);
	char opts[256];
	memset(opts, 0, sizeof(opts));
	if (strlen(mount_opts) > (sizeof(opts) - 32)) {
	}
	strncpy(opts, mount_opts, sizeof(opts) - 32);
	if (strcmp(fs, "iso9660") == 0) {
		flags |= MS_RDONLY;
	} else if (strncmp(fs, "ext", 3) == 0) {
		if (strstr(opts, "errors=panic") || strstr(opts, "errors=remount-ro") == 0)
			strcat(opts, ",errors=continue");
	} else if (strcmp(fs, "xfs") == 0) {
		strcat(opts, ",nouuid");
	}
	res = mount(source, target, fs, flags, opts);
	if (res == -1) {
		err = errno;
		goto error_clear_loop;
	}
	res = open(target, O_RDONLY | O_DIRECTORY);
	if (res == -1) {
		err = errno;
	}

error_clear_loop:
	if (need_loop_device) {
		ioctl(loopfd, LOOP_CLR_FD, 0);
		close(loopfd);
		close(memfd);
	}
	errno = err;
	return res;
}

static void kill_and_wait(int pid, int* status)
{
	kill(-pid, SIGKILL);
	kill(pid, SIGKILL);
	for (int i = 0; i < 100; i++) {
		if (waitpid(-1, status, WNOHANG | __WALL) == pid)
			return;
		usleep(1000);
	}
	DIR* dir = opendir("/sys/fs/fuse/connections");
	if (dir) {
		for (;;) {
			struct dirent* ent = readdir(dir);
			if (!ent)
				break;
			if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
				continue;
			char abort[300];
			snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", ent->d_name);
			int fd = open(abort, O_WRONLY);
			if (fd == -1) {
				continue;
			}
			if (write(fd, abort, 1) < 0) {
			}
			close(fd);
		}
		closedir(dir);
	} else {
	}
	while (waitpid(-1, status, __WALL) != pid) {
	}
}

static void reset_loop()
{
	char buf[64];
	snprintf(buf, sizeof(buf), "/dev/loop%llu", procid);
	int loopfd = open(buf, O_RDWR);
	if (loopfd != -1) {
		ioctl(loopfd, LOOP_CLR_FD, 0);
		close(loopfd);
	}
}

static void setup_test()
{
	prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
	setpgrp();
	write_file("/proc/self/oom_score_adj", "1000");
}

static void execute_one(void);

#define WAIT_FLAGS __WALL

static void loop(void)
{
	int iter = 0;
	for (;; iter++) {
		reset_loop();
		int pid = fork();
		if (pid < 0)
	exit(1);
		if (pid == 0) {
			setup_test();
			execute_one();
			exit(0);
		}
		int status = 0;
		uint64_t start = current_time_ms();
		for (;;) {
			if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid)
				break;
			sleep_ms(1);
			if (current_time_ms() - start < 5000)
				continue;
			kill_and_wait(pid, &status);
			break;
		}
	}
}

void execute_one(void)
{
memcpy((void*)0x20000040, "reiserfs\000", 9);
memcpy((void*)0x20000100, "./file0\000", 8);
*(uint64_t*)0x20000200 = 0x20010000;
memcpy((void*)0x20010000, "\x00\x40\x00\x00\x68\x02\x00\x00\x97\x3d\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x84\x3d\x00\x00\x00\x01\x00\x00\x61\x1c\xad\x49\xe1\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x10\xec\x03\x02\x00\x01\x00\x52\x65\x49\x73\x45\x72\x33\x46\x73\x00\x00\x00\x02\x00\x00\x00\x02", 69);
*(uint64_t*)0x20000208 = 0x45;
*(uint64_t*)0x20000210 = 0x10000;
*(uint64_t*)0x20000218 = 0x20011100;
memcpy((void*)0x20011100, "\x80\x00\x00\x03\x00\x20\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x84\x3d\x00\x00\x00\x01\x00\x00\x61\x1c\xad\x49\xe1", 33);
*(uint64_t*)0x20000220 = 0x21;
*(uint64_t*)0x20000228 = 0x3d96000;
*(uint64_t*)0x20000230 = 0x20000280;
memcpy((void*)0x20000280, "\x01\x00\x02\x00\x75\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\xe0\x0f\x10\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\xf4\x01\x00\x00\x02\x00\x23\x00\xbd\x0f\xf9\x0b\x71\x85\xf1\x1a\xc8\x09\x10\x8c\x77\x8d\x51\xe3\xe0\x43\xcb\x42\x20\x23\x61\x74\xc7\x8c\x42\xb1\x79\x5b\x37\x3c\xcf\x55\x37\xfa\x6c\x8b\x4c\x1b\xab\xd7\xf1\xcc\xf3\x48\x07\x55\x75\x1b\xc5\x30\xc9\xa7\xfb\x72\x41\xbd\x73\xcc\x02\x82\xae\x33\xdc\x1e\x8d\x35\xe3\xb3\x0f\x5b\xff\xf3\x3e\x0c\x10\xbd\xc3\x34\x11\x6f\xb3\x19\x36\x26\x6d\x74\x93\x69\xef\x5f\x46\x28\x66\x32\xae\xdd\xa8\x68\x21\x79\xd4\xd2\x90\x00\x00\x00\x00\x8d\xbd\x44\xdd\xc4\xa1\xc7\x5a\xb7\x9b\x5b\x94\xd6\x5c\xd1\x95\x88\x55\x40\xd1\xc0\x3d\x68\x40\x23\x2d\xdd\xa8\x3d\xd6\x70\xb6\x37\x78\x74\xd0\xf1\x51\xa2\x1b\x68\x27\x77\x9b\xf7\xb1\x0f\xf8\x60\x7a\x65\x83\x77\xd9\x00\x00\x0c\xcb\xba\xa7\xc5\xe7\x1b\xf2\x1d\x89\xe1\xe2\x46\xec\x1c\x98\x96\x67\x26\x04\x0b\x31\xac\x7f\x54\xc9\x7a\x67\x1b\x53\xf9\x99\x0d\xac\x11\xf0\x79\xbd\xa4\xdb\x57\xc9\x3f\xa1\x01\xd9\xd5\x33\x17\x40\xef\x9b\x9d\x69\xc3\x28\xa0\xea\xdf\x29\xc8\xc6\x3d\xe2\x48\xc7\x95\x93\x28\x15\x0f\x17\xb9\xcf\x72", 308);
*(uint64_t*)0x20000238 = 0x134;
*(uint64_t*)0x20000240 = 0x3d97000;
*(uint8_t*)0x20011400 = 0;
syz_mount_image(0x20000040, 0x20000100, 0, 3, 0x20000200, 0, 0x20011400);

}
int main(void)
{
		syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
	syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
	syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
			loop();
	return 0;
}
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.