tinc/src/splay_tree.h

118 lines
3.7 KiB
C
Raw Normal View History

2004-11-02 20:50:53 +00:00
/*
splay_tree.h -- header file for splay_tree.c
2013-08-13 18:38:57 +00:00
Copyright (C) 2004-2013 Guus Sliepen <guus@tinc-vpn.org>
2004-11-02 20:50:53 +00:00
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2004-11-02 20:50:53 +00:00
*/
#ifndef __SPLAY_TREE_H__
#define __SPLAY_TREE_H__
typedef struct splay_node_t {
/* Linked list part */
struct splay_node_t *next;
struct splay_node_t *prev;
/* Tree part */
struct splay_node_t *parent;
struct splay_node_t *left;
struct splay_node_t *right;
/* Payload */
void *data;
} splay_node_t;
typedef int (*splay_compare_t)(const void *, const void *);
typedef void (*splay_action_t)(const void *);
typedef void (*splay_action_node_t)(const splay_node_t *);
typedef struct splay_tree_t {
/* Linked list part */
splay_node_t *head;
splay_node_t *tail;
/* Tree part */
splay_node_t *root;
splay_compare_t compare;
splay_action_t delete;
int count;
2004-11-02 20:50:53 +00:00
} splay_tree_t;
/* (De)constructors */
extern splay_tree_t *splay_alloc_tree(splay_compare_t, splay_action_t) __attribute__ ((__malloc__));
2004-11-02 20:50:53 +00:00
extern void splay_free_tree(splay_tree_t *);
extern splay_node_t *splay_alloc_node(void) __attribute__ ((__malloc__));
2004-11-02 20:50:53 +00:00
extern void splay_free_node(splay_tree_t *tree, splay_node_t *);
/* Insertion and deletion */
extern splay_node_t *splay_insert(splay_tree_t *, void *);
extern splay_node_t *splay_insert_node(splay_tree_t *, splay_node_t *);
extern void splay_insert_top(splay_tree_t *, splay_node_t *);
extern void splay_insert_before(splay_tree_t *, splay_node_t *, splay_node_t *);
extern void splay_insert_after(splay_tree_t *, splay_node_t *, splay_node_t *);
extern splay_node_t *splay_unlink(splay_tree_t *, void *);
extern void splay_unlink_node(splay_tree_t *tree, splay_node_t *);
extern void splay_delete(splay_tree_t *, void *);
extern void splay_delete_node(splay_tree_t *, splay_node_t *);
/* Fast tree cleanup */
extern void splay_delete_tree(splay_tree_t *);
/* Searching */
extern void *splay_search(splay_tree_t *, const void *);
extern void *splay_search_closest(splay_tree_t *, const void *, int *);
extern void *splay_search_closest_smaller(splay_tree_t *, const void *);
extern void *splay_search_closest_greater(splay_tree_t *, const void *);
extern splay_node_t *splay_search_node(splay_tree_t *, const void *);
extern splay_node_t *splay_search_closest_node(splay_tree_t *, const void *, int *);
extern splay_node_t *splay_search_closest_node_nosplay(const splay_tree_t *, const void *, int *);
extern splay_node_t *splay_search_closest_smaller_node(splay_tree_t *, const void *);
extern splay_node_t *splay_search_closest_greater_node(splay_tree_t *, const void *);
/* Tree walking */
extern void splay_foreach(const splay_tree_t *, splay_action_t);
extern void splay_foreach_node(const splay_tree_t *, splay_action_t);
Protect against callbacks removing items from the io tree. The definition of the splay_each() macro is somewhat complicated for syntactic reasons. Here's what it does in a more readable way: for (splay_node_t* node = tree->head; node;) { type* item = node->data; splay_node_t* next = node->next; // RUN USER BLOCK with (item) node = next; } list_each() works in the same way. Since node->next is saved before the user block runs, this construct supports removing the current item from within the user block. However, what it does *not* support is removing *other items* from within the user block, especially the next item. Indeed, that will invalide the next pointer in the above loop and therefore result in an invalid pointer dereference. Unfortunately, there is at least one code path where that unsupported operation happens. It is located in ack_h(), where the authentication protocol code detects a double connection (i.e. being connected to another node twice). Running in the context of a socket read event, this code will happily terminate the *other* metaconnection, resulting in its socket being removed from the io tree. If, by misfortune, this other metaconnection happened to have the next socket FD number (which is quite possible due to FD reuse - albeit unlikely), and was part of the io tree (which is quite likely because if that connection is stuck, it will most likely have pending writes) then this will result in the next pending io item being destroyed. Invalid pointer dereference ensues. I did a quick audit of other uses of splay_each() and list_each() and I believe this is the only scenario in which this "next pointer invalidation" problem can occur in practice. While this bug has been there since at least 6bc5d626a8726fc23365ee705761a3c666a08ad4 (November 2012), if not sooner, it happens quite rarely due to the very specific set of conditions required to trigger it. Nevertheless, it does manage to crash my central production nodes every other week or so.
2015-06-20 10:41:20 +00:00
/*
Iterates over a tree.
CAUTION: while this construct supports deleting the current item,
it does *not* support deleting *other* nodes while iterating on the tree.
*/
2012-10-07 22:35:38 +00:00
#define splay_each(type, item, tree) (type *item = (type *)1; item; item = NULL) for(splay_node_t *node = (tree)->head, *next; item = node ? node->data : NULL, next = node ? node->next : NULL, node; node = next)
2004-11-02 20:50:53 +00:00
#endif