libprop is currently using a recursive parser. While this is fine for

userland, deeply nested arrays and dictionaries can easily overflow
the kernel stack and thereby force a panic.

Fix the internalizer and prop_object_release to use a separate call
stack and alter the dictionary and array handling to not recurse on
the C stack. The default stack has an inline depth of 16 elements,
which should keep the overhead reasonable.

This issue was found by Pavel Cahyna and Jachym Holecek.

Additionally add a limit for prop_object_copyin_ioctl to prevent user
programs from temporary allocating unbound amount of kernel memory.
Allow malloc to fail so that tight loops of userland processes can't
force panics by exhausting the kernel map.

Tested with the sample exploit of Jachym, his test suite and reviewed
by himself (initial patch), Christos Zoulas and Jason Thorpe.
This commit is contained in:
joerg 2007-08-16 21:44:06 +00:00
parent 8657b11ebd
commit e835604c26
12 changed files with 728 additions and 249 deletions

View File

@ -1,9 +1,9 @@
# $NetBSD: Makefile.inc,v 1.5 2006/10/26 05:02:12 thorpej Exp $
# $NetBSD: Makefile.inc,v 1.6 2007/08/16 21:44:06 joerg Exp $
.PATH: ${.PARSEDIR}
SRCS+= prop_array.c prop_bool.c prop_data.c prop_dictionary.c \
prop_dictionary_util.c prop_ingest.c prop_kern.c prop_number.c \
prop_object.c prop_string.c
prop_object.c prop_stack.c prop_string.c
SRCS+= prop_rb.c

View File

