- ksem_proc_dtor: fix a use-after-free
- LOCK_ASSERT -> KASSERT - Use kmem(9)
This commit is contained in:
parent
29315a1540
commit
71d19c248a
|
@ -1,11 +1,11 @@
|
|||
/* $NetBSD: uipc_sem.c,v 1.20 2007/02/09 21:55:32 ad Exp $ */
|
||||
/* $NetBSD: uipc_sem.c,v 1.21 2007/06/15 18:27:13 ad Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 2003, 2007 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Jason R. Thorpe of Wasabi Systems, Inc.
|
||||
* by Jason R. Thorpe of Wasabi Systems, Inc, and by Andrew Doran.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -63,7 +63,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.20 2007/02/09 21:55:32 ad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.21 2007/06/15 18:27:13 ad Exp $");
|
||||
|
||||
#include "opt_posix.h"
|
||||
|
||||
|
@ -75,7 +75,7 @@ __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.20 2007/02/09 21:55:32 ad Exp $");
|
|||
#include <sys/ksem.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/kauth.h>
|
||||
|
||||
|
@ -106,8 +106,9 @@ struct ksem {
|
|||
LIST_ENTRY(ksem) ks_hash; /* hash list entry */
|
||||
kmutex_t ks_interlock; /* lock on this ksem */
|
||||
kcondvar_t ks_cv; /* condition variable */
|
||||
char *ks_name; /* if named, this is the name */
|
||||
unsigned int ks_ref; /* number of references */
|
||||
char *ks_name; /* if named, this is the name */
|
||||
size_t ks_namelen; /* length of name */
|
||||
mode_t ks_mode; /* protection bits */
|
||||
uid_t ks_uid; /* creator uid */
|
||||
gid_t ks_gid; /* creator gid */
|
||||
|
@ -149,7 +150,7 @@ static void
|
|||
ksem_free(struct ksem *ks)
|
||||
{
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
|
||||
/*
|
||||
* If the ksem is anonymous (or has been unlinked), then
|
||||
|
@ -165,7 +166,7 @@ ksem_free(struct ksem *ks)
|
|||
LIST_REMOVE(ks, ks_hash);
|
||||
mutex_exit(&ksem_mutex);
|
||||
|
||||
free(ks, M_SEM);
|
||||
kmem_free(ks, sizeof(*ks));
|
||||
return;
|
||||
}
|
||||
mutex_exit(&ks->ks_interlock);
|
||||
|
@ -175,17 +176,17 @@ static inline void
|
|||
ksem_addref(struct ksem *ks)
|
||||
{
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
ks->ks_ref++;
|
||||
KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */
|
||||
KASSERT(ks->ks_ref != 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ksem_delref(struct ksem *ks)
|
||||
{
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(ks->ks_ref != 0);
|
||||
if (--ks->ks_ref == 0) {
|
||||
ksem_free(ks);
|
||||
return;
|
||||
|
@ -198,7 +199,7 @@ ksem_proc_alloc(void)
|
|||
{
|
||||
struct ksem_proc *kp;
|
||||
|
||||
kp = malloc(sizeof(*kp), M_SEM, M_WAITOK);
|
||||
kp = kmem_alloc(sizeof(*kp), KM_SLEEP);
|
||||
rw_init(&kp->kp_lock);
|
||||
LIST_INIT(&kp->kp_ksems);
|
||||
|
||||
|
@ -217,13 +218,12 @@ ksem_proc_dtor(void *arg)
|
|||
LIST_REMOVE(ksr, ksr_list);
|
||||
mutex_enter(&ksr->ksr_ksem->ks_interlock);
|
||||
ksem_delref(ksr->ksr_ksem);
|
||||
mutex_exit(&ksr->ksr_ksem->ks_interlock);
|
||||
free(ksr, M_SEM);
|
||||
kmem_free(ksr, sizeof(*ksr));
|
||||
}
|
||||
|
||||
rw_exit(&kp->kp_lock);
|
||||
rw_destroy(&kp->kp_lock);
|
||||
free(kp, M_SEM);
|
||||
kmem_free(kp, sizeof(*kp));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -238,7 +238,7 @@ ksem_add_proc(struct proc *p, struct ksem *ks)
|
|||
proc_setspecific(p, ksem_specificdata_key, kp);
|
||||
}
|
||||
|
||||
ksr = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
|
||||
ksr = kmem_alloc(sizeof(*ksr), KM_SLEEP);
|
||||
ksr->ksr_ksem = ks;
|
||||
|
||||
rw_enter(&kp->kp_lock, RW_WRITER);
|
||||
|
@ -252,7 +252,7 @@ ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks)
|
|||
{
|
||||
struct ksem_ref *ksr;
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
|
||||
if (ksr->ksr_ksem == ks) {
|
||||
ksem_delref(ks);
|
||||
|
@ -271,7 +271,7 @@ ksem_perm(struct lwp *l, struct ksem *ks)
|
|||
{
|
||||
kauth_cred_t uc;
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
uc = l->l_cred;
|
||||
if ((kauth_cred_geteuid(uc) == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
|
||||
(kauth_cred_getegid(uc) == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
|
||||
|
@ -286,7 +286,7 @@ ksem_lookup_byid(semid_t id)
|
|||
{
|
||||
struct ksem *ks;
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ksem_mutex));
|
||||
KASSERT(mutex_owned(&ksem_mutex));
|
||||
LIST_FOREACH(ks, &ksem_hash[SEM_HASH(id)], ks_hash) {
|
||||
if (ks->ks_id == id)
|
||||
return ks;
|
||||
|
@ -299,7 +299,7 @@ ksem_lookup_byname(const char *name)
|
|||
{
|
||||
struct ksem *ks;
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ksem_mutex));
|
||||
KASSERT(mutex_owned(&ksem_mutex));
|
||||
LIST_FOREACH(ks, &ksem_head, ks_entry) {
|
||||
if (strcmp(ks->ks_name, name) == 0) {
|
||||
mutex_enter(&ks->ks_interlock);
|
||||
|
@ -320,19 +320,20 @@ ksem_create(struct lwp *l, const char *name, struct ksem **ksret,
|
|||
uc = l->l_cred;
|
||||
if (value > SEM_VALUE_MAX)
|
||||
return (EINVAL);
|
||||
ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
|
||||
ret = kmem_zalloc(sizeof(*ret), KM_SLEEP);
|
||||
if (name != NULL) {
|
||||
len = strlen(name);
|
||||
if (len > SEM_MAX_NAMELEN) {
|
||||
free(ret, M_SEM);
|
||||
kmem_free(ret, sizeof(*ret));
|
||||
return (ENAMETOOLONG);
|
||||
}
|
||||
/* name must start with a '/' but not contain one. */
|
||||
if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
|
||||
free(ret, M_SEM);
|
||||
kmem_free(ret, sizeof(*ret));
|
||||
return (EINVAL);
|
||||
}
|
||||
ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
|
||||
ret->ks_namelen = len + 1;
|
||||
ret->ks_name = kmem_alloc(ret->ks_namelen, KM_SLEEP);
|
||||
strlcpy(ret->ks_name, name, len + 1);
|
||||
} else
|
||||
ret->ks_name = NULL;
|
||||
|
@ -349,8 +350,8 @@ ksem_create(struct lwp *l, const char *name, struct ksem **ksret,
|
|||
if (nsems >= SEM_MAX) {
|
||||
mutex_exit(&ksem_mutex);
|
||||
if (ret->ks_name != NULL)
|
||||
free(ret->ks_name, M_SEM);
|
||||
free(ret, M_SEM);
|
||||
kmem_free(ret->ks_name, ret->ks_namelen);
|
||||
kmem_free(ret, sizeof(*ret));
|
||||
return (ENFILE);
|
||||
}
|
||||
nsems++;
|
||||
|
@ -450,7 +451,7 @@ do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
|
|||
* Verify permissions. If we can access it, add
|
||||
* this process's reference.
|
||||
*/
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
error = ksem_perm(l, ks);
|
||||
if (error == 0)
|
||||
ksem_addref(ks);
|
||||
|
@ -491,7 +492,7 @@ do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
|
|||
id = SEM_TO_ID(ksnew);
|
||||
error = (*docopyout)(&id, idp, sizeof(id));
|
||||
if (error) {
|
||||
free(ksnew->ks_name, M_SEM);
|
||||
kmem_free(ksnew->ks_name, ksnew->ks_namelen);
|
||||
ksnew->ks_name = NULL;
|
||||
|
||||
mutex_enter(&ksnew->ks_interlock);
|
||||
|
@ -509,7 +510,7 @@ do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
|
|||
mutex_exit(&ks->ks_interlock);
|
||||
mutex_exit(&ksem_mutex);
|
||||
|
||||
free(ksnew->ks_name, M_SEM);
|
||||
kmem_free(ksnew->ks_name, ksnew->ks_namelen);
|
||||
ksnew->ks_name = NULL;
|
||||
|
||||
mutex_enter(&ksnew->ks_interlock);
|
||||
|
@ -550,7 +551,7 @@ sys__ksem_unlink(struct lwp *l, void *v, register_t *retval)
|
|||
const char *name;
|
||||
} */ *uap = v;
|
||||
char name[SEM_MAX_NAMELEN + 1], *cp;
|
||||
size_t done;
|
||||
size_t done, len;
|
||||
struct ksem *ks;
|
||||
int error;
|
||||
|
||||
|
@ -565,10 +566,11 @@ sys__ksem_unlink(struct lwp *l, void *v, register_t *retval)
|
|||
return (ENOENT);
|
||||
}
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
|
||||
LIST_REMOVE(ks, ks_entry);
|
||||
cp = ks->ks_name;
|
||||
len = ks->ks_namelen;
|
||||
ks->ks_name = NULL;
|
||||
|
||||
mutex_exit(&ksem_mutex);
|
||||
|
@ -578,7 +580,7 @@ sys__ksem_unlink(struct lwp *l, void *v, register_t *retval)
|
|||
else
|
||||
mutex_exit(&ks->ks_interlock);
|
||||
|
||||
free(cp, M_SEM);
|
||||
kmem_free(cp, len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -605,7 +607,7 @@ sys__ksem_close(struct lwp *l, void *v, register_t *retval)
|
|||
return (EINVAL);
|
||||
}
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
if (ks->ks_name == NULL) {
|
||||
mutex_exit(&ks->ks_interlock);
|
||||
rw_exit(&kp->kp_lock);
|
||||
|
@ -614,7 +616,7 @@ sys__ksem_close(struct lwp *l, void *v, register_t *retval)
|
|||
|
||||
ksr = ksem_drop_proc(kp, ks);
|
||||
rw_exit(&kp->kp_lock);
|
||||
free(ksr, M_SEM);
|
||||
kmem_free(ksr, sizeof(*ksr));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -639,7 +641,7 @@ sys__ksem_post(struct lwp *l, void *v, register_t *retval)
|
|||
if (ks == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
if (ks->ks_value == SEM_VALUE_MAX) {
|
||||
error = EOVERFLOW;
|
||||
goto out;
|
||||
|
@ -670,7 +672,7 @@ ksem_wait(struct lwp *l, semid_t id, int tryflag)
|
|||
if (ks == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
ksem_addref(ks);
|
||||
while (ks->ks_value == 0) {
|
||||
ks->ks_waiters++;
|
||||
|
@ -730,7 +732,7 @@ sys__ksem_getvalue(struct lwp *l, void *v, register_t *retval)
|
|||
if (ks == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
val = ks->ks_value;
|
||||
mutex_exit(&ks->ks_interlock);
|
||||
|
||||
|
@ -759,7 +761,7 @@ sys__ksem_destroy(struct lwp *l, void *v, register_t *retval)
|
|||
return (EINVAL);
|
||||
}
|
||||
|
||||
LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
|
||||
KASSERT(mutex_owned(&ks->ks_interlock));
|
||||
|
||||
/*
|
||||
* XXX This misses named semaphores which have been unlink'd,
|
||||
|
@ -780,7 +782,7 @@ sys__ksem_destroy(struct lwp *l, void *v, register_t *retval)
|
|||
|
||||
ksr = ksem_drop_proc(kp, ks);
|
||||
rw_exit(&kp->kp_lock);
|
||||
free(ksr, M_SEM);
|
||||
kmem_free(ksr, sizeof(*ksr));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -801,7 +803,7 @@ ksem_forkhook(struct proc *p2, struct proc *p1)
|
|||
|
||||
if (!LIST_EMPTY(&kp1->kp_ksems)) {
|
||||
LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) {
|
||||
ksr1 = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
|
||||
ksr1 = kmem_alloc(sizeof(*ksr), KM_SLEEP);
|
||||
ksr1->ksr_ksem = ksr->ksr_ksem;
|
||||
mutex_enter(&ksr->ksr_ksem->ks_interlock);
|
||||
ksem_addref(ksr->ksr_ksem);
|
||||
|
|
Loading…
Reference in New Issue