Coverage Report

Created: 2025-07-11 06:58

/src/sudo/plugins/sudoers/redblack.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2004-2005, 2007, 2009-2015
5
 *  Todd C. Miller <Todd.Miller@sudo.ws>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
20
/*
21
 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
22
 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
23
 */
24
25
/*
26
 * Adapted from the following code written by Emin Martinian:
27
 * http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
28
 *
29
 * Copyright (c) 2001 Emin Martinian
30
 *
31
 * Redistribution and use in source and binary forms, with or without
32
 * modification, are permitted provided that neither the name of Emin
33
 * Martinian nor the names of any contributors are be used to endorse or
34
 * promote products derived from this software without specific prior
35
 * written permission.
36
 *
37
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
38
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
40
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
41
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
 */
49
50
#include <config.h>
51
52
#include <stdio.h>
53
#include <stdlib.h>
54
55
#include <sudoers.h>
56
#include <redblack.h>
57
58
static void rbrepair(struct rbtree *, struct rbnode *);
59
static void rotate_left(struct rbtree *, struct rbnode *);
60
static void rotate_right(struct rbtree *, struct rbnode *);
61
static void rbdestroy_int(struct rbtree *, struct rbnode *, void (*)(void *));
62
63
/*
64
 * Red-Black tree, see http://en.wikipedia.org/wiki/Red-black_tree
65
 *
66
 * A red-black tree is a binary search tree where each node has a color
67
 * attribute, the value of which is either red or black.  Essentially, it
68
 * is just a convenient way to express a 2-3-4 binary search tree where
69
 * the color indicates whether the node is part of a 3-node or a 4-node.
70
 * In addition to the ordinary requirements imposed on binary search
71
 * trees, we make the following additional requirements of any valid
72
 * red-black tree:
73
 *  1) Every node is either red or black.
74
 *  2) The root is black.
75
 *  3) All leaves are black.
76
 *  4) Both children of each red node are black.
77
 *  5) The paths from each leaf up to the root each contain the same
78
 *     number of black nodes.
79
 */
80
81
/*
82
 * Create a red black tree struct using the specified compare routine.
83
 * Allocates and returns the initialized (empty) tree or NULL if
84
 * memory cannot be allocated.
85
 */
86
struct rbtree *
87
rbcreate(int (*compar)(const void *, const void*))
88
31
{
89
31
    struct rbtree *tree;
90
31
    debug_decl(rbcreate, SUDOERS_DEBUG_RBTREE);
91
92
31
    if ((tree = malloc(sizeof(*tree))) == NULL) {
93
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
94
0
      "unable to allocate memory");
95
0
  debug_return_ptr(NULL);
96
0
    }
97
98
31
    tree->compar = compar;
99
100
    /*
101
     * We use a self-referencing sentinel node called nil to simplify the
102
     * code by avoiding the need to check for NULL pointers.
103
     */
104
31
    tree->nil.left = tree->nil.right = tree->nil.parent = &tree->nil;
105
31
    tree->nil.color = black;
106
31
    tree->nil.data = NULL;
107
108
    /*
109
     * Similarly, the fake root node keeps us from having to worry
110
     * about splitting the root.
111
     */
112
31
    tree->root.left = tree->root.right = tree->root.parent = &tree->nil; // -V778
113
31
    tree->root.color = black;
114
31
    tree->root.data = NULL;
115
116
31
    debug_return_ptr(tree);
117
31
}
118
119
/*
120
 * Perform a left rotation starting at node.
121
 */
122
static void
123
rotate_left(struct rbtree *tree, struct rbnode *node)
124
18
{
125
18
    struct rbnode *child;
126
18
    debug_decl(rotate_left, SUDOERS_DEBUG_RBTREE);
127
128
18
    child = node->right;
129
18
    node->right = child->left;
130
131
18
    if (child->left != rbnil(tree))
132
0
        child->left->parent = node;
133
18
    child->parent = node->parent;
134
135
18
    if (node == node->parent->left)
136
18
  node->parent->left = child;
137
0
    else
138
0
  node->parent->right = child;
139
18
    child->left = node;
140
18
    node->parent = child;
141
142
18
    debug_return;
143
18
}
144
145
/*
146
 * Perform a right rotation starting at node.
147
 */