@ -1,7 +1,7 @@
/* $NetBSD: prop_array.c,v 1.9 2007/08/16 16:28:17 thorpej Exp $ */
/* $NetBSD: prop_array.c,v 1.10 2007/08/16 21:44:07 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -60,7 +60,8 @@ _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay")
_PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array",
"property array container object")
static void _prop_array_free(void *);
static int _prop_array_free(prop_stack_t, prop_object_t *);
static void _prop_array_emergency_free(prop_object_t);
static bool _prop_array_externalize(
struct _prop_object_externalize_context *,
void *);
@ -69,6 +70,7 @@ static bool _prop_array_equals(void *, void *);
static const struct _prop_object_type _prop_object_type_array = {
.pot_type = PROP_TYPE_ARRAY,
.pot_free = _prop_array_free,
.pot_emergency_free = _prop_array_emergency_free,
.pot_extern = _prop_array_externalize,
.pot_equals = _prop_array_equals,
};
@ -85,29 +87,58 @@ struct _prop_array_iterator {
#define EXPAND_STEP 16
static void
_prop_array_free(void *v)
static int
_prop_array_free(prop_stack_t stack, prop_object_t *obj)
{
prop_array_t pa = v;
prop_array_t pa = *obj;
prop_object_t po;
unsigned int idx;
_PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
_PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) ||
(pa->pa_capacity != 0 && pa->pa_array != NULL));
for (idx = 0; idx < pa->pa_count; idx++) {
po = pa->pa_array[idx];
_PROP_ASSERT(po != NULL);
prop_object_release(po);
}
/* The easy case is an empty array, just free and return. */
if (pa->pa_count == 0) {
if (pa->pa_array != NULL)
_PROP_FREE(pa->pa_array, M_PROP_ARRAY);
_PROP_RWLOCK_DESTROY(pa->pa_rwlock);
_PROP_POOL_PUT(_prop_array_pool, pa);
return (_PROP_OBJECT_FREE_DONE);
}
po = pa->pa_array[pa->pa_count - 1];
_PROP_ASSERT(po != NULL);
if (stack == NULL) {
/*
* If we are in emergency release mode,
* just let caller recurse down.
*/
*obj = po;
return (_PROP_OBJECT_FREE_FAILED);
}
/* Otherwise, try to push the current object on the stack. */
if (!_prop_stack_push(stack, pa, NULL, NULL)) {
/* Push failed, entering emergency release mode. */
return (_PROP_OBJECT_FREE_FAILED);
}
/* Object pushed on stack, caller will release it. */
--pa->pa_count;
*obj = po;
return (_PROP_OBJECT_FREE_RECURSE);
}
static void
_prop_array_emergency_free(prop_object_t obj)
{
prop_array_t pa = obj;
_PROP_ASSERT(pa->pa_count != 0);
--pa->pa_count;
}
static bool
@ -684,24 +715,70 @@ prop_array_externalize(prop_array_t pa)
* Parse an <array>...</array> and return the object created from the
* external representation.
*/
prop_object_t
_prop_array_internalize(struct _prop_object_internalize_context *ctx)
{
prop_array_t array;
prop_object_t obj;
static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
bool
_prop_array_internalize(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
/* We don't currently understand any attributes. */
if (ctx->poic_tagattr != NULL)
return (NULL);
return (true);
array = prop_array_create();
if (array == NULL)
return (NULL);
*obj = prop_array_create();
/*
* We are done if the create failed or no child elements exist.
*/
if (*obj == NULL || ctx->poic_is_empty_element)
return (true);
if (ctx->poic_is_empty_element)
return (array);
/*
* Opening tag is found, now continue to the first element.
*/
return (_prop_array_internalize_body(stack, obj, ctx));
}
static bool
_prop_array_internalize_continue(prop_stack_t stack,
prop_object_t *obj,
struct _prop_object_internalize_context *ctx,
void *data, prop_object_t child)
{
prop_array_t array;
_PROP_ASSERT(data == NULL);
if (child == NULL)
goto bad; /* Element could not be parsed. */
array = *obj;
if (prop_array_add(array, child) == false) {
prop_object_release(child);
goto bad;
}
prop_object_release(child);
/*
* Current element is processed and added, look for next.
*/
return (_prop_array_internalize_body(stack, obj, ctx));
bad:
prop_object_release(*obj);
*obj = NULL;
return (true);
}
static bool
_prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
prop_array_t array = *obj;
_PROP_ASSERT(array != NULL);
for (;;) {
/* Fetch the next tag. */
if (_prop_object_internalize_find_tag(ctx, NULL,
_PROP_TAG_TYPE_EITHER) == false)
@ -709,26 +786,19 @@ _prop_array_internalize(struct _prop_object_internalize_context *ctx)
/* Check to see if this is the end of the array. */
if (_PROP_TAG_MATCH(ctx, "array") &&
ctx->poic_tag_type == _PROP_TAG_TYPE_END)
break;
/* Fetch the object. */
obj = _prop_object_internalize_by_tag(ctx);
if (obj == NULL)
goto bad;
if (prop_array_add(array, obj) == false) {
prop_object_release(obj);
goto bad;
}
prop_object_release(obj);
ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
/* It is, so don't iterate any further. */
return (true);
}
return (array);
if (_prop_stack_push(stack, array, NULL,
_prop_array_internalize_continue))
return (false);
bad:
prop_object_release(array);
return (NULL);
*obj = NULL;
return (true);
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: prop_bool.c,v 1.10 2007/08/16 16:28:17 thorpej Exp $ */
/* $NetBSD: prop_bool.c,v 1.11 2007/08/16 21:44:07 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@ -50,7 +50,7 @@ static struct _prop_bool _prop_bool_false;
_PROP_MUTEX_DECL_STATIC(_prop_bool_initialized_mutex)
static bool _prop_bool_initialized;
static void _prop_bool_free(void *);
static int _prop_bool_free(prop_stack_t, prop_object_t *);
static bool _prop_bool_externalize(
struct _prop_object_externalize_context *,
void *);
@ -66,16 +66,17 @@ static const struct _prop_object_type _prop_object_type_bool = {
#define prop_object_is_bool(x) \
((x) != NULL && (x)->pb_obj.po_type == &_prop_object_type_bool)
static void
/* ARGSUSED */
_prop_bool_free(void *v _PROP_ARG_UNUSED)
static int
_prop_bool_free(prop_stack_t stack, prop_object_t *obj)
{
/*
* This should never happen as we "leak" our initial reference
* count.
*/
/* XXX forced assertion failure? */
return (_PROP_OBJECT_FREE_DONE);
}
static bool
@ -193,15 +194,18 @@ prop_bool_equals(prop_bool_t b1, prop_bool_t b2)
* Parse a <true/> or <false/> and return the object created from
* the external representation.
*/
prop_object_t
_prop_bool_internalize(struct _prop_object_internalize_context *ctx)
/* ARGSUSED */
bool
_prop_bool_internalize(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
bool val;
/* No attributes, and it must be an empty element. */
if (ctx->poic_tagattr != NULL ||
ctx->poic_is_empty_element == false)
return (NULL);
return (true);
if (_PROP_TAG_MATCH(ctx, "true"))
val = true;
@ -209,6 +213,6 @@ _prop_bool_internalize(struct _prop_object_internalize_context *ctx)
_PROP_ASSERT(_PROP_TAG_MATCH(ctx, "false"));
val = false;
}
return (prop_bool_create(val));
*obj = prop_bool_create(val);
return (true);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: prop_data.c,v 1.7 2007/08/16 16:28:17 thorpej Exp $ */
/* $NetBSD: prop_data.c,v 1.8 2007/08/16 21:44:07 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@ -69,7 +69,7 @@ _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
_PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
"property data container object")
static void _prop_data_free(void *);
static int _prop_data_free(prop_stack_t, prop_object_t *);
static bool _prop_data_externalize(
struct _prop_object_externalize_context *,
void *);
@ -85,14 +85,17 @@ static const struct _prop_object_type _prop_object_type_data = {
#define prop_object_is_data(x) \
((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
static void
_prop_data_free(void *v)
/* ARGSUSED */
static int
_prop_data_free(prop_stack_t stack, prop_object_t *obj)
{
prop_data_t pd = v;
prop_data_t pd = *obj;
if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
_PROP_FREE(pd->pd_mutable, M_PROP_DATA);
_PROP_POOL_PUT(_prop_data_pool, v);
_PROP_POOL_PUT(_prop_data_pool, pd);
return (_PROP_OBJECT_FREE_DONE);
}
static const char _prop_data_base64[] =
@ -228,7 +231,7 @@ prop_data_create_data(const void *v, size_t size)
if (pd != NULL) {
nv = _PROP_MALLOC(size, M_PROP_DATA);
if (nv == NULL) {
_prop_data_free(pd);
prop_object_release(pd);
return (NULL);
}
memcpy(nv, v, size);
@ -279,7 +282,7 @@ prop_data_copy(prop_data_t opd)
else if (opd->pd_size != 0) {
void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA);
if (nv == NULL) {
_prop_data_free(pd);
prop_object_release(pd);
return (NULL);
}
memcpy(nv, opd->pd_immutable, opd->pd_size);
@ -530,8 +533,14 @@ _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
* Parse a <data>...</data> and return the object created from the
* external representation.
*/
prop_object_t
_prop_data_internalize(struct _prop_object_internalize_context *ctx)
/* strtoul is used for parsing, enforce. */
typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
/* ARGSUSED */
bool
_prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
prop_data_t data;
uint8_t *buf;
@ -539,7 +548,7 @@ _prop_data_internalize(struct _prop_object_internalize_context *ctx)
/* We don't accept empty elements. */
if (ctx->poic_is_empty_element)
return (NULL);
return (true);
/*
* If we got a "size" attribute, get the size of the data blob
@ -550,23 +559,22 @@ _prop_data_internalize(struct _prop_object_internalize_context *ctx)
if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
ctx->poic_tagattrval_len == 0)
return (NULL);
return (true);
#ifndef _KERNEL
errno = 0;
#endif
/* XXX Assumes size_t and unsigned long are the same size. */
len = strtoul(ctx->poic_tagattrval, &cp, 0);
#ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */
if (len == ULONG_MAX && errno == ERANGE)
return (NULL);
return (true);
#endif
if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
return (NULL);
return (true);
_PROP_ASSERT(*cp == '\"');
} else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
NULL) == false)
return (NULL);
return (true);
/*
* Always allocate one extra in case we don't land on an even byte
@ -574,32 +582,33 @@ _prop_data_internalize(struct _prop_object_internalize_context *ctx)
*/
buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
if (buf == NULL)
return (NULL);
return (true);
if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
&ctx->poic_cp) == false) {
_PROP_FREE(buf, M_PROP_DATA);
return (NULL);
return (true);
}
if (alen != len) {
_PROP_FREE(buf, M_PROP_DATA);
return (NULL);
return (true);
}
if (_prop_object_internalize_find_tag(ctx, "data",
_PROP_TAG_TYPE_END) == false) {
_PROP_FREE(buf, M_PROP_DATA);
return (NULL);
return (true);
}
data = _prop_data_alloc();
if (data == NULL) {
_PROP_FREE(buf, M_PROP_DATA);
return (NULL);
return (true);
}
data->pd_mutable = buf;
data->pd_size = len;
return (data);
*obj = data;
return (true);
}

