mirror of
https://github.com/rkd77/elinks.git
synced 2024-10-14 05:53:36 -04: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:
parent
a7c2f14e6d
commit
eb820a57a6
@ -392,6 +392,7 @@ done_dom_node_data(struct dom_node *node)
|
||||
union dom_node_data *data;
|
||||
|
||||
assert(node);
|
||||
assert(node->type < DOM_NODES); /* unsigned comparison */
|
||||
|
||||
data = &node->data;
|
||||
|
||||
@ -427,6 +428,18 @@ done_dom_node_data(struct dom_node *node)
|
||||
|
||||
if (node->allocated)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -314,7 +314,9 @@ get_dom_node_map_entry(struct dom_node_list *node_map,
|
||||
enum dom_node_type type, uint16_t subtype,
|
||||
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);
|
||||
|
||||
#ifndef DEBUG_MEMLEAK
|
||||
|
@ -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];
|
||||
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)
|
||||
callback = context->info->push[state->node->type];
|
||||
else
|
||||
|
@ -51,7 +51,13 @@ struct dom_stack;
|
||||
|
||||
/** 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
|
||||
(*dom_stack_callback_T)(struct dom_stack *, struct dom_node *, void *);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user