Coverage Report

Created: 2026-06-30 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/unlang/xlat_alloc.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: a1a34d3c31fa0da7adf92edb518429924ac66cda $
19
 *
20
 * @file xlat_alloc.c
21
 * @brief Functions to allocate different types of xlat nodes
22
 */
23
24
RCSID("$Id: a1a34d3c31fa0da7adf92edb518429924ac66cda $")
25
26
#include <freeradius-devel/server/base.h>
27
28
29
#define _XLAT_PRIVATE
30
#include <freeradius-devel/unlang/xlat_priv.h>
31
32
xlat_exp_head_t *_xlat_exp_head_alloc(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx)
33
58.4k
{
34
58.4k
  xlat_exp_head_t *head;
35
36
58.4k
  MEM(head = talloc_zero(ctx, xlat_exp_head_t));
37
38
58.4k
  fr_dlist_init(&head->dlist, xlat_exp_t, entry);
39
58.4k
  head->flags = XLAT_FLAGS_INIT;
40
58.4k
#ifndef NDEBUG
41
58.4k
  head->file = file;
42
58.4k
  head->line = line;
43
58.4k
#endif
44
45
58.4k
  return head;
46
58.4k
}
47
48
/** Set the type of an xlat node
49
 *
50
 * Also initialises any xlat_exp_head necessary
51
 *
52
 * @param[in] node  to set type for.
53
 * @param[in] type  to set.
54
 */
55
void _xlat_exp_set_type(NDEBUG_LOCATION_ARGS xlat_exp_t *node, xlat_type_t type)
56
118k
{
57
  /*
58
   *  Do nothing if it's the same type
59
   */
60
118k
  if (node->type == type) return;
61
62
  /*
63
   *  Free existing lists if present
64
   */
65
118k
  if (node->type != 0) switch (node->type) {
66
0
  case XLAT_GROUP:
67
0
    TALLOC_FREE(node->group);
68
0
    break;
69
70
0
  case XLAT_FUNC_UNRESOLVED:
71
0
    if (type == XLAT_FUNC) goto done;  /* Just switching from unresolved to resolved */
72
0
    FALL_THROUGH;
73
74
0
  case XLAT_FUNC:
75
0
    TALLOC_FREE(node->call.args);
76
0
    break;
77
78
19.8k
  case XLAT_TMPL:
79
19.8k
    if (node->vpt && (node->fmt == node->vpt->name)) (void) talloc_steal(node, node->fmt);
80
81
    /*
82
     *  Converting a tmpl to a box.  If the tmpl is data, we can then just steal the contents
83
     *  of the box.
84
     */
85
19.8k
    if (type == XLAT_BOX) {
86
19.8k
      tmpl_t *vpt = node->vpt;
87
88
19.8k
      if (!vpt) break;
89
90
19.8k
      fr_assert(tmpl_rules_cast(vpt) == FR_TYPE_NULL);
91
92
19.8k
      if (!tmpl_is_data(vpt)) {
93
0
        talloc_free(vpt);
94
0
        break;
95
0
      }
96
97
      /*
98
       *  Initialize the box from the tmpl data.  And then do NOT re-initialize the box
99
       *  later.
100
       */
101
19.8k
      node->flags = XLAT_FLAGS_INIT;
102
19.8k
      fr_value_box_steal(node, &node->data, tmpl_value(vpt));
103
19.8k
      talloc_free(vpt);
104
19.8k
      goto done;
105
19.8k
    }
106
107
19.8k
    TALLOC_FREE(node->vpt);
108
0
    break;
109
110
0
  default:
111
0
    break;
112
19.8k
  }
113
114
  /*
115
   *  Alloc new lists to match the type
116
   */
117
98.2k
  switch (type) {
118
33.8k
  case XLAT_GROUP:
119
33.8k
    node->group = _xlat_exp_head_alloc(NDEBUG_LOCATION_VALS node);
120
33.8k
    node->flags = node->group->flags;
121
33.8k
    break;
122
123
15.9k
  case XLAT_FUNC:
124
15.9k
    node->flags = XLAT_FLAGS_INIT;
125
15.9k
    break;
126
127
0
  case XLAT_FUNC_UNRESOLVED:
128
0
    node->flags = XLAT_FLAGS_INIT;
129
0
    node->flags.needs_resolving = true;
130
0
    break;
131
132
22.4k
  case XLAT_BOX:
133
22.4k
    node->flags = XLAT_FLAGS_INIT;
134
22.4k
    fr_value_box_init_null(&node->data);
135
22.4k
    break;
136
137
0
#ifdef HAVE_REGEX
138
140
  case XLAT_REGEX:
139
140
    node->flags = (xlat_flags_t) {};
140
140
    break;
141
0
#endif
142
143
463
  case XLAT_ONE_LETTER:
144
    /*
145
     *  %% is pure.  Everything else is not.
146
     */
147
463
    fr_assert(node->fmt);
148
149
463
    if (node->fmt[0] != '%') {
150
463
      node->flags = (xlat_flags_t) {};
151
463
    } else {
152
0
      node->flags = XLAT_FLAGS_INIT;
153
0
    }
154
463
    break;
155
156
25.4k
  default:
157
25.4k
    node->flags = XLAT_FLAGS_INIT;
158
25.4k
    break;
159
98.2k
  }
160
161
118k
done:
162
118k
  node->type = type;
163
118k
}
164
165
static xlat_exp_t *xlat_exp_alloc_pool(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, unsigned int extra_hdrs, size_t extra)
166
98.2k
{
167
98.2k
  xlat_exp_t *node;
168
169
98.2k
  MEM(node = talloc_zero_pooled_object(ctx, xlat_exp_t, extra_hdrs, sizeof(xlat_exp_t) + extra * extra_hdrs));
170
98.2k
  node->flags = XLAT_FLAGS_INIT;
171
98.2k
  node->quote = T_BARE_WORD;
172
98.2k
#ifndef NDEBUG
173
98.2k
  node->file = file;
174
98.2k
  node->line = line;
175
98.2k
#endif
176
177
98.2k
  return node;
178
98.2k
}
179
180
/** Allocate an xlat node with no name, and no type set
181
 *
182
 * @param[in] ctx to allocate node in.
183
 * @return A new xlat node.
184
 */