View File

@ -1,7 +1,7 @@
/* $NetBSD: prop_dictionary.c,v 1.18 2007/08/16 16:28:17 thorpej Exp $ */
/* $NetBSD: prop_dictionary.c,v 1.19 2007/08/16 21:44:07 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -111,7 +111,8 @@ _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
_PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
"property dictionary container object")
static void _prop_dictionary_free(void *);
static int _prop_dictionary_free(prop_stack_t, prop_object_t *);
static void _prop_dictionary_emergency_free(prop_object_t);
static bool _prop_dictionary_externalize(
struct _prop_object_externalize_context *,
void *);
@ -120,11 +121,12 @@ static bool _prop_dictionary_equals(void *, void *);
static const struct _prop_object_type _prop_object_type_dictionary = {
.pot_type = PROP_TYPE_DICTIONARY,
.pot_free = _prop_dictionary_free,
.pot_emergency_free = _prop_dictionary_emergency_free,
.pot_extern = _prop_dictionary_externalize,
.pot_equals = _prop_dictionary_equals,
};
static void _prop_dict_keysym_free(void *);
static int _prop_dict_keysym_free(prop_stack_t, prop_object_t *);
static bool _prop_dict_keysym_externalize(
struct _prop_object_externalize_context *,
void *);
@ -200,16 +202,19 @@ _prop_dict_keysym_put(prop_dictionary_keysym_t pdk)
}
}
static void
_prop_dict_keysym_free(void *v)
/* ARGSUSED */
static int
_prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj)
{
prop_dictionary_keysym_t pdk = v;
prop_dictionary_keysym_t pdk = *obj;
_PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
_prop_rb_tree_remove_node(&_prop_dict_keysym_tree, &pdk->pdk_link);
_PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
_prop_dict_keysym_put(pdk);
return _PROP_OBJECT_FREE_DONE;
}
static bool
@ -316,33 +321,69 @@ _prop_dict_keysym_alloc(const char *key)
return (pdk);
}
static void
_prop_dictionary_free(void *v)
int dont_free = 1;
static int
_prop_dictionary_free(prop_stack_t stack, prop_object_t *obj)
{
prop_dictionary_t pd = v;
prop_dictionary_t pd = *obj;
prop_dictionary_keysym_t pdk;
prop_object_t po;
unsigned int idx;
_PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
_PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
(pd->pd_capacity != 0 && pd->pd_array != NULL));
for (idx = 0; idx < pd->pd_count; idx++) {
pdk = pd->pd_array[idx].pde_key;
_PROP_ASSERT(pdk != NULL);
prop_object_release(pdk);
po = pd->pd_array[idx].pde_objref;
_PROP_ASSERT(po != NULL);
prop_object_release(po);
}
/* The empty dictorinary is easy, handle that first. */
if (pd->pd_count == 0) {
if (pd->pd_array != NULL)
_PROP_FREE(pd->pd_array, M_PROP_DICT);
_PROP_RWLOCK_DESTROY(pd->pd_rwlock);
_PROP_POOL_PUT(_prop_dictionary_pool, pd);
return (_PROP_OBJECT_FREE_DONE);
}
po = pd->pd_array[pd->pd_count - 1].pde_objref;
_PROP_ASSERT(po != NULL);
if (stack == NULL) {
/*
* If we are in emergency release mode,
* just let caller recurse down.
*/
*obj = po;
return (_PROP_OBJECT_FREE_FAILED);
}
/* Otherwise, try to push the current object on the stack. */
if (!_prop_stack_push(stack, pd, NULL, NULL)) {
/* Push failed, entering emergency release mode. */
return (_PROP_OBJECT_FREE_FAILED);
}
/* Object pushed on stack, caller will release it. */
--pd->pd_count;
pdk = pd->pd_array[pd->pd_count].pde_key;
_PROP_ASSERT(pdk != NULL);
prop_object_release(pdk);
*obj = po;
return (_PROP_OBJECT_FREE_RECURSE);
}
static void
_prop_dictionary_emergency_free(prop_object_t obj)
{
prop_dictionary_t pd = obj;
prop_dictionary_keysym_t pdk;
_PROP_ASSERT(pd->pd_count != 0);
--pd->pd_count;
pdk = pd->pd_array[pd->pd_count].pde_key;
_PROP_ASSERT(pdk != NULL);
prop_object_release(pdk);
}
static bool
@ -1064,40 +1105,97 @@ prop_dictionary_externalize(prop_dictionary_t pd)
* _prop_dictionary_internalize --
* Parse a <dict>...</dict> and return the object created from the
* external representation.
*
* Internal state in via rec_data is the storage area for the last processed
* key.
* _prop_dictionary_internalize_body is the upper half of the parse loop.
* It is responsible for parsing the key directly and storing it in the area
* referenced by rec_data.
* _prop_dictionary_internalize_cont is the lower half and called with the value
* associated with the key.
*/
prop_object_t
_prop_dictionary_internalize(struct _prop_object_internalize_context *ctx)
static bool _prop_dictionary_internalize_body(prop_stack_t,
prop_object_t *, struct _prop_object_internalize_context *, char *);
bool
_prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
prop_dictionary_t dict;
prop_object_t val;
size_t keylen;
char *tmpkey;
/* We don't currently understand any attributes. */
if (ctx->poic_tagattr != NULL)
return (NULL);
return (true);
dict = prop_dictionary_create();
if (dict == NULL)
return (NULL);
return (true);
if (ctx->poic_is_empty_element)
return (dict);
if (ctx->poic_is_empty_element) {
*obj = dict;
return (true);
}
tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
if (tmpkey == NULL)
goto bad;
if (tmpkey == NULL) {
prop_object_release(dict);
return (true);
}
*obj = dict;
/*
* Opening tag is found, storage for key allocated and
* now continue to the first element.
*/
return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
}
static bool
_prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
{
prop_dictionary_t dict = *obj;
char *tmpkey = data;
_PROP_ASSERT(tmpkey != NULL);
if (child == NULL ||
prop_dictionary_set(dict, tmpkey, child) == false) {
_PROP_FREE(tmpkey, M_TEMP);
if (child != NULL)
prop_object_release(child);
prop_object_release(dict);
*obj = NULL;
return (true);
}
prop_object_release(child);
/*
* key, value was added, now continue looking for the next key
* or the closing tag.
*/
return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
}
static bool
_prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx, char *tmpkey)
{
prop_dictionary_t dict = *obj;
size_t keylen;
for (;;) {
/* Fetch the next tag. */
if (_prop_object_internalize_find_tag(ctx, NULL,
_PROP_TAG_TYPE_EITHER) == false)
if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
goto bad;
/* Check to see if this is the end of the dictionary. */
if (_PROP_TAG_MATCH(ctx, "dict") &&
ctx->poic_tag_type == _PROP_TAG_TYPE_END)
break;
ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
_PROP_FREE(tmpkey, M_TEMP);
return (true);
}
/* Ok, it must be a non-empty key start tag. */
if (!_PROP_TAG_MATCH(ctx, "key") ||
@ -1122,25 +1220,18 @@ _prop_dictionary_internalize(struct _prop_object_internalize_context *ctx)
_PROP_TAG_TYPE_START) == false)
goto bad;
val = _prop_object_internalize_by_tag(ctx);
if (val == NULL)
goto bad;
if (prop_dictionary_set(dict, tmpkey, val) == false) {
prop_object_release(val);
goto bad;
}
prop_object_release(val);
}
_PROP_FREE(tmpkey, M_TEMP);
return (dict);
/*
* Key is found, now wait for value to be parsed.
*/
if (_prop_stack_push(stack, *obj, tmpkey,
_prop_dictionary_internalize_continue))
return (false);
bad:
if (tmpkey != NULL)
_PROP_FREE(tmpkey, M_TEMP);
prop_object_release(dict);
return (NULL);
*obj = NULL;
return (true);
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: prop_kern.c,v 1.7 2006/11/28 18:36:26 cube Exp $ */
/* $NetBSD: prop_kern.c,v 1.8 2007/08/16 21:44:07 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@ -234,6 +234,9 @@ prop_dictionary_sendrecv_ioctl(prop_dictionary_t dict, int fd,
#include <uvm/uvm.h>
/* Arbitrary limit ioctl input to 64KB */
unsigned int prop_object_copyin_limit = 65536;
static int
_prop_object_copyin_ioctl(const struct plistref *pref, const prop_type_t type,
const u_long cmd, prop_object_t *objp)
@ -247,9 +250,12 @@ _prop_object_copyin_ioctl(const struct plistref *pref, const prop_type_t type,
/*
* Allocate an extra byte so we can guarantee NUL-termination.
* XXX Some sanity check on the size?
*
* Allow malloc to fail in case pmap would be exhausted.
*/
buf = malloc(pref->pref_len + 1, M_TEMP, M_WAITOK);
buf = malloc(pref->pref_len + 1, M_TEMP, M_WAITOK | M_CANFAIL);
if (buf == NULL)
return (ENOMEM);
error = copyin(pref->pref_plist, buf, pref->pref_len);
if (error) {
free(buf, M_TEMP);

View File

@ -1,4 +1,4 @@
/* $NetBSD: prop_number.c,v 1.12 2007/08/16 16:28:18 thorpej Exp $ */
/* $NetBSD: prop_number.c,v 1.13 2007/08/16 21:44:07 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@ -71,7 +71,7 @@ struct _prop_number {
_PROP_POOL_INIT(_prop_number_pool, sizeof(struct _prop_number), "propnmbr")
static void _prop_number_free(void *);
static int _prop_number_free(prop_stack_t, prop_object_t *);
static bool _prop_number_externalize(
struct _prop_object_externalize_context *,
void *);
@ -149,16 +149,19 @@ static bool _prop_number_tree_initialized;
_PROP_MUTEX_DECL_STATIC(_prop_number_tree_mutex)
static void
_prop_number_free(void *v)
/* ARGSUSED */
static int
_prop_number_free(prop_stack_t stack, prop_object_t *obj)
{
prop_number_t pn = v;
prop_number_t pn = *obj;
_PROP_MUTEX_LOCK(_prop_number_tree_mutex);
_prop_rb_tree_remove_node(&_prop_number_tree, &pn->pn_link);
_PROP_MUTEX_UNLOCK(_prop_number_tree_mutex);
_PROP_POOL_PUT(_prop_number_pool, pn);
return (_PROP_OBJECT_FREE_DONE);
}
static bool
@ -528,8 +531,10 @@ _prop_number_internalize_signed(struct _prop_object_internalize_context *ctx,
* Parse a <number>...</number> and return the object created from
* the external representation.
*/
prop_object_t
_prop_number_internalize(struct _prop_object_internalize_context *ctx)
/* ARGSUSED */
bool
_prop_number_internalize(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
struct _prop_number_value pnv;
@ -537,7 +542,7 @@ _prop_number_internalize(struct _prop_object_internalize_context *ctx)
/* No attributes, no empty elements. */
if (ctx->poic_tagattr != NULL || ctx->poic_is_empty_element)
return (NULL);
return (true);
/*
* If the first character is '-', then we treat as signed.
@ -548,19 +553,20 @@ _prop_number_internalize(struct _prop_object_internalize_context *ctx)
*/
if (ctx->poic_cp[0] == '-') {
if (_prop_number_internalize_signed(ctx, &pnv) == false)
return (NULL);
return (true);
} else if (ctx->poic_cp[0] == '0' && ctx->poic_cp[1] == 'x') {
if (_prop_number_internalize_unsigned(ctx, &pnv) == false)
return (NULL);
return (true);
} else {
if (_prop_number_internalize_signed(ctx, &pnv) == false &&
_prop_number_internalize_unsigned(ctx, &pnv) == false)
return (NULL);
return (true);
}
if (_prop_object_internalize_find_tag(ctx, "integer",
_PROP_TAG_TYPE_END) == false)
return (NULL);
return (true);
return (_prop_number_alloc(&pnv));
*obj = _prop_number_alloc(&pnv);
return (true);
}

View File

@ -1,7 +1,7 @@
/* $NetBSD: prop_object.c,v 1.14 2007/08/16 16:28:18 thorpej Exp $ */
/* $NetBSD: prop_object.c,v 1.15 2007/08/16 21:44:07 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -588,8 +588,7 @@ _prop_object_internalize_match(const char *str1, size_t len1,
static const struct _prop_object_internalizer {
const char *poi_tag;
size_t poi_taglen;
prop_object_t (*poi_intern)(
struct _prop_object_internalize_context *);
prop_object_internalizer_t poi_intern;
} _prop_object_internalizer_table[] = {
INTERNALIZER("array", _prop_array_internalize),
@ -618,19 +617,46 @@ prop_object_t
_prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
{
const struct _prop_object_internalizer *poi;
prop_object_t obj, parent_obj;
void *data, *iter;
prop_object_internalizer_continue_t iter_func;
struct _prop_stack stack;
_prop_stack_init(&stack);
match_start:
for (poi = _prop_object_internalizer_table;
poi->poi_tag != NULL; poi++) {
if (_prop_object_internalize_match(ctx->poic_tagname,
ctx->poic_tagname_len,
poi->poi_tag,
poi->poi_taglen))
return ((*poi->poi_intern)(ctx));
break;
}
if (poi == NULL) {
while (_prop_stack_pop(&stack, &obj, &data, &iter)) {
iter_func = (prop_object_internalizer_continue_t)iter;
(*iter_func)(&stack, &obj, ctx, data, NULL);
}
return (NULL);
}
obj = NULL;
if (!(*poi->poi_intern)(&stack, &obj, ctx))
goto match_start;
parent_obj = obj;
while (_prop_stack_pop(&stack, &parent_obj, &data, &iter)) {
iter_func = (prop_object_internalizer_continue_t)iter;
if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj))
goto match_start;
obj = parent_obj;
}
return (parent_obj);
}
prop_object_t
_prop_generic_internalize(const char *xml, const char *master_tag)
{
@ -643,7 +669,7 @@ _prop_generic_internalize(const char *xml, const char *master_tag)
/* We start with a <plist> tag. */
if (_prop_object_internalize_find_tag(ctx, "plist",
_PROP_TAG_TYPE_START) == FALSE)
_PROP_TAG_TYPE_START) == false)
goto out;
/* Plist elements cannot be empty. */
@ -661,7 +687,7 @@ _prop_generic_internalize(const char *xml, const char *master_tag)
/* Next we expect to see opening master_tag. */
if (_prop_object_internalize_find_tag(ctx, master_tag,
_PROP_TAG_TYPE_START) == FALSE)
_PROP_TAG_TYPE_START) == false)
goto out;
obj = _prop_object_internalize_by_tag(ctx);
@ -673,7 +699,7 @@ _prop_generic_internalize(const char *xml, const char *master_tag)
* Now we want </plist>.
*/
if (_prop_object_internalize_find_tag(ctx, "plist",
_PROP_TAG_TYPE_END) == FALSE) {
_PROP_TAG_TYPE_END) == false) {
prop_object_release(obj);
obj = NULL;
}
@ -990,6 +1016,49 @@ prop_object_retain(prop_object_t obj)
_PROP_ASSERT(ocnt != 0xffffffffU);
}
/*
* prop_object_release_emergency
* A direct free with prop_object_release failed.
* Walk down the tree until a leaf is found and
* free that. Do not recurse to avoid stack overflows.
*
* This is a slow edge condition, but necessary to
* guaranty that an object can always be freed.
*/
static void
prop_object_release_emergency(prop_object_t obj)
{
struct _prop_object *po;
prop_object_t parent = NULL;
uint32_t ocnt;
for (;;) {
po = obj;
_PROP_ASSERT(obj);
_PROP_REFCNT_LOCK();
ocnt = po->po_refcnt--;
_PROP_REFCNT_UNLOCK();
_PROP_ASSERT(ocnt != 0);
if (ocnt != 1)
break;
_PROP_ASSERT(po->po_type);
if ((po->po_type->pot_free)(NULL, &obj) == 0)
break;
parent = po;
_PROP_REFCNT_LOCK();
++po->po_refcnt;
_PROP_REFCNT_UNLOCK();
}
_PROP_ASSERT(parent);
/* One object was just freed. */
po = parent;
(*po->po_type->pot_emergency_free)(parent);
}
/*
* prop_object_release --
* Decrement the reference count on an object.
@ -1000,16 +1069,41 @@ prop_object_retain(prop_object_t obj)
void
prop_object_release(prop_object_t obj)
{
struct _prop_object *po = obj;
struct _prop_object *po;
struct _prop_stack stack;
void *dummy1, *dummy2;
int ret;
uint32_t ocnt;
_prop_stack_init(&stack);
do {
do {
po = obj;
_PROP_ASSERT(obj);
_PROP_REFCNT_LOCK();
ocnt = po->po_refcnt--;
_PROP_REFCNT_UNLOCK();
_PROP_ASSERT(ocnt != 0);
if (ocnt == 1)
(*po->po_type->pot_free)(po);
if (ocnt != 1) {
ret = 0;
break;
}
ret = (po->po_type->pot_free)(&stack, &obj);
if (ret == _PROP_OBJECT_FREE_DONE)
break;
_PROP_REFCNT_LOCK();
++po->po_refcnt;
_PROP_REFCNT_UNLOCK();
} while (ret == _PROP_OBJECT_FREE_RECURSE);
if (ret == _PROP_OBJECT_FREE_FAILED)
prop_object_release_emergency(obj);
} while (_prop_stack_pop(&stack, &obj, &dummy1, &dummy2));
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: prop_object_impl.h,v 1.16 2007/08/16 16:28:18 thorpej Exp $ */
/* $NetBSD: prop_object_impl.h,v 1.17 2007/08/16 21:44:08 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@ -45,6 +45,8 @@
#include <inttypes.h>
#endif
#include "prop_stack.h"
struct _prop_object_externalize_context {
char * poec_buf; /* string buffer */
size_t poec_capacity; /* capacity of buffer */
@ -103,6 +105,12 @@ struct _prop_object_internalize_context {
_prop_tag_type_t poic_tag_type;
};
enum {
_PROP_OBJECT_FREE_DONE,
_PROP_OBJECT_FREE_RECURSE,
_PROP_OBJECT_FREE_FAILED
};
#define _PROP_EOF(c) ((c) == '\0')
#define _PROP_ISSPACE(c) \
((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || \
@ -155,28 +163,41 @@ void _prop_object_internalize_unmap_file(
struct _prop_object_internalize_mapped_file *);
#endif /* !_KERNEL && !_STANDALONE */
typedef bool (*prop_object_internalizer_t)(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
typedef bool (*prop_object_internalizer_continue_t)(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *, void *, prop_object_t);
/* These are here because they're required by shared code. */
prop_object_t _prop_array_internalize(
bool _prop_array_internalize(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
prop_object_t _prop_bool_internalize(
bool _prop_bool_internalize(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
prop_object_t _prop_data_internalize(
bool _prop_data_internalize(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
prop_object_t _prop_dictionary_internalize(
bool _prop_dictionary_internalize(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
prop_object_t _prop_number_internalize(
bool _prop_number_internalize(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
prop_object_t _prop_string_internalize(
bool _prop_string_internalize(prop_stack_t, prop_object_t *,
struct _prop_object_internalize_context *);
struct _prop_object_type {
uint32_t pot_type; /* type indicator */
void (*pot_free)(void *); /* func to free object */
bool (*pot_extern) /* func to externalize object */
(struct _prop_object_externalize_context *,
/* type indicator */
uint32_t pot_type;
/* func to free object */
int (*pot_free)(prop_stack_t, prop_object_t *);
/*
* func to free the child returned by pot_free with stack == NULL.
*
* Must be implemented if pot_free can return non-0.
*/
void (*pot_emergency_free)(prop_object_t);
/* func to externalize object */
bool (*pot_extern)(struct _prop_object_externalize_context *,
void *);
bool (*pot_equals) /* func to test quality */
(void *, void *);
/* func to test quality */
bool (*pot_equals)(void *, void *);
};
struct _prop_object {

View File

@ -0,0 +1,104 @@
/* $NetBSD: prop_stack.c,v 1.1 2007/08/16 21:44:08 joerg Exp $ */
/*-
* Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "prop_stack.h"
#include "prop_object_impl.h"
void
_prop_stack_init(prop_stack_t stack)
{
stack->used_intern_elems = 0;
SLIST_INIT(&stack->extern_elems);
}
bool
_prop_stack_push(prop_stack_t stack, prop_object_t obj, void *obj_data, void *iter)
{
struct _prop_stack_extern_elem *eelem;
struct _prop_stack_intern_elem *ielem;
if (stack->used_intern_elems == PROP_STACK_INTERN_ELEMS) {
eelem = _PROP_MALLOC(sizeof(*eelem), M_TEMP);
if (eelem == NULL)
return false;
eelem->object = obj;
eelem->object_data = obj_data;
eelem->iterator = iter;
SLIST_INSERT_HEAD(&stack->extern_elems, eelem, stack_link);
return true;
}
_PROP_ASSERT(stack->used_intern_elems < PROP_STACK_INTERN_ELEMS);
_PROP_ASSERT(SLIST_EMPTY(&stack->extern_elems));
ielem = &stack->intern_elems[stack->used_intern_elems];
ielem->object = obj;
ielem->object_data = obj_data;
ielem->iterator = iter;
++stack->used_intern_elems;
return true;
}
bool
_prop_stack_pop(prop_stack_t stack, prop_object_t *obj, void **obj_data, void **iter)
{
struct _prop_stack_extern_elem *eelem;
struct _prop_stack_intern_elem *ielem;
if (stack->used_intern_elems == 0)
return false;
if ((eelem = SLIST_FIRST(&stack->extern_elems)) != NULL) {
_PROP_ASSERT(stack->used_intern_elems == PROP_STACK_INTERN_ELEMS);
SLIST_REMOVE_HEAD(&stack->extern_elems, stack_link);
*obj = eelem->object;
*obj_data = eelem->object_data;
*iter = eelem->iterator;
_PROP_FREE(eelem, M_TEMP);
return true;
}
--stack->used_intern_elems;
ielem = &stack->intern_elems[stack->used_intern_elems];
*obj = ielem->object;
*obj_data = ielem->object_data;
*iter = ielem->iterator;
return true;
}

View File

@ -0,0 +1,66 @@
/* $NetBSD: prop_stack.h,v 1.1 2007/08/16 21:44:08 joerg Exp $ */
/*-
* Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _PROP_STACK_H
#define _PROP_STACK_H
#include <sys/queue.h>
#include <prop/prop_object.h>
struct _prop_stack_intern_elem {
prop_object_t object;
void *object_data;
void *iterator;
};
struct _prop_stack_extern_elem {
SLIST_ENTRY(_prop_stack_extern_elem) stack_link;
prop_object_t object;
void *object_data;
void *iterator;
};
#define PROP_STACK_INTERN_ELEMS 16
struct _prop_stack {
struct _prop_stack_intern_elem intern_elems[PROP_STACK_INTERN_ELEMS];
size_t used_intern_elems;
SLIST_HEAD(, _prop_stack_extern_elem) extern_elems;
};
typedef struct _prop_stack *prop_stack_t;
void _prop_stack_init(prop_stack_t);
bool _prop_stack_push(prop_stack_t, prop_object_t, void *, void *);
bool _prop_stack_pop(prop_stack_t, prop_object_t *, void **, void **);
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: prop_string.c,v 1.7 2007/08/16 16:28:18 thorpej Exp $ */
/* $NetBSD: prop_string.c,v 1.8 2007/08/16 21:44:08 joerg Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@ -58,7 +58,7 @@ _PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng")
_PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string",
"property string container object")
static void _prop_string_free(void *);
static int _prop_string_free(prop_stack_t, prop_object_t *);
static bool _prop_string_externalize(
struct _prop_object_externalize_context *,
void *);
@ -75,14 +75,17 @@ static const struct _prop_object_type _prop_object_type_string = {
((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string)
#define prop_string_contents(x) ((x)->ps_immutable ? (x)->ps_immutable : "")
static void
_prop_string_free(void *v)
/* ARGSUSED */
static int
_prop_string_free(prop_stack_t stack, prop_object_t *obj)
{
prop_string_t ps = v;
prop_string_t ps = *obj;
if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL)
_PROP_FREE(ps->ps_mutable, M_PROP_STRING);
_PROP_POOL_PUT(_prop_string_pool, v);
_PROP_POOL_PUT(_prop_string_pool, ps);
return (_PROP_OBJECT_FREE_DONE);
}
static bool
@ -165,7 +168,7 @@ prop_string_create_cstring(const char *str)
len = strlen(str);
cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
if (cp == NULL) {
_prop_string_free(ps);
prop_object_release(ps);
return (NULL);
}
strcpy(cp, str);
@ -216,7 +219,7 @@ prop_string_copy(prop_string_t ops)
else {
char *cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING);
if (cp == NULL) {
_prop_string_free(ps);
prop_object_release(ps);
return (NULL);
}
strcpy(cp, prop_string_contents(ops));
@ -244,7 +247,7 @@ prop_string_copy_mutable(prop_string_t ops)
ps->ps_size = ops->ps_size;
cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING);
if (cp == NULL) {
_prop_string_free(ps);
prop_object_release(ps);
return (NULL);
}
strcpy(cp, prop_string_contents(ops));
@ -413,51 +416,56 @@ prop_string_equals_cstring(prop_string_t ps, const char *cp)
* Parse a <string>...</string> and return the object created from the
* external representation.
*/
prop_object_t
_prop_string_internalize(struct _prop_object_internalize_context *ctx)
/* ARGSUSED */
bool
_prop_string_internalize(prop_stack_t stack, prop_object_t *obj,
struct _prop_object_internalize_context *ctx)
{
prop_string_t string;
char *str;
size_t len, alen;
if (ctx->poic_is_empty_element)
return (prop_string_create());
if (ctx->poic_is_empty_element) {
*obj = prop_string_create();
return (true);
}
/* No attributes recognized here. */
if (ctx->poic_tagattr != NULL)
return (NULL);
return (true);
/* Compute the length of the result. */
if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
NULL) == false)
return (NULL);
return (true);
str = _PROP_MALLOC(len + 1, M_PROP_STRING);
if (str == NULL)
return (NULL);
return (true);
if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
&ctx->poic_cp) == false ||
alen != len) {
_PROP_FREE(str, M_PROP_STRING);
return (NULL);
return (true);
}
str[len] = '\0';
if (_prop_object_internalize_find_tag(ctx, "string",
_PROP_TAG_TYPE_END) == false) {
_PROP_FREE(str, M_PROP_STRING);
return (NULL);
return (true);
}
string = _prop_string_alloc();
if (string == NULL) {
_PROP_FREE(str, M_PROP_STRING);
return (NULL);
return (true);
}
string->ps_mutable = str;
string->ps_size = len;
*obj = string;
return (string);
return (true);
}