diff -urPX nopatch linux-2.0.38/drivers/net/ppp.c linux/drivers/net/ppp.c --- linux-2.0.38/drivers/net/ppp.c Sun Jun 13 21:21:01 1999 +++ linux/drivers/net/ppp.c Fri Jan 21 09:17:58 2000 @@ -780,11 +780,16 @@ static int ppp_tty_open (struct tty_struct *tty) { - struct ppp *ppp = tty2ppp (tty); + struct ppp *ppp; int indx; + + if (!suser()) + return -EPERM; + /* * There should not be an existing table for this slot. */ + ppp = tty2ppp (tty); if (ppp) { if (ppp->flags & SC_DEBUG) printk (KERN_ERR diff -urPX nopatch linux-2.0.38/drivers/net/slip.c linux/drivers/net/slip.c --- linux-2.0.38/drivers/net/slip.c Tue Jul 14 00:47:31 1998 +++ linux/drivers/net/slip.c Fri Jan 21 09:17:58 2000 @@ -698,10 +698,14 @@ static int slip_open(struct tty_struct *tty) { - struct slip *sl = (struct slip *) tty->disc_data; + struct slip *sl; int err; + if (!suser()) + return -EPERM; + /* First make sure we're not already connected. */ + sl = (struct slip *) tty->disc_data; if (sl && sl->magic == SLIP_MAGIC) { return -EEXIST; } diff -urPX nopatch linux-2.0.38/fs/binfmt_elf.c linux/fs/binfmt_elf.c --- linux-2.0.38/fs/binfmt_elf.c Thu Jun 4 02:17:49 1998 +++ linux/fs/binfmt_elf.c Fri Jan 21 09:17:58 2000 @@ -197,28 +197,20 @@ interp_elf_ex->e_type != ET_DYN) || !elf_check_arch(interp_elf_ex->e_machine) || (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)) { + !interpreter_inode->i_op->default_file_ops->mmap) || + interp_elf_ex->e_phentsize != sizeof(struct elf_phdr) || + interp_elf_ex->e_phnum < 1 || + interp_elf_ex->e_phnum > PAGE_SIZE / sizeof(struct elf_phdr)) { return ~0UL; } /* Now read in all of the header information */ - if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) - return ~0UL; - elf_phdata = (struct elf_phdr *) kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL); if (!elf_phdata) return ~0UL; - /* - * If the size of this structure has changed, then punt, since - * we will be doing the wrong thing. - */ - if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) { - kfree(elf_phdata); - return ~0UL; - } retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); @@ -306,6 +298,11 @@ kfree(elf_phdata); *interp_load_addr = load_addr; + /* + * AUDIT: is everything deallocated properly if this happens + * to be ~0UL? We'd better switch to out-of-band error reporting. + * Also for a.out. + */ return ((unsigned long) interp_elf_ex->e_entry) + load_addr; } @@ -397,7 +394,10 @@ elf_ex.e_type != ET_DYN) || (!elf_check_arch(elf_ex.e_machine)) || (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)) { + !bprm->inode->i_op->default_file_ops->mmap) || + elf_ex.e_phentsize != sizeof(struct elf_phdr) || + elf_ex.e_phnum < 1 || + elf_ex.e_phnum > 0xfff0 / sizeof(struct elf_phdr)) { return -ENOEXEC; } /* Now read in all of the header information */ @@ -434,12 +434,14 @@ for (i = 0; i < elf_ex.e_phnum; i++) { if (elf_ppnt->p_type == PT_INTERP) { - if (elf_interpreter != NULL) { + if (elf_interpreter != NULL || + elf_ppnt->p_filesz < 2 || + elf_ppnt->p_filesz > PAGE_SIZE) { iput(interpreter_inode); kfree(elf_phdata); kfree(elf_interpreter); sys_close(elf_exec_fileno); - return -EINVAL; + return -ENOEXEC; } /* This is the program interpreter used for * shared libraries - for now assume that this @@ -456,6 +458,7 @@ retval = read_exec(bprm->inode, elf_ppnt->p_offset, elf_interpreter, elf_ppnt->p_filesz, 1); + elf_interpreter[elf_ppnt->p_filesz - 1] = 0; /* If the program interpreter is one of these two, then assume an iBCS2 image. Otherwise assume a native linux image. */ @@ -629,14 +632,17 @@ &interp_load_addr); iput(interpreter_inode); - kfree(elf_interpreter); if (elf_entry == ~0UL) { - printk("Unable to load interpreter\n"); + printk("Unable to load interpreter %.128s\n", + elf_interpreter); + kfree(elf_interpreter); kfree(elf_phdata); send_sig(SIGSEGV, current, 0); return 0; } + + kfree(elf_interpreter); } kfree(elf_phdata); diff -urPX nopatch linux-2.0.38/fs/exec.c linux/fs/exec.c --- linux-2.0.38/fs/exec.c Tue Jul 14 00:47:34 1998 +++ linux/fs/exec.c Fri Jan 21 09:17:58 2000 @@ -178,26 +178,51 @@ /* * count() counts the number of arguments/envelopes - * - * We also do some limited EFAULT checking: this isn't complete, but - * it does cover most cases. I'll have to do this correctly some day.. */ -static int count(char ** argv) +static int count(void *base, int size, int max) { int error, i = 0; - char ** tmp, *p; + void *tmp = base; + unsigned long length = 0, chunk = size, limit; + int grow = 1; + + if (!tmp) return 0; + + limit = PAGE_SIZE - ((unsigned long)tmp & (PAGE_SIZE - 1)); + error = verify_area(VERIFY_READ, tmp, limit); + if (error) limit = 0; + + do { + if (length >= limit) + do { + if (!grow) { + if (chunk <= sizeof(char *)) + return -EFAULT; + chunk >>= 1; + } + error = verify_area(VERIFY_READ, tmp, chunk); + if (error) grow = 0; else { + limit += chunk; + if (grow) chunk <<= 1; + } + } while (error); - if ((tmp = argv) != NULL) { - error = verify_area(VERIFY_READ, tmp, sizeof(char *)); - if (error) - return error; - while ((p = get_user(tmp++)) != NULL) { - i++; - error = verify_area(VERIFY_READ, p, 1); - if (error) - return error; + if (size == 1) { + do { + if (!get_user(((char *)tmp)++)) goto out; + if (++i > max) return -E2BIG; + } while (i < limit); + length = i; + } else { + do { + if (!get_user(((char **)tmp)++)) goto out; + if ((length += size) > max) return -E2BIG; + i++; + } while (length < limit); } - } + } while (1); + +out: return i; } @@ -221,12 +246,12 @@ unsigned long copy_strings(int argc,char ** argv,unsigned long *page, unsigned long p, int from_kmem) { - char *tmp, *tmp1, *pag = NULL; + char *tmp, *pag = NULL; int len, offset = 0; unsigned long old_fs, new_fs; - if (!p) - return 0; /* bullet-proofing */ + if ((long)p <= 0) + return p; /* bullet-proofing */ new_fs = get_ds(); old_fs = get_fs(); if (from_kmem==2) @@ -234,16 +259,16 @@ while (argc-- > 0) { if (from_kmem == 1) set_fs(new_fs); - if (!(tmp1 = tmp = get_user(argv+argc))) + if (!(tmp = get_user(argv+argc))) panic("VFS: argc is wrong"); if (from_kmem == 1) set_fs(old_fs); - while (get_user(tmp++)); - len = tmp - tmp1; - if (p < len) { /* this shouldn't happen - 128kB */ + len = count(tmp, 1, p); + if (len < 0 || len >= p) { /* EFAULT or E2BIG */ set_fs(old_fs); - return 0; + return len < 0 ? len : -E2BIG; } + tmp += ++len; while (len) { --p; --tmp; --len; if (--offset < 0) { @@ -253,7 +278,7 @@ if (!(pag = (char *) page[p/PAGE_SIZE]) && !(pag = (char *) page[p/PAGE_SIZE] = (unsigned long *) get_free_page(GFP_USER))) - return 0; + return -EFAULT; if (from_kmem==2) set_fs(new_fs); @@ -559,6 +584,27 @@ if (!suser()) return -EPERM; } + + /* + * Increment the privileged execution counter, so that our + * old children know not to send bad exit_signal's to us. + */ + if (!++current->priv) { + struct task_struct *p; + + /* + * The counter can't really overflow with real-world + * programs (and it has to be the privileged program + * itself that causes the overflow), but we handle + * this case anyway, just for correctness. + */ + for_each_task(p) { + if (p->p_pptr == current) { + p->ppriv = 0; + current->priv = 1; + } + } + } } memset(bprm->buf,0,sizeof(bprm->buf)); @@ -601,6 +647,8 @@ bprm->dont_iput = 1; remove_arg_zero(bprm); bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2); + if ((long)bprm->p < 0) + return (long)bprm->p; bprm->argc++; bprm->loader = bprm->p; retval = open_namei(dynloader[0], 0, 0, &bprm->inode, NULL); @@ -673,9 +721,9 @@ bprm.loader = 0; bprm.exec = 0; bprm.dont_iput = 0; - if ((bprm.argc = count(argv)) < 0) + if ((bprm.argc = count(argv, sizeof(char *), bprm.p)) < 0) return bprm.argc; - if ((bprm.envc = count(envp)) < 0) + if ((bprm.envc = count(envp, sizeof(char *), bprm.p)) < 0) return bprm.envc; retval = prepare_binprm(&bprm); @@ -685,8 +733,8 @@ bprm.exec = bprm.p; bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0); bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0); - if (!bprm.p) - retval = -E2BIG; + if ((long)bprm.p < 0) + retval = (long)bprm.p; } if(retval>=0) diff -urPX nopatch linux-2.0.38/fs/proc/array.c linux/fs/proc/array.c --- linux-2.0.38/fs/proc/array.c Sun Jun 13 21:21:03 1999 +++ linux/fs/proc/array.c Fri Jan 21 09:17:58 2000 @@ -610,6 +610,7 @@ vsize += vma->vm_end - vma->vm_start; vma = vma->vm_next; } + if ((current->fsuid == tsk->euid && tsk->dumpable) || suser()) if (tsk->kernel_stack_page) { eip = KSTK_EIP(tsk); esp = KSTK_ESP(tsk); @@ -1026,7 +1027,7 @@ case PROC_PID_CMDLINE: return 0; } - if(suser() || current->fsuid == (*p)->euid) + if ((current->fsuid == (*p)->euid && (*p)->dumpable) || suser()) return 0; return 1; } diff -urPX nopatch linux-2.0.38/fs/proc/fd.c linux/fs/proc/fd.c --- linux-2.0.38/fs/proc/fd.c Thu Nov 13 07:36:41 1997 +++ linux/fs/proc/fd.c Fri Jan 21 09:17:58 2000 @@ -86,6 +86,7 @@ } iput(dir); fd = 0; + if (len > 1 && *name == '0') fd = 0xfffff; else while (len-- > 0) { c = *name - '0'; name++; diff -urPX nopatch linux-2.0.38/fs/proc/root.c linux/fs/proc/root.c --- linux-2.0.38/fs/proc/root.c Tue Apr 30 14:09:45 1996 +++ linux/fs/proc/root.c Fri Jan 21 09:17:58 2000 @@ -466,6 +466,7 @@ } pid *= 10; pid += c; + if (!pid) break; if (pid & 0xffff0000) { pid = 0; break; diff -urPX nopatch linux-2.0.38/include/linux/sched.h linux/include/linux/sched.h --- linux-2.0.38/include/linux/sched.h Wed Dec 3 01:18:11 1997 +++ linux/include/linux/sched.h Fri Jan 21 09:17:58 2000 @@ -244,6 +244,8 @@ struct mm_struct *mm; /* signal handlers */ struct signal_struct *sig; +/* privileged execution counters, for exit_signal permission checking */ + int priv, ppriv; #ifdef __SMP__ int processor; int last_processor; @@ -309,6 +311,7 @@ /* files */ &init_files, \ /* mm */ &init_mm, \ /* signals */ &init_signals, \ +/* priv */ 0, 0, \ } extern struct mm_struct init_mm; diff -urPX nopatch linux-2.0.38/kernel/exit.c linux/kernel/exit.c --- linux-2.0.38/kernel/exit.c Thu Jun 4 02:17:50 1998 +++ linux/kernel/exit.c Fri Jan 21 09:17:58 2000 @@ -118,7 +118,8 @@ void notify_parent(struct task_struct * tsk, int signal) { - send_sig(signal, tsk->p_pptr, 1); + send_sig(signal, tsk->p_pptr, !signal || signal == SIGCHLD || + tsk->p_pptr->priv == tsk->ppriv); wake_up_interruptible(&tsk->p_pptr->wait_chldexit); } diff -urPX nopatch linux-2.0.38/kernel/fork.c linux/kernel/fork.c --- linux-2.0.38/kernel/fork.c Thu Jun 4 02:17:50 1998 +++ linux/kernel/fork.c Fri Jan 21 09:17:58 2000 @@ -228,10 +228,23 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) { int nr; - int error = -ENOMEM; + int error = -EINVAL; unsigned long new_stack; struct task_struct *p; +/* + * Disallow unknown clone(2) flags, as well as CLONE_PID, unless we are + * the boot up thread. + * + * Avoid taking any branches in the common case. + */ + if (clone_flags & + (-(signed long)current->pid >> (sizeof(long) * 8 - 1)) & + ~(unsigned long)(CSIGNAL | + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) + goto bad_fork; + + error = -ENOMEM; p = (struct task_struct *) kmalloc(sizeof(*p), GFP_KERNEL); if (!p) goto bad_fork; @@ -264,6 +277,8 @@ p->p_cptr = NULL; init_waitqueue(&p->wait_chldexit); p->signal = 0; + p->priv = 0; + p->ppriv = current->priv; p->it_real_value = p->it_virt_value = p->it_prof_value = 0; p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0; init_timer(&p->real_timer); diff -urPX nopatch linux-2.0.38/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c --- linux-2.0.38/net/ipv4/tcp_input.c Tue Sep 7 10:39:54 1999 +++ linux/net/ipv4/tcp_input.c Fri Jan 21 09:17:58 2000 @@ -1186,6 +1186,15 @@ * then we can probably ignore it. */ + if (sk->state == TCP_SYN_RECV) { + /* + * Should be the exact sequence number for the handshake + * to succeed, or sequence prediction gets a bit easier. + * Also, "partially-established" connections are bad for + * the rest of our code. + */ + if (ack != sk->sent_seq) goto uninteresting_ack; + } else if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq)) goto uninteresting_ack;