diff options
author | netchild <netchild@FreeBSD.org> | 2006-12-02 14:56:25 +0000 |
---|---|---|
committer | netchild <netchild@FreeBSD.org> | 2006-12-02 14:56:25 +0000 |
commit | 2db40ccfce7ea070a08fa10f3969ee21f4d49779 (patch) | |
tree | 12cfdac707eb1c777faf6a99820a9d9176a76fdc | |
parent | 1cfd0ae28fc82197d5129459391224f42bc78cd5 (diff) | |
download | FreeBSD-src-2db40ccfce7ea070a08fa10f3969ee21f4d49779.zip FreeBSD-src-2db40ccfce7ea070a08fa10f3969ee21f4d49779.tar.gz |
MFP4 (108673, 110519, 110874):
- Currently LINUX_MAX_COMM_LEN is smaller than MAXCOMLEN, but in case
this will change we have a buffer overflow. Apply some defensive
programming to DTRT when this should happen.
- Use copyinstr() instead of copyin where appropriate.
* Fallback to copyin() in case of ENAMETOOLONG. [1]
* Use the right source and destination (it was wrong before).
- Use strlcpy instead of strcpy.
- Properly lock the read case (PR_GET_NAME) like the write case.
Reviewed by: rwatson (except [1])
Suggested by: rwatson [1]
-rw-r--r-- | sys/compat/linux/linux_misc.c | 35 |
1 files changed, 30 insertions, 5 deletions
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index c7ace44..10b0ad0 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -1588,7 +1588,7 @@ linux_exit_group(struct thread *td, struct linux_exit_group_args *args) int linux_prctl(struct thread *td, struct linux_prctl_args *args) { - int error = 0; + int error = 0, max_size; struct proc *p = td->td_proc; char comm[LINUX_MAX_COMM_LEN]; struct linux_emuldata *em; @@ -1615,16 +1615,41 @@ linux_prctl(struct thread *td, struct linux_prctl_args *args) EMUL_UNLOCK(&emul_lock); break; case LINUX_PR_SET_NAME: - comm[LINUX_MAX_COMM_LEN-1] = 0; - error = copyin(comm, (void *)(register_t) args->arg2, LINUX_MAX_COMM_LEN-1); + /* + * To be on the safe side we need to make sure to not + * overflow the size a linux program expects. We already + * do this here in the copyin, so that we don't need to + * check on copyout. + */ + max_size = MIN(sizeof(comm), sizeof(p->p_comm)); + error = copyinstr((void *)(register_t) args->arg2, comm, + max_size, NULL); + + /* Linux silently truncates the name if it is too long. */ + if (error == ENAMETOOLONG) { + /* + * XXX: copyinstr() isn't documented to populate the + * array completely, so do a copyin() to be on the + * safe side. This should be changed in case + * copyinstr() is changed to guarantee this. + */ + error = copyin((void *)(register_t)args->arg2, comm, + max_size - 1); + comm[max_size - 1] = '\0'; + } if (error) return (error); + PROC_LOCK(p); - strcpy(p->p_comm, comm); + strlcpy(p->p_comm, comm, sizeof(p->p_comm)); PROC_UNLOCK(p); break; case LINUX_PR_GET_NAME: - error = copyout(&p->p_comm, (void *)(register_t) args->arg2, MAXCOMLEN+1); + PROC_LOCK(p); + strlcpy(comm, p->p_comm, sizeof(comm)); + PROC_UNLOCK(p); + error = copyout(comm, (void *)(register_t) args->arg2, + strlen(comm)+1); break; default: error = EINVAL; |