185
xlat_exp_t *_xlat_exp_alloc_null(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx)
186
466
{
187
466
  return xlat_exp_alloc_pool(NDEBUG_LOCATION_VALS ctx, 0, 0);
188
466
}
189
190
/** Allocate an xlat node
191
 *
192
 * @param[in] ctx to allocate node in.
193
 * @param[in] type  of the node.
194
 * @param[in] in  original input string.
195
 * @param[in] inlen the length of the original input string.
196
 * @return A new xlat node.
197
 */
198
xlat_exp_t *_xlat_exp_alloc(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, xlat_type_t type, char const *in, size_t inlen)
199
97.7k
{
200
97.7k
  xlat_exp_t *node;
201
97.7k
  unsigned int extra_hdrs;
202
97.7k
  size_t extra;
203
204
  /*
205
   *  Figure out how much extra memory we
206
   *  need to allocate for this node type.
207
   */
208
97.7k
  switch (type) {
209
33.8k
  case XLAT_GROUP:
210
33.8k
    extra_hdrs = 1;
211
33.8k
    extra = sizeof(xlat_exp_head_t);
212
33.8k
    break;
213
214
15.9k
  case XLAT_FUNC:
215
15.9k
    extra_hdrs = 1;
216
15.9k
    extra = sizeof(xlat_exp_head_t);
217
15.9k
    break;
218
219
47.9k
  default:
220
47.9k
    extra_hdrs = 0;
221
47.9k
    extra = 0;
222
97.7k
  }
223
224
97.7k
  node = xlat_exp_alloc_pool(NDEBUG_LOCATION_VALS
225
97.7k
           ctx,
226
97.7k
           (in != NULL) + extra_hdrs,
227
97.7k
           inlen + extra);
228
97.7k
  _xlat_exp_set_type(NDEBUG_LOCATION_VALS node, type);
229
230
97.7k
  node->quote = T_BARE_WORD; /* ensure that this is always initialized */
231
232
97.7k
  if (!in) return node;
233
234
16.0k
  node->fmt = talloc_bstrndup(node, in, inlen);
235
16.0k
  switch (type) {
236
0
  case XLAT_BOX:
237
0
    fr_value_box_strdup_shallow(&node->data, NULL, node->fmt, false);
238
0
    break;
239
240
16.0k
  default:
241
16.0k
    break;
242
16.0k
  }
243
244
16.0k
  return node;
245
16.0k
}
246
247
/** Set the tmpl for a node, along with flags and the name.
248
 *
249
 * @param[in] node  to set fmt for.
250
 * @param[in] vpt the tmpl to set
251
 */