148
static void
149
rotate_right(struct rbtree *tree, struct rbnode *node)
150
18
{
151
18
    struct rbnode *child;
152
18
    debug_decl(rotate_right, SUDOERS_DEBUG_RBTREE);
153
154
18
    child = node->left;
155
18
    node->left = child->right;
156
157
18
    if (child->right != rbnil(tree))
158
0
        child->right->parent = node;
159
18
    child->parent = node->parent;
160
161
18
    if (node == node->parent->left)
162
18
  node->parent->left = child;
163
0
    else
164
0
  node->parent->right = child;
165
18
    child->right = node;
166
18
    node->parent = child;
167
168
18
    debug_return;
169
18
}
170
171
/*
172
 * Insert data pointer into a redblack tree.
173
 * Returns a 0 on success, 1 if a node matching "data" already exists
174
 * (filling in "existing" if not NULL), or -1 on malloc() failure.
175
 */
176
int
177
rbinsert(struct rbtree *tree, void *data, struct rbnode **existing)
178
102
{
179
102
    struct rbnode *node = rbfirst(tree);
180
102
    struct rbnode *parent = rbroot(tree);
181
102
    int res;
182
102
    debug_decl(rbinsert, SUDOERS_DEBUG_RBTREE);
183
184
    /* Find correct insertion point. */
185
216
    while (node != rbnil(tree)) {
186
114
  parent = node;
187
114
  if ((res = tree->compar(data, node->data)) == 0) {
188
0
      if (existing != NULL)
189
0
    *existing = node;
190
0
      debug_return_int(1);
191
0
  }
192
114
  node = res < 0 ? node->left : node->right;
193
114
    }
194
195
102
    node = malloc(sizeof(*node));
196
102
    if (node == NULL) {
197
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
198
0
      "unable to allocate memory");
199
0
  debug_return_int(-1);
200
0
    }
201
102
    node->data = data;
202
102
    node->left = node->right = rbnil(tree);
203
102
    node->parent = parent;
204
102
    if (parent == rbroot(tree) || tree->compar(data, parent->data) < 0)
205
66
  parent->left = node;
206
36
    else
207
36
  parent->right = node;
208
102
    node->color = red;
209
210
    /*
211
     * If the parent node is black we are all set, if it is red we have
212
     * the following possible cases to deal with.  We iterate through
213
     * the rest of the tree to make sure none of the required properties
214
     * is violated.
215
     *
216
     *  1) The uncle is red.  We repaint both the parent and uncle black
217
     *     and repaint the grandparent node red.
218
     *
219
     *  2) The uncle is black and the new node is the right child of its
220
     *     parent, and the parent in turn is the left child of its parent.
221
     *     We do a left rotation to switch the roles of the parent and
222
     *     child, relying on further iterations to fixup the old parent.
223
     *
224
     *  3) The uncle is black and the new node is the left child of its
225
     *     parent, and the parent in turn is the left child of its parent.
226
     *     We switch the colors of the parent and grandparent and perform
227
     *     a right rotation around the grandparent.  This makes the former
228
     *     parent the parent of the new node and the former grandparent.
229
     *
230
     * Note that because we use a sentinel for the root node we never
231
     * need to worry about replacing the root.
232
     */
233
144
    while (node->parent->color == red) {
234
42
  struct rbnode *uncle;
235
42
  if (node->parent == node->parent->parent->left) {
236
18
      uncle = node->parent->parent->right;
237
18
      if (uncle->color == red) {
238
0
    node->parent->color = black;
239
0
    uncle->color = black;
240
0
    node->parent->parent->color = red;
241
0
    node = node->parent->parent;
242
18
      } else /* if (uncle->color == black) */ {
243
18
    if (node == node->parent->right) {
244
6
        node = node->parent;
245
6
        rotate_left(tree, node);
246
6
    }
247
18
    node->parent->color = black;
248
18
    node->parent->parent->color = red;
249
18
    rotate_right(tree, node->parent->parent);
250
18
      }
251
24
  } else { /* if (node->parent == node->parent->parent->right) */
252
24
      uncle = node->parent->parent->left;
253
24
      if (uncle->color == red) {
254
12
    node->parent->color = black;
255
12
    uncle->color = black;
256
12
    node->parent->parent->color = red;
257
12
    node = node->parent->parent;
258
12
      } else /* if (uncle->color == black) */ {
259
12
    if (node == node->parent->left) {
260
0
        node = node->parent;
261
0
        rotate_right(tree, node);
262
0
    }
263
12
    node->parent->color = black;
264
12
    node->parent->parent->color = red;
265
12
    rotate_left(tree, node->parent->parent);
266
12
      }
267
24
  }
268
42
    }
