FreeBSD Bugzilla – Attachment 233588 Details for
Bug 263662
[fusefs] kernel panics if fuse daemon returns parent's i-number in FUSE_CREATE
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
fuse daemon that causes a panic by returning parent dir's i-number for FUSE_CREATE
futo1a.c (text/plain), 17.01 KB, created by
Robert Morris
on 2022-04-29 17:52:22 UTC
(
hide
)
Description:
fuse daemon that causes a panic by returning parent dir's i-number for FUSE_CREATE
Filename:
MIME Type:
Creator:
Robert Morris
Created:
2022-04-29 17:52:22 UTC
Size:
17.01 KB
patch
obsolete
>// >// fuse toy daemon. >// >// pkg install fusefs-libs >// cc -I/usr/local/include/fuse -o futo1a futo1a.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 <sys/wait.h> >#include <fuse.h> >#include <fuse_lowlevel.h> >#ifdef __linux__ >#include <linux/fuse.h> >#else >#include <fuse_kernel.h> >#endif > >int which_op = -1; >int which_count = 0; >int symex_ino = 0; >int symex_type = 0; > >int counts[1000]; // indexed by operation > >#define SYM 0 > >static inline unsigned long symx() { > return 0; >} > >void * >xinit(struct fuse_conn_info *conn) >{ > 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; > if(ii < 256){ > a->ino = ino ^ (symex_ino ? symx() : 0); > a->mode = (info[ii].type | 0777) ^ (symex_type ? symx() : 0); > a->nlink = 1; > a->size = 1024; > a->blksize = 512; > a->blocks = 2; > } else { > a->ino = ino ^ (symex_ino ? symx() : 0); > a->mode = (S_IFDIR | 0777) ^ (symex_type ? symx() : 0); > 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[] = { "futo", "/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); > long ibufsize = fuse_chan_bufsize(ch); > char *ibuf = valloc(ibufsize); > if(ibuf == 0) { perror("valloc"); exit(1); } > > sync(); sleep(1); > > int nops = 0; > > while(1){ > int icc = read(fd, ibuf, ibufsize); > if(icc <= 0){ > perror("read"); > exit(1); > } > struct fuse_in_header *ih = (struct fuse_in_header *) ibuf; > > char obuf[8192]; > 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 < 0 || which_count == counts[ih->opcode])){ > syming = 1; > } > > printf("%d %d %d: ", nops, ih->opcode, counts[ih->opcode]); >#if !SYM > printf("ino=%ld ", ih->nodeid); >#endif > > if(ih->opcode == FUSE_INIT){ > struct fuse_init_in *in = (struct fuse_init_in *)(ih+1); > printf("init "); >#if !SYM > printf(" major %d minor %d flags 0x%x", in->major, in->minor, in->flags); >#endif > printf("\n"); > 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\n"); > 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 = 1; > 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\n"); > 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\n"); > 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 "); >#if !SYM > printf("offset=%lu size=%d read_flags=%x", > in->offset, in->size, in->read_flags); >#endif > printf("\n"); > oh->len = sizeof(struct fuse_out_header); > if(in->offset == 0){ > char *content = (char *)(oh + 1); > for(int ii = 0; ii < 256; ii++){ > if(info[ii].name && info[ii].parent == ih->nodeid){ > struct fuse_dirent *dirent = (struct fuse_dirent *) content; > dirent->ino = info[ii].ino ^ (symex_ino ? symx() : 0); > dirent->off = 1; > dirent->namelen = strlen(info[ii].name); > dirent->type = (S_IFREG >> 12) ^ (symex_type ? symx() : 0); > memcpy(dirent->name, info[ii].name, dirent->namelen); > int sz = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + dirent->namelen); > content += sz; > } > } > oh->len = content - obuf; > } > } else if(ih->opcode == 44){ // FUSE_READDIRPLUS > struct fuse_read_in *in = (struct fuse_read_in *)(ih+1); > printf("readdirplus "); >#if !SYM > printf("offset=%lu size=%d read_flags=%xn", > in->offset, in->size, in->read_flags); >#endif > printf("\n"); > oh->len = sizeof(struct fuse_out_header); > if(in->offset == 0){ > char *content = (char *)(oh + 1); > for(int ii = 0; ii < 256; ii++){ > if(info[ii].name && info[ii].parent == ih->nodeid){ > struct fuse_direntplus { > struct fuse_entry_out entry_out; > struct fuse_dirent dirent; > }; > struct fuse_direntplus *dplus = (struct fuse_direntplus *)content; > { > struct fuse_entry_out *msg = &(dplus->entry_out); > msg->nodeid = info[ii].ino ^ (symex_ino ? symx() : 0); > msg->generation = 1; > msg->entry_valid = 1; > msg->attr_valid = 1; > fill_attr(&msg->attr, info[ii].ino); > } > struct fuse_dirent *dirent = &(dplus->dirent); > dirent->ino = info[ii].ino ^ (symex_ino ? symx() : 0); > dirent->off = 1; > dirent->namelen = strlen(info[ii].name); > dirent->type = (S_IFREG >> 12) ^ (symex_type ? symx() : 0); > memcpy(dirent->name, info[ii].name, dirent->namelen); > int sz = sizeof(struct fuse_direntplus) + dirent->namelen; > while((sz % 8) != 0) sz += 1; > content += sz; > } > } > oh->len = content - obuf; > } > } 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 "); >#if !SYM > printf("%s", name); >#endif > printf("\n"); > 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 ^ (symex_ino ? symx() : 0); > msg->generation = 1; > msg->entry_valid = 1; > msg->attr_valid = 1; > 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 "); >#if !SYM > printf("flags=%x release_flags=%x", > in->flags, in->release_flags); >#endif > printf("\n"); > 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\n"); > 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 = 1; > 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\n"); > oh = 0; // no reply > } else if(ih->opcode == FUSE_OPEN){ > struct fuse_open_in *in = (struct fuse_open_in *)(ih+1); > printf("open\n"); > 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 "); >#if !SYM > printf("offset=%lu size=%d read_flags=%x", > in->offset, in->size, in->read_flags); >#endif > printf("\n"); > 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\n"); > 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 "); >#if !SYM > printf("offset=%lu size=%d", in->offset, in->size); >#endif > printf("\n"); > 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\n"); > 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 "); >#if !SYM > printf("name=%s mode=0%o", name, in->mode); >#endif > printf("\n"); > 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 ^ (symex_ino ? symx() : 0); > msg->generation = 1; > msg->entry_valid = 1; > msg->attr_valid = 1; > fill_attr(&msg->attr, nino); > printf(" new nodeid=%lu\n", msg->nodeid); > fflush(stdout); > usleep(100000); > } else if(ih->opcode == FUSE_CREATE){ > struct fuse_create_in *in = (struct fuse_create_in *)(ih+1); > char *name = (char *)(in + 1); > printf("create "); >#if !SYM > printf("name=%s mode=0%o", name, in->mode); >#endif > printf("\n"); > 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 = ih->nodeid; // give new file the same inumber as parent dir. > msg->generation = 1; > msg->entry_valid = 1; > msg->attr_valid = 1; > fill_attr(&msg->attr, nino); > struct fuse_open_out *msg1 = (struct fuse_open_out *)(msg + 1); > msg1->fh = nino; > printf(" new nodeid=%lu\n", msg->nodeid); > fflush(stdout); > usleep(100000); > } else if(ih->opcode == FUSE_UNLINK){ > char *name= (char *)(ih+1); > printf("unlink\n"); > 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 "); >#if !SYM > printf("name=%s size=%d", name, in->size); >#endif > printf("\n"); > if(in->size == 0){ > struct fuse_getxattr_out *msg = (struct fuse_getxattr_out *)(oh + 1); > if(strcmp(name, "system.posix_acl_access") == 0){ > msg->size = 64 + 4; > } else if(strcmp(name, "security.capability") == 0){ > msg->size = 20; > } else { > msg->size = 64; > } > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_getxattr_out); > } else { > int xsize = 64; > if(strcmp(name, "system.posix_acl_access") == 0){ > xsize = 64 + 4; > } else if(strcmp(name, "security.capability") == 0){ > xsize = 20; > } > oh->len = sizeof(struct fuse_out_header) + xsize; > char *content = (char *)(in + 1); > for(int i = 0; i < xsize; 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 "); >#if !SYM > printf("name=%s size=%d", name, in->size); >#endif > printf("\n"); > struct fuse_getxattr_out *msg = (struct fuse_getxattr_out *)(oh + 1); > msg->size = 64; > int actual = msg->size; > if(actual > in->size) > actual = in->size; > oh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_getxattr_out) + actual; > char *content = (char *)(msg + 1); > for(int i = 0; i < actual; i++){ > content[i] = 'x'; > } > } else if(ih->opcode == FUSE_REMOVEXATTR){ > char *name= (char *)(ih+1); > printf("removexattr "); >#if !SYM > printf("name=%s", name); >#endif > printf("\n"); > oh->len = sizeof(struct fuse_out_header); > } else if(ih->opcode == FUSE_SETXATTR){ > struct fuse_setxattr_in *in = (struct fuse_setxattr_in *)(ih+1); > char *name = (char*)(in+1); > char *value = name + strlen(name) + 1; > printf("setxattr "); >#if !SYM > printf("size=%d flags=%x name=%s value=%s", > in->size, in->flags, name, value); >#endif > printf("\n"); > oh->len = sizeof(struct fuse_out_header); > } else if(ih->opcode == FUSE_RENAME){ > struct fuse_rename_in *in = (struct fuse_rename_in *)(ih+1); > char *name1 = (char *)(in + 1); > char *name2 = name1 + strlen(name1) + 1; > printf("rename "); >#if !SYM > printf("%s %s", name1, name2); >#endif > printf("\n"); > oh->len = sizeof(struct fuse_out_header); > } else { > printf("unknown op %d\n", ih->opcode); > oh->len = sizeof(struct fuse_out_header); > oh->error = -EINVAL; > } > > if(oh){ > if(syming){ > for(int i = 16; i < oh->len; i += 8) > *(long long *)(obuf+i) ^= symx(); > unsigned long long eee = symx(); > 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(int argc, char *argv[]) >{ > struct rlimit r; > r.rlim_cur = r.rlim_max = 0; > setrlimit(RLIMIT_CORE, &r); > signal(SIGPIPE, SIG_IGN); > > if(argc > 1 && strcmp(argv[1], "d") == 0){ > server(); > exit(0); > } > > int spid = fork(); > if(spid < 0) { perror("fork"); exit(1); } > if(spid == 0){ > server(); > exit(1); > } > > sync(); sleep(1); > > int cpid = fork(); > if(cpid < 0) { perror("fork"); exit(1); } > if(cpid == 0){ > mkdir("/mnt/d", 0777); > creat("/mnt/d/a", 0777); > exit(0); > } > > for(int i = 0; i < 20; i++){ > int st; > if(waitpid(cpid, &st, WNOHANG) == cpid) > break; > sleep(1); > } >}
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 263662
: 233588