|
commit 78bc9d39b0b31d647b35131ae45b21a145112192
|
|
Author: Willy Tarreau <w@1wt.eu>
|
|
Date: Fri Nov 12 10:26:18 2021 +0100
|
|
|
|
BUG/MINOR: pools: don't mark ourselves as harmless in DEBUG_UAF mode
|
|
|
|
When haproxy is built with DEBUG_UAF=1, some particularly slow
|
|
allocation functions are used for each pool, and it was not uncommon
|
|
to see the watchdog trigger during performance tests. For this reason
|
|
the allocation functions were surrounded by a pair of thread_harmless
|
|
calls to mention that the function was waiting in slow syscalls. The
|
|
problem is that this also releases functions blocked in thread_isolate()
|
|
which can then start their work.
|
|
|
|
In order to protect against the accidental removal of a shared resource
|
|
in this situation, in 2.5-dev4 with commit ba3ab7907 ("MEDIUM: servers:
|
|
make the server deletion code run under full thread isolation") was added
|
|
thread_isolate_full() for functions which want to be totally protected
|
|
due to being manipulating some data.
|
|
|
|
But this is not sufficient, because there are still places where we
|
|
can allocate/free (thus sleep) under a lock, such as in long call
|
|
chains involving the release of an idle connection. In this case, if
|
|
one thread asks for isolation, one thread might hang in
|
|
pool_alloc_area_uaf() with a lock held (for example the conns_lock
|
|
when coming from conn_backend_get()->h1_takeover()->task_new()), with
|
|
another thread blocked on a lock waiting for that one to release it,
|
|
both keeping their bit clear in the thread_harmless mask, preventing
|
|
the first thread from being released, thus causing a deadlock.
|
|
|
|
In addition to this, it was already seen that the "show fd" CLI handler
|
|
could wake up during a pool_free_area_uaf() with an incompletely
|
|
released memory area while deleting a file descriptor, and be fooled
|
|
showing bad pointers, or during a pool_alloc() on another thread that
|
|
was in the process of registering a freshly allocated connection to a
|
|
new file descriptor.
|
|
|
|
One solution could consist in replacing all thread_isolate() calls by
|
|
thread_isolate_full() but then that makes thread_isolate() useless
|
|
and only shifts the problem by one slot.
|
|
|
|
A better approach could possibly consist in having a way to mark that
|
|
a thread is entering an extremely slow section. Such sections would
|
|
be timed so that this is not abused, and the bit would be used to
|
|
make the watchdog more patient. This would be acceptable as this would
|
|
only affect debugging.
|
|
|
|
The approach used here for now consists in removing the harmless bits
|
|
around the UAF allocator, thus essentially undoing commit 85b2cae63
|
|
("MINOR: pools: make the thread harmless during the mmap/munmap
|
|
syscalls").
|
|
|
|
This is marked as minor because nobody is expected to be running with
|
|
DEBUG_UAF outside of development or serious debugging, so this issue
|
|
cannot affect regular users. It must be backported to stable branches
|
|
that have thread_harmless_now() around the mmap() call.
|
|
|
|
(cherry picked from commit fdf53b4962229b6cfcc5bc11151356c3d92d7023)
|
|
[wt: applied to include/pool-os.h]
|
|
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
|
|
--- a/include/haproxy/pool-os.h
|
|
+++ b/include/haproxy/pool-os.h
|
|
@@ -65,12 +65,8 @@ static inline void pool_free_area(void *
|
|
static inline void *pool_alloc_area(size_t size)
|
|
{
|
|
size_t pad = (4096 - size) & 0xFF0;
|
|
- int isolated;
|
|
void *ret;
|
|
|
|
- isolated = thread_isolated();
|
|
- if (!isolated)
|
|
- thread_harmless_now();
|
|
ret = mmap(NULL, (size + 4095) & -4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
|
if (ret != MAP_FAILED) {
|
|
/* let's dereference the page before returning so that the real
|
|
@@ -83,8 +79,6 @@ static inline void *pool_alloc_area(size
|
|
} else {
|
|
ret = NULL;
|
|
}
|
|
- if (!isolated)
|
|
- thread_harmless_end();
|
|
return ret;
|
|
}
|
|
|
|
@@ -101,9 +95,7 @@ static inline void pool_free_area(void *
|
|
if (pad >= sizeof(void *) && *(void **)(area - sizeof(void *)) != area)
|
|
ABORT_NOW();
|
|
|
|
- thread_harmless_now();
|
|
munmap(area - pad, (size + 4095) & -4096);
|
|
- thread_harmless_end();
|
|
}
|
|
|
|
#endif /* DEBUG_UAF */
|