269
102
    rbfirst(tree)->color = black;  /* first node is always black */
270
102
    debug_return_int(0);
271
102
}
272
273
/*
274
 * Look for a node matching key in tree.
275
 * Returns a pointer to the node if found, else NULL.
276
 */
277
struct rbnode *
278
rbfind(struct rbtree *tree, void *key)
279
27
{
280
27
    struct rbnode *node = rbfirst(tree);
281
27
    int res;
282
27
    debug_decl(rbfind, SUDOERS_DEBUG_RBTREE);
283
284
52
    while (node != rbnil(tree)) {
285
34
  if ((res = tree->compar(key, node->data)) == 0)
286
9
      debug_return_ptr(node);
287
25
  node = res < 0 ? node->left : node->right;
288
25
    }
289
18
    debug_return_ptr(NULL);
290
18
}
291
292
/*
293
 * Call func() for each node, passing it the node data and a cookie;
294
 * If func() returns non-zero for a node, the traversal stops and the
295
 * error value is returned.  Returns 0 on successful traversal.
296
 */
297
int
298
rbapply_node(struct rbtree *tree, struct rbnode *node,
299
    int (*func)(void *, void *), void *cookie, enum rbtraversal order)
300
0
{
301
0
    int error;
302
0
    debug_decl(rbapply_node, SUDOERS_DEBUG_RBTREE);
303
304
0
    if (node != rbnil(tree)) {
305
0
  if (order == preorder)
306
0
      if ((error = func(node->data, cookie)) != 0)
307
0
    debug_return_int(error);
308
0
  if ((error = rbapply_node(tree, node->left, func, cookie, order)) != 0)
309
0
      debug_return_int(error);
310
0
  if (order == inorder)
311
0
      if ((error = func(node->data, cookie)) != 0)
312
0
    debug_return_int(error);
313
0
  if ((error = rbapply_node(tree, node->right, func, cookie, order)) != 0)
314
0
      debug_return_int(error);
315
0
  if (order == postorder)
316
0
      if ((error = func(node->data, cookie)) != 0)
317
0
    debug_return_int(error);
318
0
    }
319
0
    debug_return_int(0);
320
0
}
321
322
/*
323
 * Returns the successor of node, or nil if there is none.
324
 */
325
static struct rbnode *
326
rbsuccessor(struct rbtree *tree, struct rbnode *node)
327
0
{
328
0
    struct rbnode *succ;
329
0
    debug_decl(rbsuccessor, SUDOERS_DEBUG_RBTREE);
330
331
0
    if ((succ = node->right) != rbnil(tree)) {
332
0
  while (succ->left != rbnil(tree))
333
0
      succ = succ->left;
334
0
    } else {
335
  /* No right child, move up until we find it or hit the root */
336
0
  for (succ = node->parent; node == succ->right; succ = succ->parent)
337
0
      node = succ;
338
0
  if (succ == rbroot(tree))
339
0
      succ = rbnil(tree);
340
0
    }
341
0
    debug_return_ptr(succ);
342
0
}
343
344
/*
345
 * Recursive portion of rbdestroy().
346
 */
347
static void
348
rbdestroy_int(struct rbtree *tree, struct rbnode *node, void (*destroy)(void *))
349
235
{
350
235
    debug_decl(rbdestroy_int, SUDOERS_DEBUG_RBTREE);
351
235
    if (node != rbnil(tree)) {
352
102
  rbdestroy_int(tree, node->left, destroy);
353
102
  rbdestroy_int(tree, node->right, destroy);
354
102
  if (destroy != NULL)
355
102
      destroy(node->data);
356
102
  free(node);
357
102
    }
358
235
    debug_return;
359
235
}
360
361
/*
362
 * Destroy the specified tree, calling the destructor "destroy"
363
 * for each node and then freeing the tree itself.
364
 */
