mirror of git://sourceware.org/git/glibc.git
Compare commits
2 Commits
3fd794264e
...
855a67c3cc
Author | SHA1 | Date |
---|---|---|
|
855a67c3cc | |
|
480660e270 |
|
@ -60,8 +60,11 @@ do_test (void)
|
|||
*(p++) = '/';
|
||||
p[path_len - (p - path) - 1] = '\0';
|
||||
|
||||
/* This call crashes before the fix for bz22786 on 32-bit platforms. */
|
||||
/* This call crashes before the fix for bz22786 on 32-bit platforms.
|
||||
It may trigger an OOM event. */
|
||||
support_accept_oom (true);
|
||||
p = realpath (path, NULL);
|
||||
support_accept_oom (false);
|
||||
TEST_VERIFY (p == NULL);
|
||||
/* For 64-bit platforms readlink return ENAMETOOLONG, while for 32-bit
|
||||
realpath will try to allocate a buffer larger than PTRDIFF_MAX. */
|
||||
|
|
|
@ -334,6 +334,7 @@ tests = \
|
|||
tst-support-open-dev-null-range \
|
||||
tst-support-openpty \
|
||||
tst-support-process_state \
|
||||
tst-support_accept_oom \
|
||||
tst-support_blob_repeat \
|
||||
tst-support_capture_subprocess \
|
||||
tst-support_descriptors \
|
||||
|
|
|
@ -196,9 +196,11 @@ void support_test_compare_string_wide (const wchar_t *left,
|
|||
const char *left_expr,
|
||||
const char *right_expr);
|
||||
|
||||
/* Internal function called by the test driver. */
|
||||
/* Internal functions called by the test driver. */
|
||||
int support_report_failure (int status)
|
||||
__attribute__ ((weak, warn_unused_result));
|
||||
int support_is_oom_accepted (void)
|
||||
__attribute__ ((weak, warn_unused_result));
|
||||
|
||||
/* Internal function used to test the failure recording framework. */
|
||||
void support_record_failure_reset (void);
|
||||
|
|
|
@ -239,6 +239,15 @@ int support_open_dev_null_range (int num, int flags, mode_t mode);
|
|||
/* Check if kernel supports set VMA range name. */
|
||||
extern bool support_set_vma_name_supported (void);
|
||||
|
||||
/* If invoked with a true argument, it instructs the supervising
|
||||
process to ignore unexpected termination of the test process,
|
||||
likely due to an OOM error. (This can theoretically mask other
|
||||
test errors, so it should be used sparingly.)
|
||||
|
||||
If invoked with a false argument, the default behavior is restored,
|
||||
and OOM-induced errors result in test failure. */
|
||||
void support_accept_oom (bool);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* SUPPORT_H */
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
failure is detected, so that even if the counter wraps around to
|
||||
zero, the failure of a test can be detected.
|
||||
|
||||
If the accept_oom member is not zero, the supervisor process will
|
||||
use heuristics to suppress process termination due to OOM
|
||||
conditions.
|
||||
|
||||
The init constructor function below puts *state on a shared
|
||||
anonymous mapping, so that failure reports from subprocesses
|
||||
propagate to the parent process. */
|
||||
|
@ -38,6 +42,7 @@ struct test_failures
|
|||
{
|
||||
unsigned int counter;
|
||||
unsigned int failed;
|
||||
unsigned int accept_oom;
|
||||
};
|
||||
static struct test_failures *state;
|
||||
|
||||
|
@ -122,3 +127,34 @@ support_record_failure_barrier (void)
|
|||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
support_accept_oom (bool onoff)
|
||||
{
|
||||
if (onoff)
|
||||
{
|
||||
/* One thread detects the overflow. */
|
||||
if (__atomic_fetch_add (&state->accept_oom, 1, __ATOMIC_RELAXED)
|
||||
== UINT_MAX)
|
||||
{
|
||||
puts ("error: OOM acceptance counter overflow");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* One thread detects the underflow. */
|
||||
if (__atomic_fetch_add (&state->accept_oom, -1, __ATOMIC_RELAXED)
|
||||
== 0)
|
||||
{
|
||||
puts ("error: OOM acceptance counter underflow");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
support_is_oom_accepted (void)
|
||||
{
|
||||
return __atomic_load_n (&state->accept_oom, __ATOMIC_RELAXED) != 0;
|
||||
}
|
||||
|
|
|
@ -264,6 +264,20 @@ adjust_exit_status (int status)
|
|||
return status;
|
||||
}
|
||||
|
||||
/* Return true if the exit status looks like it may have been
|
||||
triggered by kernel OOM handling, and support_accept_oom (true) was
|
||||
active in the test process. This is a very approximate check.
|
||||
Unfortunately, the SI_KERNEL value for si_code in siginfo_t is not
|
||||
observable via waitid (it gets translated to CLD_KILLED. */
|
||||
static bool
|
||||
accept_oom_heuristic (int status)
|
||||
{
|
||||
return (WIFSIGNALED (status)
|
||||
&& WTERMSIG (status) == SIGKILL
|
||||
&& support_is_oom_accepted != NULL
|
||||
&& support_is_oom_accepted ());
|
||||
}
|
||||
|
||||
int
|
||||
support_test_main (int argc, char **argv, const struct test_config *config)
|
||||
{
|
||||
|
@ -497,6 +511,11 @@ support_test_main (int argc, char **argv, const struct test_config *config)
|
|||
/* Process was killed by timer or other signal. */
|
||||
else
|
||||
{
|
||||
if (accept_oom_heuristic (status))
|
||||
{
|
||||
puts ("Heuristically determined OOM termination; SIGKILL ignored");
|
||||
exit (adjust_exit_status (EXIT_UNSUPPORTED));
|
||||
}
|
||||
if (config->expected_signal == 0)
|
||||
{
|
||||
printf ("Didn't expect signal from child: got `%s'\n",
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/* Test that OOM error suppression works.
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This test reacts to the reject_oom and inject_error environment
|
||||
variables. It is never executed automatically because it can run
|
||||
for a very long time on large systems, and is generally stressful
|
||||
to the system. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support.h>
|
||||
#include <support/check.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* If true, support_accept_oom is called. */
|
||||
static bool accept_oom;
|
||||
|
||||
/* System page size. Allocations are always at least that large. */
|
||||
static size_t page_size;
|
||||
|
||||
/* All allocated bytes. */
|
||||
static size_t total_bytes;
|
||||
|
||||
/* Try to allocate SIZE bytes of memory, and ensure that is backed by
|
||||
actual memory. */
|
||||
static bool
|
||||
populate_memory (size_t size)
|
||||
{
|
||||
TEST_COMPARE (size % page_size, 0);
|
||||
char *ptr = mmap (NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (ptr == MAP_FAILED)
|
||||
return false;
|
||||
|
||||
if (accept_oom)
|
||||
support_accept_oom (true);
|
||||
|
||||
/* Ensure that the kernel allocates backing storage. Make the pages
|
||||
distinct using the total_bytes counter. */
|
||||
for (size_t offset = 0; offset < size; offset += page_size)
|
||||
{
|
||||
memcpy (ptr + offset, &total_bytes, sizeof (total_bytes));
|
||||
total_bytes += page_size;
|
||||
}
|
||||
|
||||
if (accept_oom)
|
||||
support_accept_oom (false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
if (getenv ("oom_test_active") == NULL)
|
||||
{
|
||||
puts ("info: This test does nothing by default.");
|
||||
puts ("info: Set the oom_test_active environment variable to enable it.");
|
||||
puts ("info: Consider testing with inject_error and reject_oom as well.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
accept_oom = getenv ("reject_oom") == NULL;
|
||||
|
||||
page_size = sysconf (_SC_PAGESIZE);
|
||||
size_t size = page_size;
|
||||
|
||||
/* The environment variable can be set to trigger a test failure.
|
||||
The OOM event should not obscure this error. */
|
||||
TEST_COMPARE_STRING (getenv ("inject_error"), NULL);
|
||||
|
||||
/* Grow the allocation until allocation fails. */
|
||||
while (true)
|
||||
{
|
||||
size_t new_size = 2 * size;
|
||||
if (new_size == 0 || !populate_memory (new_size))
|
||||
break;
|
||||
size = new_size;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!populate_memory (size))
|
||||
{
|
||||
/* Decrease size and see if the allocation succeeds. */
|
||||
size /= 2;
|
||||
if (size < page_size)
|
||||
FAIL_UNSUPPORTED ("could not trigger OOM"
|
||||
" after allocating %zu bytes",
|
||||
total_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
Loading…
Reference in New Issue