The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
request_data.c
Go to the documentation of this file.
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: b55850d4b1ea96d0bbdab66dc874024e0aafd78e $
19  *
20  * @brief Functions for allocating requests and storing internal data in them.
21  * @file src/lib/server/request_data.c
22  *
23  * @copyright 2019 The FreeRADIUS server project
24  */
25 RCSID("$Id: b55850d4b1ea96d0bbdab66dc874024e0aafd78e $")
26 
27 #include <freeradius-devel/util/debug.h>
28 #include <freeradius-devel/server/request_data.h>
29 
30 /** Per-request opaque data, added by modules
31  *
32  */
34  fr_dlist_t list; //!< Next opaque request data struct linked to this request.
35 
36  void const *unique_ptr; //!< Key to lookup request data.
37  int unique_int; //!< Alternative key to lookup request data.
38  char const *type; //!< Opaque type e.g. fr_pair_t, fr_dict_attr_t etc...
39  void *opaque; //!< Opaque data.
40  bool free_on_replace; //!< Whether to talloc_free(opaque) when the request data is removed.
41  bool free_on_parent; //!< Whether to talloc_free(opaque) when the request is freed
42  bool persist; //!< Whether this data should be transferred to a session_entry_t
43  //!< after we're done processing this request.
44 
45 #ifndef NDEBUG
46  char const *file; //!< File where this request data was added.
47  int line; //!< Line where this request data was added.
48 #endif
49 };
50 
51 static char *request_data_description(TALLOC_CTX *ctx, request_data_t *rd)
52 {
53  char *where;
54  char *what;
55  char *out;
56 
57  /*
58  * Where was the request data added
59  */
60 #ifndef NDEBUG
61  where = talloc_typed_asprintf(NULL, " added at %s:%i", rd->file, rd->line);
62 #else
63  where = NULL;
64 #endif
65 
66  /*
67  * What was added
68  */
69  if (rd->type) {
70  what = talloc_typed_asprintf(NULL, "%p (%s)", rd->opaque, rd->type);
71  } else {
72  what = talloc_typed_asprintf(NULL, "%p", rd->opaque);
73  }
74 
75  out = talloc_typed_asprintf(ctx, "[0x%012"PRIxPTR":%i]%s %p, opaque %s%s",
76  (uintptr_t)rd->unique_ptr,
77  rd->unique_int,
78  rd->persist ? "[P]" : "",
79  rd,
80  what,
81  where ? where : "");
82  talloc_free(what);
83  talloc_free(where);
84 
85  return out;
86 }
87 
88 /* Initialise a dlist for storing request data
89  *
90  * @param[in] list to initialise.
91  */
93 {
95 }
96 
97 /** Ensure opaque data is freed by binding its lifetime to the request_data_t
98  *
99  * @param rd Request data being freed.
100  * @return
101  * - 0 if free on parent is false or there's no opaque data.
102  * - ...else whatever the destructor for the opaque data returned.
103  */
105 {
106  char *desc = NULL;
107 
108  /*
109  * In the vast majority of cases the request data will
110  * unlinked from its list before being freed.
111  * But in case it's not, do this now.
112  *
113  * This helps in a very specific case where there's a list
114  * of request_data_t, and the state_ctx that the
115  * request_data_t is parented off is freed without the
116  * request_data_t being unlinked explicitly, but before
117  * the request itself is freed something attempts to access
118  * the request_data_t list, and runs into freed memory.
119  *
120  * It's a similar pattern to structs removing themselves
121  * from trees when they're freed, but with the added bonus
122  * of never running into use after free errors/
123  */
125 
126  if (DEBUG_ENABLED4) desc = request_data_description(rd, rd);
127 
128  if (rd->free_on_parent && rd->opaque) {
129  int ret;
130 
131  DEBUG4("%s - freed with opaque data", desc);
132 
133  ret = talloc_free(rd->opaque);
134  rd->opaque = NULL;
135 
136  return ret;
137  }
138 
139  DEBUG4("%s - freed, but leaving opaque data", desc);
140 
141  return 0;
142 }
143 
144 /** Allocate request data
145  *
146  * @param[in] ctx to allocate request data in.
147  * @return new request data.
148  */
149 static inline request_data_t *request_data_alloc(TALLOC_CTX *ctx)
150 {
151  request_data_t *rd;
152 
153  MEM(rd = talloc_zero(ctx, request_data_t));
154  talloc_set_destructor(rd, _request_data_free);
155 
156  return rd;
157 }
158 
159 /** Add opaque data to a request_t
160  *
161  * The unique ptr is meant to be a module configuration, and the unique
162  * integer allows the caller to have multiple opaque data associated with a request_t.
163  *
164  * @param[in] request to associate data with.
165  * @param[in] unique_ptr Identifier for the data.
166  * @param[in] unique_int Qualifier for the identifier.
167  * @param[in] type Type of data (if talloced)
168  * @param[in] opaque Data to associate with the request. May be NULL.
169  * @param[in] free_on_replace Free opaque data if this request_data is replaced.
170  * @param[in] free_on_parent Free opaque data if the request or session is freed.
171  * Must not be set if the opaque data is also parented by
172  * the request or state (double free).
173  * @param[in] persist Transfer request data to an #fr_state_entry_t, and
174  * add it back to the next request we receive for the
175  * session.
176  * @param[in] file request data was added in.
177  * @param[in] line request data was added on.
178  * @return
179  * - -2 on bad arguments.
180  * - -1 on memory allocation error.
181  * - 0 on success.
182  */
183 int _request_data_add(request_t *request, void const *unique_ptr, int unique_int, char const *type, void *opaque,
184  bool free_on_replace, bool free_on_parent, bool persist,
185 #ifndef NDEBUG
186  char const *file, int line
187 #else
188  UNUSED char const *file, UNUSED int line
189 #endif
190  )
191 {
192  request_data_t *rd = NULL;
193 
194  /*
195  * Request must have a state ctx
196  */
197  fr_assert(request);
198  fr_assert(!persist || request->session_state_ctx);
199  fr_assert(!persist ||
200  (talloc_parent(opaque) == request->session_state_ctx) ||
201  (talloc_parent(opaque) == talloc_null_ctx()));
202  fr_assert(!free_on_parent || (talloc_parent(opaque) != request));
203 
204 #ifndef TALLOC_GET_TYPE_ABORT_NOOP
205  if (type) opaque = _talloc_get_type_abort(opaque, type, __location__);
206 #endif
207 
208  while ((rd = fr_dlist_next(&request->data, rd))) {
209  if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
210 
211  fr_dlist_remove(&request->data, rd); /* Unlink from the list */
212 
213  /*
214  * If caller requires custom behaviour on free
215  * they must set a destructor.
216  */
217  if (rd->free_on_replace && rd->opaque) {
218  RDEBUG4("%s: Freeing %s%s%p at %p:%i via replacement",
219  __FUNCTION__,
220  rd->type ? rd->type : "", rd->type ? " " : "",
221  rd->opaque, rd->unique_ptr, rd->unique_int);
222  talloc_free(rd->opaque);
223  }
224  /*
225  * Need a new one, rd one's parent is wrong.
226  * And no, we can't just steal.
227  */
228  if (rd->persist != persist) {
229  rd->free_on_parent = false;
230  TALLOC_FREE(rd);
231  }
232 
233  break; /* replace the existing entry */
234  }
235 
236  /*
237  * Only alloc new memory if we're not replacing
238  * an existing entry.
239  *
240  * Tie the lifecycle of the data to either the state_ctx
241  * or the request, depending on whether it should
242  * persist or not.
243  */
244  if (!rd) {
245  if (persist) {
246  fr_assert(request->session_state_ctx);
247  rd = request_data_alloc(request->session_state_ctx);
248  } else {
249  rd = request_data_alloc(request);
250  }
251 
252  }
253  if (!rd) return -1;
254 
255  rd->unique_ptr = unique_ptr;
256  rd->unique_int = unique_int;
257  rd->type = type;
258  rd->opaque = opaque;
259  rd->free_on_replace = free_on_replace;
260  rd->free_on_parent = free_on_parent;
261  rd->persist = persist;
262 #ifndef NDEBUG
263  rd->file = file;
264  rd->line = line;
265 #endif
266 
267  fr_dlist_insert_head(&request->data, rd);
268 
269  RDEBUG4("%s: %s%s%p at %p:%i, free_on_replace: %s, free_on_parent: %s, persist: %s",
270  __FUNCTION__,
271  rd->type ? rd->type : "", rd->type ? " " : "",
272  rd->opaque, rd->unique_ptr, rd->unique_int,
273  free_on_replace ? "yes" : "no",
274  free_on_parent ? "yes" : "no",
275  persist ? "yes" : "no");
276 
277  return 0;
278 }
279 
280 /** Get opaque data from a request
281  *
282  * @note The unique ptr is meant to be a module configuration, and the unique
283  * integer allows the caller to have multiple opaque data associated with a request_t.
284  *
285  * @param[in] request to retrieve data from.
286  * @param[in] unique_ptr Identifier for the data.
287  * @param[in] unique_int Qualifier for the identifier.
288  * @return
289  * - NULL if no opaque data could be found.
290  * - the opaque data. The entry holding the opaque data is removed from the request.
291  */
292 void *request_data_get(request_t *request, void const *unique_ptr, int unique_int)
293 {
294  request_data_t *rd = NULL;
295 
296  if (!request) return NULL;
297 
298  while ((rd = fr_dlist_next(&request->data, rd))) {
299  void *ptr;
300 
301  if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
302 
303  ptr = rd->opaque;
304 
305  rd->free_on_parent = false; /* Don't free opaque data we're handing back */
306  fr_dlist_remove(&request->data, rd);
307 
308 #ifndef TALLOC_GET_TYPE_ABORT_NOOP
309  if (rd->type) ptr = _talloc_get_type_abort(ptr, rd->type, __location__);
310 #endif
311 
312  RDEBUG4("%s: %s%s%p at %p:%i retrieved and unlinked",
313  __FUNCTION__,
314  rd->type ? rd->type : "", rd->type ? " " : "",
315  rd->opaque, rd->unique_ptr, rd->unique_int);
316 
317  talloc_free(rd);
318 
319  return ptr;
320  }
321 
322  RDEBUG4("%s: No request data found at %p:%i", __FUNCTION__, unique_ptr, unique_int);
323 
324  return NULL; /* wasn't found, too bad... */
325 }
326 
327 /** Get opaque data from a request without removing it
328  *
329  * @note The unique ptr is meant to be a module configuration, and the unique
330  * integer allows the caller to have multiple opaque data associated with a request_t.
331  *
332  * @param request to retrieve data from.
333  * @param unique_ptr Identifier for the data.
334  * @param unique_int Qualifier for the identifier.
335  * @return
336  * - NULL if no opaque data could be found.
337  * - the opaque data.
338  */
339 void *request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
340 {
341  request_data_t *rd = NULL;
342 
343  if (!request) return NULL;
344 
345  while ((rd = fr_dlist_next(&request->data, rd))) {
346  if ((rd->unique_ptr != unique_ptr) || (rd->unique_int != unique_int)) continue;
347 
348 #ifndef TALLOC_GET_TYPE_ABORT_NOOP
349  if (rd->type) rd->opaque = _talloc_get_type_abort(rd->opaque, rd->type, __location__);
350 #endif
351 
352  RDEBUG4("%s: %s%s%p at %p:%i retrieved",
353  __FUNCTION__,
354  rd->type ? rd->type : "", rd->type ? " " : "",
355  rd->opaque, rd->unique_ptr, rd->unique_int);
356 
357  return rd->opaque;
358  }
359 
360  RDEBUG4("%s: No request data found at %p:%i", __FUNCTION__, unique_ptr, unique_int);
361 
362  return NULL; /* wasn't found, too bad... */
363 }
364 
365 /** Loop over all the request data, pulling out ones matching persist state
366  *
367  * @param[out] out Head of result list.
368  * @param[in] request to search for request_data_t in.
369  * @param[in] persist Whether to pull persistable or non-persistable data.
370  * @return number of request_data_t retrieved.
371  */
373 {
374  int count = 0;
375  request_data_t *rd = NULL, *prev;
376 
377  while ((rd = fr_dlist_next(&request->data, rd))) {
378  if (rd->persist != persist) continue;
379 
380  prev = fr_dlist_remove(&request->data, rd);
382  rd = prev;
383  }
384 
385  return count;
386 }
387 
388 /** Loop over all the request data, copying, then freeing ones matching persist state
389  *
390  * @param[in] ctx To allocate new request_data_t.
391  * @param[out] out Head of result list. If NULL, data
392  * will be reparented in place.
393  * @param[in] request to search for request_data_t in.
394  * @param[in] persist Whether to pull persistable or non-persistable data.
395  * @return number of request_data_t retrieved.
396  */
397 int request_data_by_persistance_reparent(TALLOC_CTX *ctx, fr_dlist_head_t *out, request_t *request, bool persist)
398 {
399  int count = 0;
400  request_data_t *rd = NULL, *new, *prev;
402 
404 
405  while ((rd = fr_dlist_next(&request->data, rd))) {
406  if (rd->persist != persist) continue;
407 
408  prev = fr_dlist_remove(&request->data, rd);
409 
410  new = request_data_alloc(ctx);
411  memcpy(new, rd, sizeof(*new));
412 
413  /*
414  * Clear the list pointers...
415  */
416  memset(&new->list, 0, sizeof(new->list));
417  rd->free_on_parent = false;
418  talloc_free(rd);
419 
420  if (out) {
422  } else {
423  fr_dlist_insert_tail(&head, new);
424  }
425  rd = prev;
426  }
427 
428  if (!out) fr_dlist_move(&request->data, &head);
429 
430  return count;
431 }
432 
433 /** Return how many request data entries exist of a given persistence
434  *
435  * @param[in] request to check in.
436  * @param[in] persist Whether to count persistable or non-persistable data.
437  * @return number of request_data_t that exist in persistable or non-persistable form
438  */
439 int request_data_by_persistance_count(request_t *request, bool persist)
440 {
441  int count = 0;
442  request_data_t *rd = NULL;
443 
444  while ((rd = fr_dlist_next(&request->data, rd))) {
445  if (rd->persist != persist) continue;
446 
447  count++;
448  }
449 
450  return count;
451 }
452 
453 /** Add request data back to a request
454  *
455  * @note May add multiple entries (if they're linked).
456  * @note Will not check for duplicates.
457  *
458  * @param request to add data to.
459  * @param in Data to add.
460  */
462 {
463  fr_dlist_move(&request->data, in);
464 }
465 
466 /** Used for removing data from subrequests that are about to be freed
467  *
468  * @param[in] request to remove persistable data from.
469  */
471 {
473 
475 
476  request_data_by_persistance(&head, request, true);
477 
479 }
480 
481 
483 {
484  request_data_t *rd = NULL;
485 
486  if (fr_dlist_empty(head)) return;
487 
488  while ((rd = fr_dlist_next(head, rd))) {
489  char *desc;
490 
491  desc = request_data_description(NULL, rd);
492  ROPTIONAL(RDEBUG, DEBUG, "%s", desc);
493  talloc_free(desc);
494  }
495 }
496 
498 {
499  request_data_list_dump(request, &request->data);
500 }
501 
502 #ifdef WITH_VERIFY_PTR
503 bool request_data_persistable(request_data_t *rd)
504 {
505  return rd->persist;
506 }
507 
508 /** Verify all request data is parented by the specified context
509  *
510  * @note Only available if built with WITH_VERIFY_PTR
511  *
512  * @param parent that should hold the request data.
513  * @param entry to verify.
514  * @return
515  * - true if chunk lineage is correct.
516  * - false if one of the chunks is parented by something else.
517  */
518 bool request_data_verify_parent(TALLOC_CTX *parent, fr_dlist_head_t *entry)
519 {
520  request_data_t *rd = NULL;
521 
522  while ((rd = fr_dlist_next(entry, rd))) if (talloc_parent(rd) != parent) return false;
523 
524  return true;
525 }
526 #endif
int const char * file
Definition: acutest.h:702
int const char int line
Definition: acutest.h:702
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_slen_t in
Definition: dict.h:645
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition: dlist.h:908
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
Definition: dlist.h:146
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
Definition: dlist.h:501
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
static int fr_dlist_move(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
Merge two lists, inserting the source at the tail of the destination.
Definition: dlist.h:763
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:275
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition: dlist.h:338
Head of a doubly linked list.
Definition: dlist.h:51
Entry in a doubly linked list.
Definition: dlist.h:41
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define DEBUG_ENABLED4
True if global debug level 1-3 messages are enabled.
Definition: log.h:260
#define DEBUG4(_fmt,...)
Definition: log.h:267
#define RDEBUG4(fmt,...)
Definition: log.h:344
talloc_free(reap)
#define RDEBUG(fmt,...)
Definition: radclient.h:53
bool free_on_replace
Whether to talloc_free(opaque) when the request data is removed.
Definition: request_data.c:40
char const * file
File where this request data was added.
Definition: request_data.c:46
int request_data_by_persistance_count(request_t *request, bool persist)
Return how many request data entries exist of a given persistence.
Definition: request_data.c:439
int request_data_by_persistance(fr_dlist_head_t *out, request_t *request, bool persist)
Loop over all the request data, pulling out ones matching persist state.
Definition: request_data.c:372
fr_dlist_t list
Next opaque request data struct linked to this request.
Definition: request_data.c:34
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
Definition: request_data.c:292
int _request_data_add(request_t *request, void const *unique_ptr, int unique_int, char const *type, void *opaque, bool free_on_replace, bool free_on_parent, bool persist, char const *file, int line)
Add opaque data to a request_t.
Definition: request_data.c:183
static char * request_data_description(TALLOC_CTX *ctx, request_data_t *rd)
Definition: request_data.c:51
void * opaque
Opaque data.
Definition: request_data.c:39
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
Definition: request_data.c:339
static int _request_data_free(request_data_t *rd)
Ensure opaque data is freed by binding its lifetime to the request_data_t.
Definition: request_data.c:104
void request_data_list_init(fr_dlist_head_t *data)
Definition: request_data.c:92
void const * unique_ptr
Key to lookup request data.
Definition: request_data.c:36
bool persist
Whether this data should be transferred to a session_entry_t after we're done processing this request...
Definition: request_data.c:42
void request_data_restore(request_t *request, fr_dlist_head_t *in)
Add request data back to a request.
Definition: request_data.c:461
static request_data_t * request_data_alloc(TALLOC_CTX *ctx)
Allocate request data.
Definition: request_data.c:149
char const * type
Opaque type e.g. fr_pair_t, fr_dict_attr_t etc...
Definition: request_data.c:38
void request_data_dump(request_t *request)
Definition: request_data.c:497
int line
Line where this request data was added.
Definition: request_data.c:47
bool free_on_parent
Whether to talloc_free(opaque) when the request is freed.
Definition: request_data.c:41
void request_data_persistable_free(request_t *request)
Used for removing data from subrequests that are about to be freed.
Definition: request_data.c:470
int request_data_by_persistance_reparent(TALLOC_CTX *ctx, fr_dlist_head_t *out, request_t *request, bool persist)
Loop over all the request data, copying, then freeing ones matching persist state.
Definition: request_data.c:397
void request_data_list_dump(request_t *request, fr_dlist_head_t *head)
Definition: request_data.c:482
int unique_int
Alternative key to lookup request data.
Definition: request_data.c:37
Per-request opaque data, added by modules.
Definition: request_data.c:33
return count
Definition: module.c:175
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_aka_sim_id_type_t type
void * talloc_null_ctx(void)
Retrieve the current talloc NULL ctx.
Definition: talloc.c:49
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380
static fr_slen_t head
Definition: xlat.h:408
static fr_slen_t parent
Definition: pair.h:844
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984