* discovering races Try to change timing conditions of the env. - more/less faster/slower CPUs - change memory size - change disk types/sizes - add/remove printfs and assertions (can sometimes help) * assorted hw1 issues * adding/overriding system calls in Linux have to change the dispatch table of system calls used to have a global "void *syscall_table[]" variable - and a max_syscalls variable Over years, made it harder and harder to modify syscalls using a loadable module. - bsd has an inherited syscall table per process, that can be modified. out solution: create a static new syscall that takes a void* - can override the fxn ptr using a module - void* can pack any struct for any no. of args inside kernel in your syscall code: 1. verify that your void* is valid, for the sizeof(struct mysyscallargs) - e.g. verify_area(...) 2. make a private copy of the struct - kaddr = kmalloc(...) - copy_from_user(kaddr, vaddr, len) // stick a note @ and of fxn "don't forget to kfree(kaddr)" 3. kaddr is ptr to struct with several args: - char *infile, char *outfile, void *keybuf, u_int keylen, u_int flags - deref kaddr->flags ? yes - deref kaddr->keylen ? yes - deref kaddr->keybuf ? no. first verify_area(), then kmalloc, copy_from_user... - deref kaddr->infile ? no: need to "copy from user" but null terminated strings, can't easily tell how long till \0 note: string pathnames can't be longer than 4KB (POSIX) use a helper routine kpath = getname(__user char *path) and don't forget to call "putpath" caveat: past kernels didn't EXPORT_SYMBOL(getname) and putname 3. then you verify the args passed to syscall - return appropriate errors, don't forget cleanup/kfree/etc. - when you can't tell what error to return for invalid inputs, use EINVAL 4. open in/outfiles as needed - filp_open() ... filp_close() use IS_ERR, ERR_PTR strategy: incremental dev - open/close infile - then read 1B from infile - then 4KB - then more - then test writing "hello world" to a new file - then write infile data to outfile - try small files, try files == 4KB in length, multiples of 4KB, and finally any arbitrary length (where last 4KB of file isn't all filled in). - what's an optimal unit size to read/write/copy in hw1: use PAGE_CACHE_SIZE (sometimes called PAGE_SIZE) -- 4KB - don't forget to commit+push your code to git frequently. 5. reading/writing data in kernel using kernel buffers - most read/write functions in kernel assume __user buffers, and translate them to kaddrs - in hw1, your buffer to read/write is a kaddr: must NOT translate thinking it's a __user vaddr! (you'll get some other random address that's wrong). - don't call kernel_read/write, b/c they force translation of addrs as if they are __user addresses. Instead, call vfs_read/write, which can take a kernel addr as is. 6. your user level test C program should check for bad parameters, but your kernel code cannot assume that user code checked for all kinds of bad parameters (e.g., passing both enc + dec flags). 7. testing (next time)