FreeBSD Bugzilla – Attachment 233202 Details for
Bug 263263
[FUSEFS] fuse daemon can cause kernel page fault with huge size in write reply
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
fuse daemon that replies to write with a huge size, causing a kernel page fault
futo17a.c (text/plain), 13.34 KB, created by
Robert Morris
on 2022-04-13 17:56:23 UTC
(
hide
)
Description:
fuse daemon that replies to write with a huge size, causing a kernel page fault
Filename:
MIME Type:
Creator:
Robert Morris
Created:
2022-04-13 17:56:23 UTC
Size:
13.34 KB
patch
obsolete
>// >// fuse toy daemon. >// >// pkg install fusefs-libs >// cc -I/usr/local/include/fuse -o futo17a futo17a.c -L/usr/local/lib -lfuse >// > >#define _FILE_OFFSET_BITS 64 >#define FUSE_USE_VERSION 26 >#include <stdio.h> >#include <stdlib.h> >#include <unistd.h> >#include <fcntl.h> >#include <sys/resource.h> >#include <sys/stat.h> >#include <string.h> >#include <signal.h> >#include <stddef.h> >#include <errno.h> >#include <fuse.h> >#include <fuse_lowlevel.h> >#include <fuse_kernel.h> > >int which_op = 16; >int which_count = 0; > >int counts[1000]; // indexed by operation > >unsigned long long aa[] = { >0xfffc5eb8ull, >0x0ull, >}; >int aai; > >void * >xinit(struct fuse_conn_info *conn) >{ > printf("xinit\n"); > return 0; >} > >struct info { > int parent; // inumber > char *name; > int ino; > int type; // S_IFDIR, S_IFREG >} info[256] = { > { 0, "", 1, S_IFDIR }, >}; > >void >fill_attr(struct fuse_attr *a, int ino) >{ > int ii; > for(ii = 0; ii < 256; ii++) > if(ino == info[ii].ino) > break; > a->ino = ino; > a->mode = info[ii].type | 0777; > a->nlink = 1; > a->size = 1024; > a->blksize = 512; > a->blocks = 2; >} > >int >lookup(int parent, char *name) >{ > for(int ii = 0; ii < 256; ii++){ > if(info[ii].name && info[ii].parent == parent && strcmp(name, info[ii].name) == 0){ > return info[ii].ino; > } > } > return -1; >} > >int >find_free_slot() >{ > for(int ii = 0; ii < 256; ii++){ > if(info[ii].name == 0 && info[ii].ino == 0){ > return ii; > } > } > printf("out of inodes\n"); > exit(1); >} > >void >server() >{ > > // system("kldload fusefs"); > > int argc = 5; > char *argv[] = { "a.out", "/mnt", "-s", "-d", "-f", 0 }; > struct fuse_operations ops; > memset(&ops, 0, sizeof(ops)); > ops.init = xinit; > char *mountpoint = "/mnt"; > int multithreaded = 0; > int xxxfd = -1; > struct fuse *fuse = fuse_setup(argc, argv, &ops, sizeof(ops), > &mountpoint, &multithreaded, &xxxfd); > if(fuse == 0){ > fprintf(stderr, "fuse_setup() failed\n"); > exit(1); > } > struct fuse_session *se = fuse_get_session(fuse); > struct fuse_chan *ch = fuse_session_next_chan(se, 0); > int fd = fuse_chan_fd(ch); > > int nops = 0; > > while(1){ > char ibuf[1024]; > > int icc = read(fd, ibuf, sizeof(ibuf)); > if(icc <= 0){ > perror("read"); > exit(1); > } > struct fuse_in_header *ih = (struct fuse_in_header *) ibuf; > > char obuf[1024]; > memset(obuf, 0, sizeof(obuf)); > struct fuse_out_header *oh = (struct fuse_out_header *) obuf; > oh->unique = ih->unique; > > int syming = 0; > if(which_op == ih->opcode && which_count == counts[ih->opcode]){ > syming = 1; > } > > printf("%d %d %d: ", nops, ih->opcode, counts[ih->opcode]); > > if(ih->opcode == FUSE_INIT){ > struct fuse_init_in *in = (struct fuse_init_in *)(ih+1); > printf("init major %d minor %d flags 0x%x\n", in->major, in->minor, in->flags); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_init_out); > struct fuse_init_out *msg = (struct fuse_init_out *)(oh + 1); > msg->major = 7; // version > msg->minor = 28; > msg->max_readahead = 1024; > msg->max_write = 1024; > msg->max_background = 1024; > msg->congestion_threshold = 1024; > msg->flags = 0x7f | FUSE_FLOCK_LOCKS; > msg->flags |= FUSE_POSIX_LOCKS; > msg->flags |= (1 << 13); // FUSE_DO_READDIRPLUS; > msg->flags |= (1 << 14); // FUSE_READDIRPLUS_AUTO; > msg->flags |= (1 << 20); // FUSE_POSIX_ACL; > } else if(ih->opcode == FUSE_GETATTR){ > struct fuse_getattr_in *in = (struct fuse_getattr_in *)(ih+1); > printf("getattr ino=%lu flags=%x\n", ih->nodeid, in->getattr_flags); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out); > struct fuse_attr_out *msg = (struct fuse_attr_out *)(oh + 1); > msg->attr_valid = 10; > fill_attr(&msg->attr, ih->nodeid); > } else if(ih->opcode == FUSE_ACCESS){ > struct fuse_access_in *in = (struct fuse_access_in *)(ih+1); > printf("access ino=%lu mask=%x\n", ih->nodeid, in->mask); > oh->len = sizeof(struct fuse_out_header); > } else if(ih->opcode == FUSE_OPENDIR){ > struct fuse_open_in *in = (struct fuse_open_in *)(ih+1); > printf("opendir ino=%lu flags=%x\n", ih->nodeid, in->flags); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out); > struct fuse_open_out *msg = (struct fuse_open_out *)(oh + 1); > msg->fh = ih->nodeid; > msg->open_flags = 0; > } else if(ih->opcode == FUSE_READDIR){ > struct fuse_read_in *in = (struct fuse_read_in *)(ih+1); > printf("readdir ino=%lu offset=%lu size=%d read_flags=%x\n", > ih->nodeid, in->offset, in->size, in->read_flags); > oh->len = sizeof(struct fuse_out_header); > int ii; > for(ii = 0; ii < 256; ii++){ > if(info[ii].name && info[ii].parent == ih->nodeid){ > break; > } > } > if(in->offset == 0 && ii < 256 && info[ii].parent == ih->nodeid){ > char *content = (char *)(oh + 1); > struct fuse_dirent *dirent = (struct fuse_dirent *) content; > dirent->ino = info[ii].ino; > dirent->off = 1; > dirent->namelen = strlen(info[ii].name); > dirent->type = S_IFREG >> 12; > memcpy(dirent->name, info[ii].name, dirent->namelen); > int sz = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + dirent->namelen); > oh->len += sz; > } > } else if(ih->opcode == FUSE_STATFS){ > printf("statfs\n"); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_statfs_out); > struct fuse_statfs_out *msg = (struct fuse_statfs_out *)(oh + 1); > msg->st.blocks = 1000000; > msg->st.bfree = 500000; > msg->st.bavail = 500000; > msg->st.files = 1000000; > msg->st.files = 500000; > msg->st.bsize = 512; > msg->st.namelen = 256; > msg->st.frsize = 512; > } else if(ih->opcode == FUSE_LOOKUP){ > char *name= (char *)(ih+1); > printf("lookup ino=%lu %s\n", ih->nodeid, name); > struct fuse_entry_out *msg = (struct fuse_entry_out *)(oh + 1); > int ino = lookup(ih->nodeid, name); > if(ino < 0){ > oh->len = sizeof(struct fuse_out_header); > oh->error = -ENOENT; > } else { > msg->nodeid = ino; > msg->generation = 1; > msg->entry_valid = 100; > msg->attr_valid = 100; > msg->entry_valid_nsec = 100000000; > msg->attr_valid_nsec = 100000000; > fill_attr(&msg->attr, ino); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out); > } > } else if(ih->opcode == FUSE_RELEASEDIR){ > struct fuse_release_in *in = (struct fuse_release_in *)(ih+1); > printf("releasedir fh=%lx flags=%x release_flags=%x\n", > in->fh, in->flags, in->release_flags); > oh->len = sizeof(struct fuse_out_header); > } else if(ih->opcode == FUSE_SETATTR){ > struct fuse_setattr_in *in = (struct fuse_setattr_in *)(ih+1); > printf("setattr ino=%lu\n", ih->nodeid); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out); > struct fuse_attr_out *msg = (struct fuse_attr_out *)(oh + 1); > msg->attr_valid = 10; > fill_attr(&msg->attr, ih->nodeid); > } else if(ih->opcode == FUSE_FORGET){ > struct fuse_forget_in *in = (struct fuse_forget_in *)(ih+1); > printf("forget nlookup=%lx\n", in->nlookup); > oh = 0; // no reply > } else if(ih->opcode == FUSE_OPEN){ > struct fuse_open_in *in = (struct fuse_open_in *)(ih+1); > printf("open ino=%lu flags=%x\n", ih->nodeid, in->flags); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out); > struct fuse_open_out *msg = (struct fuse_open_out *)(oh + 1); > msg->fh = ih->nodeid; > msg->open_flags = 0; > } else if(ih->opcode == FUSE_READ){ > struct fuse_read_in *in = (struct fuse_read_in *)(ih+1); > printf("read fh=%lx offset=%lu size=%d read_flags=%x\n", > in->fh, in->offset, in->size, in->read_flags); > oh->len = sizeof(struct fuse_out_header); > if(in->offset == 0){ > char *content = (char *)(oh + 1); > memcpy(content, "hello\n", 6); > int sz = 6; > oh->len += sz; > } > } else if(ih->opcode == FUSE_FLUSH){ > struct fuse_flush_in *in = (struct fuse_flush_in *)(ih+1); > printf("flush fh=%lx\n", in->fh); > oh->len = sizeof(struct fuse_out_header); > } else if(ih->opcode == FUSE_WRITE){ > struct fuse_write_in *in = (struct fuse_write_in *)(ih+1); > printf("write fh=%lx offset=%lu size=%d\n", in->fh, in->offset, in->size); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out); > struct fuse_write_out *msg = (struct fuse_write_out *)(oh + 1); > msg->size = in->size; > } else if(ih->opcode == FUSE_RELEASE){ > struct fuse_release_in *in = (struct fuse_release_in *)(ih+1); > printf("release fh=%lx flags=%x release_flags=%x\n", > in->fh, in->flags, in->release_flags); > oh->len = sizeof(struct fuse_out_header); > } else if(ih->opcode == FUSE_MKDIR){ > struct fuse_mkdir_in *in = (struct fuse_mkdir_in *)(ih+1); > char *name = (char *)(in + 1); > printf("mkdir ino=%lu name=%s mode=0%o\n", ih->nodeid, name, in->mode); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out); > struct fuse_entry_out *msg = (struct fuse_entry_out *)(oh + 1); > int slot = find_free_slot(); > int nino = slot + 100; > info[slot].parent = ih->nodeid; > info[slot].name = malloc(strlen(name)+1); > strcpy(info[slot].name, name); > info[slot].ino = nino; > info[slot].type = S_IFDIR; > msg->nodeid = nino; > msg->generation = 1; > msg->entry_valid = 100; > msg->attr_valid = 100; > msg->entry_valid_nsec = 100000000; > msg->attr_valid_nsec = 100000000; > fill_attr(&msg->attr, nino); > } else if(ih->opcode == FUSE_CREATE){ > struct fuse_create_in *in = (struct fuse_create_in *)(ih+1); > char *name = (char *)(in + 1); > printf("create ino=%lu name=%s mode=0%o\n", ih->nodeid, name, in->mode); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out); > struct fuse_entry_out *msg = (struct fuse_entry_out *)(oh + 1); > int slot = find_free_slot(); > int nino = slot + 100; > info[slot].parent = ih->nodeid; > info[slot].name = malloc(strlen(name)+1); > strcpy(info[slot].name, name); > info[slot].ino = nino; > info[slot].type = S_IFREG; > msg->nodeid = nino; > msg->generation = 1; > msg->entry_valid = 100; > msg->attr_valid = 100; > msg->entry_valid_nsec = 100000000; > msg->attr_valid_nsec = 100000000; > fill_attr(&msg->attr, nino); > struct fuse_open_out *msg1 = (struct fuse_open_out *)(msg + 1); > msg1->fh = nino; > } else if(ih->opcode == FUSE_UNLINK){ > char *name= (char *)(ih+1); > printf("unlink ino=%lu %s\n", ih->nodeid, name); > struct fuse_entry_out *msg = (struct fuse_entry_out *)(oh + 1); > int ino = lookup(ih->nodeid, name); > oh->len = sizeof(struct fuse_out_header); > if(ino < 0){ > oh->error = -ENOENT; > } else { > for(int ii = 0; ii < 256; ii++){ > if(info[ii].ino == ino){ > memset(&info[ii], 0, sizeof(info[ii])); > } > } > oh->error = 0; > } > } else if(ih->opcode == FUSE_GETXATTR){ > struct fuse_getxattr_in *in = (struct fuse_getxattr_in *)(ih+1); > char *name = (char *)(in + 1); > printf("getxattr ino=%lu name=%s size=%d\n", ih->nodeid, name, in->size); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_getxattr_out) + 64; > struct fuse_getxattr_out *msg = (struct fuse_getxattr_out *)(oh + 1); > msg->size = 64; > char *content = (char *)(msg + 1); > for(int i = 0; i < msg->size; i++){ > content[i] = 'x'; > } > } else if(ih->opcode == FUSE_LISTXATTR){ > struct fuse_getxattr_in *in = (struct fuse_getxattr_in *)(ih+1); > char *name = (char *)(in + 1); > printf("listxattr ino=%lu name=%s size=%d\n", ih->nodeid, name, in->size); > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_getxattr_out) + 64; > struct fuse_getxattr_out *msg = (struct fuse_getxattr_out *)(oh + 1); > msg->size = 64; > char *content = (char *)(msg + 1); > for(int i = 0; i < msg->size; i++){ > content[i] = 'x'; > } > } else { > printf("unknown op %d\n", ih->opcode); > oh = 0; > } > > if(oh){ > if(syming){ > for(int i = 16; i < oh->len; i += 8) > *(long long *)(obuf+i) ^= aa[aai++]; > unsigned long long eee = aa[aai++]; > if(oh->error || oh->len == sizeof(struct fuse_out_header)){ > oh->error ^= eee; > } > } > int cc = write(fd, obuf, oh->len); > if(cc <= 0) { perror("write"); } > } > > nops += 1; > counts[ih->opcode] += 1; > } >} > >int >main() >{ > struct rlimit r; > r.rlim_cur = r.rlim_max = 0; > setrlimit(RLIMIT_CORE, &r); > signal(SIGPIPE, SIG_IGN); > > int spid = fork(); > if(spid < 0) { perror("fork"); exit(1); } > if(spid == 0){ > server(); > exit(1); > } > > sleep(5); > > int cpid = fork(); > if(cpid < 0) { perror("fork"); exit(1); } > if(cpid == 0){ > system("mkdir /mnt/d"); > system("touch /mnt/d/a"); > system("touch /mnt/d/b"); > system("ls -l /mnt/d > /dev/null"); > system("chmod 0 /mnt/d/a"); > system("echo x > /mnt/y"); > system("dd if=/mnt/y of=/dev/zero bs=1 count=16 > /dev/null"); > system("rm /mnt/d/a"); > system("getextattr -x system xxx /mnt/y > /dev/null"); > system("lsextattr system /mnt/y > /dev/null"); > exit(0); > } > > sleep(20); > >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 263263
: 233202