1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00

bug 1067: Assertions and comments about done_dom_node().

In bug 1067, dom_rss_pop_document() freed a node with done_dom_node()
even though call_dom_node_callbacks() was still using that node.  This
made call_dom_node_callbacks() read a function pointer from beyond the
end of an array and call that.  Add assertions to detect out-of-range
node types, and comments to warn about the bug.
This commit is contained in:
Kalle Olavi Niemitalo 2009-02-15 03:10:14 +02:00 committed by Kalle Olavi Niemitalo
parent a7c2f14e6d
commit eb820a57a6
4 changed files with 34 additions and 2 deletions

View File

@ -392,6 +392,7 @@ done_dom_node_data(struct dom_node *node)
union dom_node_data *data; union dom_node_data *data;
assert(node); assert(node);
assert(node->type < DOM_NODES); /* unsigned comparison */
data = &node->data; data = &node->data;
@ -427,6 +428,18 @@ done_dom_node_data(struct dom_node *node)
if (node->allocated) if (node->allocated)
done_dom_string(&node->string); done_dom_string(&node->string);
/* call_dom_stack_callbacks() asserts that the node type is
* within range. If assertions are enabled, store an
* out-of-range value there to make the assertion more likely
* to fail if a callback has freed the node prematurely.
* This is not entirely reliable though, because the memory
* is freed below and may be allocated for some other purpose
* before the assertion. */
#ifndef CONFIG_FASTMEM
node->type = -1;
#endif
mem_free(node); mem_free(node);
} }

View File

@ -314,7 +314,9 @@ get_dom_node_map_entry(struct dom_node_list *node_map,
enum dom_node_type type, uint16_t subtype, enum dom_node_type type, uint16_t subtype,
struct dom_string *name); struct dom_string *name);
/* Removes the node and all its children and free()s itself */ /* Removes the node and all its children and free()s itself.
* A dom_stack_callback_T must not use this to free the node
* it gets as a parameter. */
void done_dom_node(struct dom_node *node); void done_dom_node(struct dom_node *node);
#ifndef DEBUG_MEMLEAK #ifndef DEBUG_MEMLEAK

View File

@ -139,6 +139,17 @@ call_dom_stack_callbacks(struct dom_stack *stack, struct dom_stack_state *state,
struct dom_stack_context *context = stack->contexts[i]; struct dom_stack_context *context = stack->contexts[i];
dom_stack_callback_T callback; dom_stack_callback_T callback;
assert(state->node->type < DOM_NODES); /* unsigned comparison */
if_assert_failed {
/* The node type is out of range for the
* callback arrays. The node may have been
* corrupted or already freed. Ignore
* free_node here because attempting to free
* the node would probably just corrupt things
* further. */
return 0;
}
if (action == DOM_STACK_PUSH) if (action == DOM_STACK_PUSH)
callback = context->info->push[state->node->type]; callback = context->info->push[state->node->type];
else else

View File

@ -51,7 +51,13 @@ struct dom_stack;
/** DOM stack callback /** DOM stack callback
* *
* Used by contexts, for 'hooking' into the node traversing. */ * Used by contexts, for 'hooking' into the node traversing.
*
* This callback must not call done_dom_node() to free the node it
* gets as a parameter, because call_dom_node_callbacks() may be
* intending to call more callbacks for the same node. Instead, the
* callback can return ::DOM_CODE_FREE_NODE, and the node will then
* be freed after the callbacks of all contexts have been called. */
typedef enum dom_code typedef enum dom_code
(*dom_stack_callback_T)(struct dom_stack *, struct dom_node *, void *); (*dom_stack_callback_T)(struct dom_stack *, struct dom_node *, void *);