365
void
366
rbdestroy(struct rbtree *tree, void (*destroy)(void *))
367
31
{
368
31
    debug_decl(rbdestroy, SUDOERS_DEBUG_RBTREE);
369
31
    rbdestroy_int(tree, rbfirst(tree), destroy);
370
31
    free(tree);
371
31
    debug_return;
372
31
}
373
374
/*
375
 * Delete node 'z' from the tree and return its data pointer.
376
 */
377
void *rbdelete(struct rbtree *tree, struct rbnode *z)
378
0
{
379
0
    struct rbnode *x, *y;
380
0
    void *data = z->data;
381
0
    debug_decl(rbdelete, SUDOERS_DEBUG_RBTREE);
382
383
0
    if (z->left == rbnil(tree) || z->right == rbnil(tree))
384
0
  y = z;
385
0
    else
386
0
  y = rbsuccessor(tree, z);
387
0
    x = (y->left == rbnil(tree)) ? y->right : y->left;
388
389
0
    if ((x->parent = y->parent) == rbroot(tree)) {
390
0
  rbfirst(tree) = x;
391
0
    } else {
392
0
  if (y == y->parent->left)
393
0
      y->parent->left = x;
394
0
  else
395
0
      y->parent->right = x;
396
0
    }
397
0
    if (y->color == black)
398
0
  rbrepair(tree, x);
399
0
    if (y != z) {
400
0
  y->left = z->left;
401
0
  y->right = z->right;
402
0
  y->parent = z->parent;
403
0
  y->color = z->color;
404
0
  z->left->parent = z->right->parent = y;
405
0
  if (z == z->parent->left)
406
0
      z->parent->left = y; 
407
0
  else
408
0
      z->parent->right = y;
409
0
    }
410
0
    free(z); 
411
    
412
0
    debug_return_ptr(data);
413
0
}
414
415
/*
416
 * Repair the tree after a node has been deleted by rotating and repainting
417
 * colors to restore the 4 properties inherent in red-black trees.
418
 */
419
static void
420
rbrepair(struct rbtree *tree, struct rbnode *node)
421
0
{
422
0
    struct rbnode *sibling;
423
0
    debug_decl(rbrepair, SUDOERS_DEBUG_RBTREE);
424
425
0
    while (node->color == black && node != rbfirst(tree)) {
426
0
  if (node == node->parent->left) {
427
0
      sibling = node->parent->right;
428
0
      if (sibling->color == red) {
429
0
    sibling->color = black;
430
0
    node->parent->color = red;
431
0
    rotate_left(tree, node->parent);
432
0
    sibling = node->parent->right;
433
0
      }
434
0
      if (sibling->right->color == black && sibling->left->color == black) {
435
0
    sibling->color = red;
436
0
    node = node->parent;
437
0
      } else {
438
0
    if (sibling->right->color == black) {
439
0
          sibling->left->color = black;
440
0
          sibling->color = red;
441
0
          rotate_right(tree, sibling);
442
0
          sibling = node->parent->right;
443
0
    }
444
0
    sibling->color = node->parent->color;
445
0
    node->parent->color = black;
446
0
    sibling->right->color = black;
447
0
    rotate_left(tree, node->parent);
448
0
    node = rbfirst(tree); /* exit loop */
449
0
      }
450
0
  } else { /* if (node == node->parent->right) */
451
0
      sibling = node->parent->left;
452
0
      if (sibling->color == red) {
453
0
    sibling->color = black;
454
0
    node->parent->color = red;
455
0
    rotate_right(tree, node->parent);
456
0
    sibling = node->parent->left;
457
0
      }
458
0
      if (sibling->right->color == black && sibling->left->color == black) {
459
0
    sibling->color = red;
460
0
    node = node->parent;
461
0
      } else {
462
0
    if (sibling->left->color == black) {
463
0
        sibling->right->color = black;
464
0
        sibling->color = red;
465
0
        rotate_left(tree, sibling);
466
0
        sibling = node->parent->left;
467
0
    }
468
0
    sibling->color = node->parent->color;
469
0
    node->parent->color = black;
470
0
    sibling->left->color = black;
471
0
    rotate_right(tree, node->parent);
472
0
    node = rbfirst(tree); /* exit loop */
473
0
      }
474
0
  }
475
0
    }
476
0
    node->color = black;
477
478
0
    debug_return;
479
0
}