252
void xlat_exp_set_vpt(xlat_exp_t *node, tmpl_t *vpt)
253
24.3k
{
254
24.3k
  if (tmpl_contains_xlat(vpt)) {
255
3.74k
    node->flags = tmpl_xlat(vpt)->flags;
256
3.74k
  }
257
258
24.3k
  if (tmpl_is_exec(vpt) || tmpl_contains_attr(vpt)) {
259
629
    node->flags = (xlat_flags_t) {};
260
629
  }
261
262
24.3k
  node->flags.needs_resolving |= tmpl_needs_resolving(vpt);
263
264
24.3k
  node->vpt = vpt;
265
24.3k
  xlat_exp_set_name_shallow(node, vpt->name);
266
24.3k
}
267
268
/** Set the function for a node
269
 *
270
 * @param[in] node  to set fmt for.
271
 * @param[in] func  to set
272
 * @param[in] dict  the dictionary to set
273
 */
274
void xlat_exp_set_func(xlat_exp_t *node, xlat_t const *func, fr_dict_t const *dict)
275
15.9k
{
276
15.9k
  node->call.func = func;
277
15.9k
  node->call.dict = dict;
278
15.9k
  node->flags = func->flags;
279
15.9k
  node->flags.impure_func = !func->flags.pure;
280
281
15.9k
  if (!dict) node->flags.needs_resolving = true;
282
15.9k
}
283
284
void xlat_exp_finalize_func(xlat_exp_t *node)
285
1.20k
{
286
1.20k
  if (!node->call.args) return;
287
288
1.20k
  node->call.args->is_argv = true;
289
290
1.20k
  if (node->type == XLAT_FUNC_UNRESOLVED) return;
291
292
1.20k
  xlat_flags_merge(&node->flags, &node->call.args->flags);
293
294
  /*
295
   *  We might not be able to purify the function call, but perhaps we can purify the arguments to it.
296
   */
297
1.20k
  node->flags.can_purify = (node->call.func->flags.pure && node->call.args->flags.pure) | node->call.args->flags.can_purify;
298
1.20k
  node->flags.impure_func = !node->call.func->flags.pure;
299
1.20k
}
300
301
302
/** Set the format string for an xlat node
303
 *
304
 * @param[in] node  to set fmt for.
305
 * @param[in] fmt talloced buffer to set as the fmt string.
306
 * @param[in] len of fmt string.
307
 */
308
void xlat_exp_set_name(xlat_exp_t *node, char const *fmt, size_t len)
309
31.6k
{
310
31.6k
  fr_assert(node->fmt != fmt);
311
312
31.6k
  if (node->fmt) talloc_const_free(node->fmt);
313
31.6k
  MEM(node->fmt = talloc_bstrndup(node, fmt, len));
314
31.6k
}
315
316
/** Set the format string for an xlat node, copying from a talloc'd buffer
317
 *
318
 * @param[in] node  to set fmt for.
319
 * @param[in] fmt talloced buffer to set as the fmt string.
320
 */
321
void xlat_exp_set_name_buffer(xlat_exp_t *node, char const *fmt)
322
{
323
  if (node->fmt) {
324
    if (node->fmt == fmt) {
325
      (void) talloc_steal(node, fmt);
326
    } else {
327
      talloc_const_free(node->fmt);
328
    }
329
  }
330
  MEM(node->fmt = talloc_typed_strdup_buffer(node, fmt));
331
}
332
333
/** Set the format string for an xlat node from a pre-existing buffer
334
 *
335
 * @param[in] node  to set fmt for.
336
 * @param[in] fmt talloced buffer to set as the fmt string.
337
 */
