Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/kernel/syscalls.c 3 * 4 * Copyright 2018, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/types.h> 9 #include <fiwix/syscalls.h> 10 #include <fiwix/mm.h> 11 #include <fiwix/errno.h> 12 #include <fiwix/string.h> 13 14 #ifdef __DEBUG__ 15 #include <fiwix/stdio.h> 16 #endif /*__DEBUG__ */ 17 18 void free_name(const char *name) 19 { 20 kfree((unsigned int)name); 21 } 22 23 /* 24 * This function has two objectives: 25 * 1. to check the memory address validity of the char pointer supplied by the 26 * user, while at the same time limit its length to PAGE_SIZE (4096) bytes. 27 * 2. to create a copy of 'filename' in the kernel data space before using it. 28 */ 29 int malloc_name(const char *filename, char **name) 30 { 31 struct vma *vma; 32 unsigned int start; 33 short int n, len; 34 char *b; 35 36 /* 37 * Verifies if the 'vma' array of that process is not empty. It can 38 * only be empty during the initialization of INIT, when it calls to 39 * sys_execve and sys_open without having yet a proper setup. 40 */ 41 if(current->vma[0].s_type != 0) { 42 if(!filename) { 43 return -EFAULT; 44 } 45 start = (unsigned int)filename; 46 if(!(vma = find_vma_region(start))) { 47 return -EFAULT; 48 } 49 if(!(vma->prot & PROT_READ)) { 50 return -EFAULT; 51 } 52 len = MIN(vma->end - start, PAGE_SIZE); 53 if(len < PAGE_SIZE) { 54 if((vma = find_vma_region(vma->end))) { 55 if(vma->prot & PROT_READ) { 56 len = PAGE_SIZE; 57 } 58 } 59 } 60 } else { 61 len = PAGE_SIZE; 62 } 63 if(!(b = (char *)kmalloc())) { 64 return -ENOMEM; 65 } 66 *name = b; 67 for(n = 0; n < len; n++) { 68 if(!(*b = *filename)) { 69 return 0; 70 } 71 b++; 72 filename++; 73 } 74 75 free_name(*name); 76 return -ENAMETOOLONG; 77 } 78 79 int check_user_permission(struct inode *i) 80 { 81 if(!IS_SUPERUSER) { 82 if(current->euid != i->i_uid) { 83 return 1; 84 } 85 } 86 return 0; 87 } 88 89 int check_group(struct inode *i) 90 { 91 int n; 92 __gid_t gid; 93 94 if(current->flags & PF_USEREAL) { 95 gid = current->gid; 96 } else { 97 gid = current->egid; 98 } 99 100 if(i->i_gid == gid) { 101 return 0; 102 } 103 104 for(n = 0; n < NGROUPS_MAX; n++) { 105 if(current->groups[n] == -1) { 106 break; 107 } 108 if(current->groups[n] == i->i_gid) { 109 return 0; 110 } 111 } 112 return 1; 113 } 114 115 int check_user_area(int type, const void *addr, unsigned int size) 116 { 117 struct vma *vma; 118 unsigned int start; 119 120 /* 121 * Verifies if the 'vma' array of that process is not empty. It can 122 * only be empty during the initialization of INIT, when it calls to 123 * sys_execve and sys_open without having yet a proper setup. 124 */ 125 if(current->vma[0].s_type != 0) { 126 start = (unsigned int)addr; 127 if(!(vma = find_vma_region(start))) { 128 return -EFAULT; 129 } 130 131 for(;;) { 132 if(type == VERIFY_WRITE) { 133 if(!(vma->prot & PROT_WRITE)) { 134 return -EFAULT; 135 } 136 } else { 137 if(!(vma->prot & PROT_READ)) { 138 return -EFAULT; 139 } 140 } 141 if(start + size <= vma->end) { 142 break; 143 } 144 if(!(vma = find_vma_region(vma->end))) { 145 return -EFAULT; 146 } 147 } 148 } 149 150 return 0; 151 } 152 153 int check_permission(int mask, struct inode *i) 154 { 155 __uid_t uid; 156 157 if(current->flags & PF_USEREAL) { 158 uid = current->uid; 159 } else { 160 uid = current->euid; 161 } 162 163 if(uid == 0) { 164 return 0; 165 } 166 if(i->i_uid == uid) { 167 if((((i->i_mode >> 6) & 7) & mask) == mask) { 168 return 0; 169 } 170 } 171 if(!check_group(i)) { 172 if((((i->i_mode >> 3) & 7) & mask) == mask) { 173 return 0; 174 } 175 } 176 if(((i->i_mode & 7) & mask) == mask) { 177 return 0; 178 } 179 180 return -EACCES; 181 } 182 183 /* Linux 2.0.40 ABI system call (some from 2.2.26) */ 184 void *syscall_table[] = { 185 NULL, /* 0 */ /* sys_setup (-ENOSYS) */ 186 sys_exit, 187 sys_fork, 188 sys_read, 189 sys_write, 190 sys_open, /* 5 */ 191 sys_close, 192 sys_waitpid, 193 sys_creat, 194 sys_link, 195 sys_unlink, /* 10 */ 196 sys_execve, 197 sys_chdir, 198 sys_time, 199 sys_mknod, 200 sys_chmod, /* 15 */ 201 sys_chown, 202 NULL, /* sys_break (-ENOSYS) */ 203 sys_stat, 204 sys_lseek, 205 sys_getpid, /* 20 */ 206 sys_mount, 207 sys_umount, 208 sys_setuid, 209 sys_getuid, 210 sys_stime, /* 25 */ 211 NULL, // sys_ptrace 212 sys_alarm, 213 sys_fstat, 214 sys_pause, 215 sys_utime, /* 30 */ 216 NULL, /* sys_stty (-ENOSYS) */ 217 NULL, /* sys_gtty (-ENOSYS) */ 218 sys_access, 219 NULL, // sys_nice 220 sys_ftime, /* 35 */ 221 sys_sync, 222 sys_kill, 223 sys_rename, 224 sys_mkdir, 225 sys_rmdir, /* 40 */ 226 sys_dup, 227 sys_pipe, 228 sys_times, 229 NULL, // sys_prof 230 sys_brk, /* 45 */ 231 sys_setgid, 232 sys_getgid, 233 sys_signal, 234 sys_geteuid, 235 sys_getegid, /* 50 */ 236 NULL, // sys_acct 237 sys_umount2, 238 NULL, /* sys_lock (-ENOSYS) */ 239 sys_ioctl, 240 sys_fcntl, /* 55 */ 241 NULL, /* sys_mpx (-ENOSYS) */ 242 sys_setpgid, 243 NULL, /* sys_ulimit (-ENOSYS) */ 244 sys_olduname, 245 sys_umask, /* 60 */ 246 sys_chroot, 247 sys_ustat, 248 sys_dup2, 249 sys_getppid, 250 sys_getpgrp, /* 65 */ 251 sys_setsid, 252 sys_sigaction, 253 sys_sgetmask, 254 sys_ssetmask, 255 sys_setreuid, /* 70 */ 256 sys_setregid, 257 sys_sigsuspend, 258 sys_sigpending, 259 sys_sethostname, 260 sys_setrlimit, /* 75 */ 261 sys_getrlimit, 262 sys_getrusage, 263 sys_gettimeofday, 264 sys_settimeofday, 265 sys_getgroups, /* 80 */ 266 sys_setgroups, 267 old_select, 268 sys_symlink, 269 sys_lstat, 270 sys_readlink, /* 85 */ 271 NULL, // sys_uselib 272 NULL, // sys_swapon 273 sys_reboot, 274 NULL, // old_readdir 275 old_mmap, /* 90 */ 276 sys_munmap, 277 sys_truncate, 278 sys_ftruncate, 279 sys_fchmod, 280 sys_fchown, /* 95 */ 281 NULL, // sys_getpriority 282 NULL, // sys_setpriority 283 NULL, /* sys_profil (-ENOSYS) */ 284 sys_statfs, 285 sys_fstatfs, /* 100 */ 286 sys_ioperm, 287 sys_socketcall, // sys_socketcall XXX 288 NULL, // sys_syslog 289 sys_setitimer, 290 sys_getitimer, /* 105 */ 291 sys_newstat, 292 sys_newlstat, 293 sys_newfstat, 294 sys_uname, 295 sys_iopl, /* 110 */ 296 NULL, // sys_vhangup 297 NULL, /* sys_idle (-ENOSYS) */ 298 NULL, // sys_vm86old 299 sys_wait4, 300 NULL, // sys_swapoff /* 115 */ 301 sys_sysinfo, 302 NULL, // sys_ipc 303 sys_fsync, 304 sys_sigreturn, 305 NULL, // sys_clone /* 120 */ 306 sys_setdomainname, 307 sys_newuname, 308 NULL, // sys_modify_ldt 309 NULL, // sys_adjtimex 310 sys_mprotect, /* 125 */ 311 sys_sigprocmask, 312 NULL, // sys_create_module 313 NULL, // sys_init_module 314 NULL, // sys_delete_module 315 NULL, // sys_get_kernel_syms /* 130 */ 316 NULL, // sys_quotactl 317 sys_getpgid, 318 sys_fchdir, 319 NULL, // sys_bdflush 320 NULL, // sys_sysfs /* 135 */ 321 sys_personality, 322 NULL, /* afs_syscall (-ENOSYS) */ 323 sys_setfsuid, 324 sys_setfsgid, 325 sys_llseek, /* 140 */ 326 sys_getdents, 327 sys_select, 328 sys_flock, 329 NULL, // sys_msync 330 NULL, // sys_readv /* 145 */ 331 NULL, // sys_writev 332 sys_getsid, 333 sys_fdatasync, 334 NULL, // sys_sysctl 335 NULL, // sys_mlock /* 150 */ 336 NULL, // sys_munlock 337 NULL, // sys_mlockall 338 NULL, // sys_munlockall 339 NULL, // sys_sched_setparam 340 NULL, // sys_sched_getparam /* 155 */ 341 NULL, // sys_sched_setscheduler 342 NULL, // sys_sched_getscheduler 343 NULL, // sys_sched_yield 344 NULL, // sys_sched_get_priority_max 345 NULL, // sys_sched_get_priority_min /* 160 */ 346 NULL, // sys_sched_rr_get_interval 347 sys_nanosleep, 348 NULL, // sys_mremap 349 350 NULL, 351 NULL, 352 NULL, 353 NULL, 354 NULL, 355 NULL, 356 NULL, 357 NULL, 358 NULL, 359 NULL, 360 NULL, 361 NULL, 362 NULL, 363 NULL, 364 NULL, 365 NULL, 366 NULL, 367 NULL, 368 sys_chown, /* 182 */ 369 sys_getcwd, 370 NULL, 371 NULL, 372 NULL, 373 NULL, 374 NULL, 375 NULL, 376 sys_fork, /* 190 (sys_vfork) */ 377 }; 378 379 static void do_bad_syscall(unsigned int num) 380 { 381 #ifdef __DEBUG__ 382 printk("***** (pid %d) system call %d not supported yet *****\n", current->pid, num); 383 #endif /*__DEBUG__ */ 384 } 385 386 /* 387 * The argument 'struct sigcontext' is needed because there are some system 388 * calls (such as sys_iopl and sys_fork) that need to get information from 389 * certain registers (EFLAGS and ESP). The rest of system calls will ignore 390 * such extra argument. 391 */ 392 int do_syscall(unsigned int num, int arg1, int arg2, int arg3, int arg4, int arg5, struct sigcontext sc) 393 { 394 int (*sys_func)(int, ...); 395 396 if(num > NR_SYSCALLS) { 397 do_bad_syscall(num); 398 return -ENOSYS; 399 } 400 sys_func = syscall_table[num]; 401 if(!sys_func) { 402 do_bad_syscall(num); 403 return -ENOSYS; 404 } 405 current->sp = (unsigned int)≻ 406 return sys_func(arg1, arg2, arg3, arg4, arg5, &sc); 407 }