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:
parent
8657b11ebd
commit
e835604c26
@ -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
|
||||
|
@ -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,17 +60,19 @@ _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 *);
|
||||
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_extern = _prop_array_externalize,
|
||||
.pot_equals = _prop_array_equals,
|
||||
.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,
|
||||
};
|
||||
|
||||
#define prop_object_is_array(x) \
|
||||
@ -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);
|
||||
}
|
||||
|
||||
if (pa->pa_array != NULL)
|
||||
_PROP_FREE(pa->pa_array, M_PROP_ARRAY);
|
||||
po = pa->pa_array[pa->pa_count - 1];
|
||||
_PROP_ASSERT(po != NULL);
|
||||
|
||||
_PROP_RWLOCK_DESTROY(pa->pa_rwlock);
|
||||
if (stack == NULL) {
|
||||
/*
|
||||
* If we are in emergency release mode,
|
||||
* just let caller recurse down.
|
||||
*/
|
||||
*obj = po;
|
||||
return (_PROP_OBJECT_FREE_FAILED);
|
||||
}
|
||||
|
||||
_PROP_POOL_PUT(_prop_array_pool, pa);
|
||||
/* 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,51 +715,90 @@ 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);
|
||||
|
||||
if (ctx->poic_is_empty_element)
|
||||
return (array);
|
||||
|
||||
for (;;) {
|
||||
/* Fetch the next tag. */
|
||||
if (_prop_object_internalize_find_tag(ctx, NULL,
|
||||
_PROP_TAG_TYPE_EITHER) == false)
|
||||
goto bad;
|
||||
*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);
|
||||
|
||||
/* 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;
|
||||
/*
|
||||
* Opening tag is found, now continue to the first element.
|
||||
*/
|
||||
return (_prop_array_internalize_body(stack, obj, ctx));
|
||||
}
|
||||
|
||||
/* Fetch the object. */
|
||||
obj = _prop_object_internalize_by_tag(ctx);
|
||||
if (obj == NULL)
|
||||
goto bad;
|
||||
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;
|
||||
|
||||
if (prop_array_add(array, obj) == false) {
|
||||
prop_object_release(obj);
|
||||
goto bad;
|
||||
}
|
||||
prop_object_release(obj);
|
||||
_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);
|
||||
|
||||
/* Fetch the next tag. */
|
||||
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 array. */
|
||||
if (_PROP_TAG_MATCH(ctx, "array") &&
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
/* ARGSUSED */
|
||||
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);
|
||||
}
|
||||
|
@ -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,8 +548,8 @@ _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
|
||||
* from that. Otherwise, we have to figure it out from the base64.
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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,20 +111,22 @@ _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 *);
|
||||
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_extern = _prop_dictionary_externalize,
|
||||
.pot_equals = _prop_dictionary_equals,
|
||||
.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);
|
||||
}
|
||||
|
||||
if (pd->pd_array != NULL)
|
||||
_PROP_FREE(pd->pd_array, M_PROP_DICT);
|
||||
po = pd->pd_array[pd->pd_count - 1].pde_objref;
|
||||
_PROP_ASSERT(po != NULL);
|
||||
|
||||
_PROP_RWLOCK_DESTROY(pd->pd_rwlock);
|
||||
if (stack == NULL) {
|
||||
/*
|
||||
* If we are in emergency release mode,
|
||||
* just let caller recurse down.
|
||||
*/
|
||||
*obj = po;
|
||||
return (_PROP_OBJECT_FREE_FAILED);
|
||||
}
|
||||
|
||||
_PROP_POOL_PUT(_prop_dictionary_pool, pd);
|
||||
/* 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,83 +1105,133 @@ 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);
|
||||
|
||||
tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
|
||||
if (tmpkey == NULL)
|
||||
goto bad;
|
||||
|
||||
for (;;) {
|
||||
/* Fetch the next tag. */
|
||||
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;
|
||||
|
||||
/* Ok, it must be a non-empty key start tag. */
|
||||
if (!_PROP_TAG_MATCH(ctx, "key") ||
|
||||
ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
|
||||
ctx->poic_is_empty_element)
|
||||
goto bad;
|
||||
|
||||
if (_prop_object_internalize_decode_string(ctx,
|
||||
tmpkey, PDK_MAXKEY, &keylen,
|
||||
&ctx->poic_cp) == false)
|
||||
goto bad;
|
||||
|
||||
_PROP_ASSERT(keylen <= PDK_MAXKEY);
|
||||
tmpkey[keylen] = '\0';
|
||||
|
||||
if (_prop_object_internalize_find_tag(ctx, "key",
|
||||
_PROP_TAG_TYPE_END) == false)
|
||||
goto bad;
|
||||
|
||||
/* ..and now the beginning of the value. */
|
||||
if (_prop_object_internalize_find_tag(ctx, NULL,
|
||||
_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);
|
||||
if (ctx->poic_is_empty_element) {
|
||||
*obj = dict;
|
||||
return (true);
|
||||
}
|
||||
|
||||
_PROP_FREE(tmpkey, M_TEMP);
|
||||
return (dict);
|
||||
tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
|
||||
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;
|
||||
|
||||
/* Fetch the next tag. */
|
||||
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) {
|
||||
_PROP_FREE(tmpkey, M_TEMP);
|
||||
return (true);
|
||||
}
|
||||
|
||||
/* Ok, it must be a non-empty key start tag. */
|
||||
if (!_PROP_TAG_MATCH(ctx, "key") ||
|
||||
ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
|
||||
ctx->poic_is_empty_element)
|
||||
goto bad;
|
||||
|
||||
if (_prop_object_internalize_decode_string(ctx,
|
||||
tmpkey, PDK_MAXKEY, &keylen,
|
||||
&ctx->poic_cp) == false)
|
||||
goto bad;
|
||||
|
||||
_PROP_ASSERT(keylen <= PDK_MAXKEY);
|
||||
tmpkey[keylen] = '\0';
|
||||
|
||||
if (_prop_object_internalize_find_tag(ctx, "key",
|
||||
_PROP_TAG_TYPE_END) == false)
|
||||
goto bad;
|
||||
|
||||
/* ..and now the beginning of the value. */
|
||||
if (_prop_object_internalize_find_tag(ctx, NULL,
|
||||
_PROP_TAG_TYPE_START) == false)
|
||||
goto bad;
|
||||
|
||||
/*
|
||||
* 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_FREE(tmpkey, M_TEMP);
|
||||
prop_object_release(dict);
|
||||
return (NULL);
|
||||
*obj = NULL;
|
||||
return (true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
@ -586,10 +586,9 @@ _prop_object_internalize_match(const char *str1, size_t len1,
|
||||
{ t, sizeof(t) - 1, f }
|
||||
|
||||
static const struct _prop_object_internalizer {
|
||||
const char *poi_tag;
|
||||
size_t poi_taglen;
|
||||
prop_object_t (*poi_intern)(
|
||||
struct _prop_object_internalize_context *);
|
||||
const char *poi_tag;
|
||||
size_t poi_taglen;
|
||||
prop_object_internalizer_t poi_intern;
|
||||
} _prop_object_internalizer_table[] = {
|
||||
INTERNALIZER("array", _prop_array_internalize),
|
||||
|
||||
@ -618,17 +617,44 @@ 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);
|
||||
}
|
||||
|
||||
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
|
||||
@ -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_REFCNT_LOCK();
|
||||
ocnt = po->po_refcnt--;
|
||||
_PROP_REFCNT_UNLOCK();
|
||||
_prop_stack_init(&stack);
|
||||
|
||||
_PROP_ASSERT(ocnt != 0);
|
||||
if (ocnt == 1)
|
||||
(*po->po_type->pot_free)(po);
|
||||
do {
|
||||
do {
|
||||
po = obj;
|
||||
_PROP_ASSERT(obj);
|
||||
|
||||
_PROP_REFCNT_LOCK();
|
||||
ocnt = po->po_refcnt--;
|
||||
_PROP_REFCNT_UNLOCK();
|
||||
|
||||
_PROP_ASSERT(ocnt != 0);
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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(
|
||||
struct _prop_object_internalize_context *);
|
||||
prop_object_t _prop_bool_internalize(
|
||||
struct _prop_object_internalize_context *);
|
||||
prop_object_t _prop_data_internalize(
|
||||
struct _prop_object_internalize_context *);
|
||||
prop_object_t _prop_dictionary_internalize(
|
||||
struct _prop_object_internalize_context *);
|
||||
prop_object_t _prop_number_internalize(
|
||||
struct _prop_object_internalize_context *);
|
||||
prop_object_t _prop_string_internalize(
|
||||
struct _prop_object_internalize_context *);
|
||||
bool _prop_array_internalize(prop_stack_t, prop_object_t *,
|
||||
struct _prop_object_internalize_context *);
|
||||
bool _prop_bool_internalize(prop_stack_t, prop_object_t *,
|
||||
struct _prop_object_internalize_context *);
|
||||
bool _prop_data_internalize(prop_stack_t, prop_object_t *,
|
||||
struct _prop_object_internalize_context *);
|
||||
bool _prop_dictionary_internalize(prop_stack_t, prop_object_t *,
|
||||
struct _prop_object_internalize_context *);
|
||||
bool _prop_number_internalize(prop_stack_t, prop_object_t *,
|
||||
struct _prop_object_internalize_context *);
|
||||
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 *,
|
||||
void *);
|
||||
bool (*pot_equals) /* func to test quality */
|
||||
(void *, void *);
|
||||
/* 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 *);
|
||||
/* func to test quality */
|
||||
bool (*pot_equals)(void *, void *);
|
||||
};
|
||||
|
||||
struct _prop_object {
|
||||
|
104
common/lib/libprop/prop_stack.c
Normal file
104
common/lib/libprop/prop_stack.c
Normal 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;
|
||||
}
|
66
common/lib/libprop/prop_stack.h
Normal file
66
common/lib/libprop/prop_stack.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user