338
void xlat_exp_set_name_shallow(xlat_exp_t *node, char const *fmt)
339
66.2k
{
340
66.2k
  fr_assert(node->fmt != fmt);
341
342
66.2k
  if (node->fmt) talloc_const_free(node->fmt);
343
66.2k
  node->fmt = talloc_get_type_abort_const(fmt, char);
344
66.2k
}
345
346
/** Copy all nodes in the input list to the output list
347
 *
348
 * @param[in] ctx to allocate new nodes in.
349
 * @param[out] out  Where to write new nodes.
350
 * @param[in] in  Input nodes.
351
 * @return
352
 *  - 0 on success.
353
 *  - -1 on failure.
354
 */
355
static int CC_HINT(nonnull) _xlat_copy_internal(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, xlat_exp_head_t *out, xlat_exp_head_t const *in)
356
0
{
357
0
  xlat_flags_merge(&out->flags, &in->flags);
358
359
  /*
360
   *  Copy everything in the list of nodes
361
   */
362
0
  xlat_exp_foreach(in, p) {
363
0
    xlat_exp_t *node;
364
365
0
    (void)talloc_get_type_abort(p, xlat_exp_t);
366
367
    /*
368
     *  Ensure the format string is valid...  At this point
369
     *  they should all be talloc'd strings.
370
     */
371
0
    MEM(node = xlat_exp_alloc(ctx, p->type,
372
0
            talloc_get_type_abort_const(p->fmt, char), talloc_strlen(p->fmt)));
373
374
0
    node->quote = p->quote;
375
0
    node->flags = p->flags;
376
377
0
    switch (p->type) {
378
0
    case XLAT_INVALID:
379
0
      fr_strerror_printf("Cannot copy xlat node of type \"invalid\"");
380
0
    error:
381
0
      return -1;
382
383
0
    case XLAT_BOX:
384
0
      if (unlikely(fr_value_box_copy(node, &node->data, &p->data) < 0)) goto error;
385
0
      break;
386
387
0
    case XLAT_ONE_LETTER: /* Done with format */
388
0
    case XLAT_FUNC_UNRESOLVED:
389
0
      break;
390
391
0
    case XLAT_FUNC:
392
      /*
393
       *  Only copy the function pointer, and whether this
394
       *  is ephemeral.
395
       *
396
       *  All instance data is specific to the xlat node and
397
       *  cannot be duplicated.
398
       *
399
       *  The node xlat nodes will need to be registered in
400
       *  the xlat instantiation table later.
401
       */
402
0
      node->call.func = p->call.func;
403
0
      node->call.dict = p->call.dict;
404
0
      node->call.ephemeral = p->call.ephemeral;
405
0
      node->call.args = xlat_exp_head_alloc(node);
406
0
      node->call.args->is_argv = true;
407
0
      if (unlikely(_xlat_copy_internal(NDEBUG_LOCATION_VALS
408
0
               node, node->call.args, p->call.args) < 0)) goto error;
409
0
      break;
410
411
0
    case XLAT_TMPL:
412
0
      node->vpt = tmpl_copy(node, p->vpt);
413
0
      break;
414
415
0
#ifdef HAVE_REGEX
416
0
    case XLAT_REGEX:
417
0
      node->regex_index = p->regex_index;
418
0
      break;
419
0
#endif
420
421
0
    case XLAT_GROUP:
422
0
      if (unlikely(_xlat_copy_internal(NDEBUG_LOCATION_VALS
423
0
               node, node->group, p->group) < 0)) goto error;
424
0
      break;
425
0
    }
426
427
0
    xlat_exp_insert_tail(out, node);
428
0
  }
429
430
0
  return 0;
431
0
}
432
433
int _xlat_copy(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, xlat_exp_head_t *out, xlat_exp_head_t const *in)
434
0
{
435
0
  int ret;
436
437
0
  if (!in) return 0;
438
439
0
  XLAT_HEAD_VERIFY(in);
440
0
  ret = _xlat_copy_internal(NDEBUG_LOCATION_VALS ctx, out, in);
441
0
  XLAT_HEAD_VERIFY(out);
442
443
0
  return ret;
444
0
}
445
446
#ifdef WITH_VERIFY_PTR
447
void xlat_exp_verify(xlat_exp_t const *node)
448
12.5M
{
449
12.5M
  (void)talloc_get_type_abort_const(node, xlat_exp_t);
450
451
12.5M
  switch (node->type) {
452
6.22M
  case XLAT_GROUP:
453
6.22M
    xlat_exp_head_verify(node->group);
454
6.22M
    (void)talloc_get_type_abort_const(node->fmt, char);
455
6.22M
    return;
456
457
3.18M
  case XLAT_FUNC:
458
3.18M
    fr_assert(node->call.args->is_argv);
459
460
6.18M
    xlat_exp_foreach(node->call.args, arg) {
461
6.18M
      fr_assert(arg->type == XLAT_GROUP);
462
463
      /*
464
       *  We can't do this yet, because the old function argument parser doesn't do the
465
       *  right thing.
466
       */
467
//      fr_assert(arg->quote == T_BARE_WORD);
468
6.18M
    }
469
470
3.18M
    xlat_exp_head_verify(node->call.args);
471
3.18M
    (void)talloc_get_type_abort_const(node->fmt, char);
472
3.18M
    return;
473
474
976k
  case XLAT_TMPL: {
475
976k
    tmpl_t const *vpt = node->vpt;
476
477
976k
    if (node->quote != node->vpt->quote) {
478
1.40k
      if (node->vpt->quote == T_SOLIDUS_QUOTED_STRING) {
479
        /*
480
         *  m'foo' versus /foo/
481
         */
482
1.40k
        fr_assert(node->quote != T_BARE_WORD);
483
1.40k
      } else {
484
        /*
485
         *  Mismatching quotes are bad.
486
         */
487
0
        fr_assert(node->quote == T_BARE_WORD);
488
0
      }
489
1.40k
    }
490
491
976k
    if (tmpl_is_attr(vpt)) {
492
3.19k
      fr_dict_attr_t const *da;
493
3.19k
      da = tmpl_attr_tail_da(node->vpt);
494
495
3.19k
      if (tmpl_rules_cast(node->vpt) != FR_TYPE_NULL) {
496
        /*
497
         *  Casts must be omitted, unless we're using a cast as a way to get rid
498
         *  of enum names.
499
         */
500
0
        if (tmpl_rules_cast(node->vpt) == da->type) {
501
0
          fr_assert(da->flags.has_value);
502
0
        }
503
504
3.19k
      } else if (node->quote != T_BARE_WORD) {
505
0
        fr_assert(da->type != FR_TYPE_STRING);
506
0
      }
507
508
3.19k
      return;
509
3.19k
    }
510
511
    /*
512
     *  Casts should have been hoisted.
513
     */
514
973k
    if (tmpl_is_data(node->vpt)) {
515
0
      fr_assert(tmpl_rules_cast(node->vpt) == FR_TYPE_NULL);
516
0
    }
517
518
#if 0
519
    /*
520
     *  @todo - xlats SHOULD have been hoisted, unless they're quoted or cast.
521
     */
522
    if (tmpl_is_xlat(node->vpt)) {
523
      fr_assert((node->vpt->quote != T_BARE_WORD) ||
524
          (tmpl_rules_cast(node->vpt) != FR_TYPE_NULL));
525
      return;
526
    }
527
#endif
528
529
973k
    if (tmpl_is_exec(node->vpt) || tmpl_is_exec_unresolved(node->vpt)) {
530
0
      fr_assert(node->quote == T_BACK_QUOTED_STRING);
531
0
      fr_assert(!node->flags.constant);
532
0
      fr_assert(!node->flags.pure);
533
0
      fr_assert(!node->flags.can_purify);
534
0
    }
535
536
973k
    return;
537
976k
  }
538
539
2.10M
  case XLAT_BOX:
540
2.10M
    fr_assert(node->flags.constant);
541
2.10M
    fr_assert(node->flags.pure);
542
//    fr_assert(node->flags.can_purify);
543
2.10M
    break;
544
545
1.30k
  default:
546
1.30k
    break;
547
12.5M
  }
548
12.5M
}
549
550
/** Performs recursive validation of node lists
551
 */
552
void xlat_exp_head_verify(xlat_exp_head_t const *head)
553
9.41M
{
554
9.41M
  (void)talloc_get_type_abort_const(head, xlat_exp_head_t);
555
556
12.4M
  xlat_exp_foreach(head, node) xlat_exp_verify(node);
557
9.41M
}
558
#endif