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 \
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);
/* 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 */
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. */
{
unsigned int counter;
unsigned int failed;
+ unsigned int accept_oom;
};
static struct test_failures *state;
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;
+}
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)
{
/* 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",
--- /dev/null
+/* 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>