@harrysintonen@infosec.exchange
Post #2505356
2025-09-10 19:39 UTC
Replies (7)
-
@harrysintonen@infosec.exchange 2025-09-10 20:48
The code in question is mirrored here https://github.com/coreutils/gnulib/blob/3aca72031cf0f856c3354df64570358c929263ca/lib/pagealign_alloc.c
-
@harrysintonen@infosec.exchange 2025-09-10 20:55
Presumably mmap should always return pagesize aligned memory, so it is a bit puzzling why the mmap codepath uses the list at all. Possibly an oversight there as well?
-
@harrysintonen@infosec.exchange 2025-09-10 21:33
So how does CVS use pagealign_xalloc? Like this: /* Allocate more buffer_data structures. */ /* Get a new buffer_data structure. */ static struct buffer_data * get_buffer_data (void) { struct buffer_data *ret; ret = xmalloc (sizeof (struct buffer_data)); ret->text = pagealign_xalloc (BUFFER_DATA_SIZE); return ret; } Surely BUFFER_DATA_SIZE will be something sensible? Unfortunately it is not: #define BUFFER_DATA_SIZE getpagesize () So it will by create total_data_size / pagesize number of list nodes in the linear list. Maybe it's not that bad if the nodes are released in an optimal order? The pagealign code stores new nodes always to the head of its list: new_node->next = memnode_table; memnode_table = new_node; The datanodes in CVS code are however inserted into a list tail: newdata = get_buffer_data (); if (newdata == NULL) { (*buf->memory_error) (buf); return; } if (buf->data == NULL) buf->data = newdata; else buf->last->next = newdata; newdata->next = NULL; buf->last = newdata; This creates a pathological situation where the nodes in the aligned list are in worst possible order as buf_free_datas() walks the internal list in first to last node, calling the pagealign_free: static inline void buf_free_datas (struct buffer_data *first, struct buffer_data *last) { struct buffer_data *b, *n, *p; b = first; do { p = b; n = b->next; pagealign_free (b->text); free (b); b = n; } while (p != last); } In short: This is very bad. It will be slow as heck as soon as large amounts of data is processed by this code. So imagine you have 2GB buffer allocated by using this code on a system that has 4KB pagesize. This would result in 524288 nodes. Each node would be stored in two lists, in first one they're last-head and in the other they're last-tail. When the buf_free_datas is called for this buffer, it will walk totalnodes - index pagealign nodes for each of the released nodes. First iteration is (524288 - 1) "unnecessary" node walks, second (524288 - 2) and so forth. In other terms "sum of all integers smaller than itself", so in total totalnodes * (totalnodes - 1) / 2 extra operations. This gives 137438691328 iterations.
-
@harrysintonen@infosec.exchange 2025-09-11 10:13
The fix to Gnulib is here: https://cgit.git.savannah.gnu.org/cgit/gnulib.git/commit/?id=7c96b402be00117225a6943ab53bb9e5e6e2c02b The most clean solution was to prefer posix_memalign over mmap. It was also pointed out that according to POSIX, mmap can return non-pagesize-aligned pointers, making my idea of avoiding the use of the list for the USE_MMAP case non-portable. It'd work for most platforms, but it could then break for some. This is something that Gnulib, of course, cannot do. Another idea was to use some more suitable data structure with a sensible search performance over the linked list. However, using a more exotic data structure (hash, tree, etc.) would likely be a bit of overkill and wasn't deemed worth the amount of code complexity it'd add. Finally, placing the allocation pointer in a negative offset of the aligned pointer would lead to significant allocation overhead due to it having to allocate at least one extra full page worth of memory - at least if the code would need to stay portable, as is the case with Gnulib.
-
@joncruz@mstdn.social 2025-09-11 02:16
@harrysintonen@infosec.exchange 😱
-
@gnomon@mastodon.social 2025-09-11 04:11
@harrysintonen@infosec.exchange this is an excellent write-up, thank you!
-
@aperezdc@oldbytes.space 2025-09-11 18:30
@harrysintonen@infosec.exchange very nice writeup, this reads like a post from https://www.tumblr.com/accidentallyquadratic — too bad it hasn't received updates since 2019, because surely there's more issues lurking in the corners of many software projects.