Message ID | 20180513012325.877-1-ebiggers3@gmail.com |
---|---|
State | Superseded |
Headers | show |
Series | syscalls/shmctl05: new test for IPC file use-after-free bug | expand |
Hi! > +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c > @@ -0,0 +1,113 @@ > +/* > + * Copyright (c) 2018 Google, Inc. > + * > + * This program is free software: you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation, either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program, if not, see <http://www.gnu.org/licenses/>. > + */ > + > +/* > + * Regression test for commit 3f05317d9889 ("ipc/shm: fix use-after-free of shm > + * file via remap_file_pages()"). This bug allowed the remap_file_pages() > + * syscall to use the file of a System V shared memory segment after its ID had > + * been reallocated and the file freed. This test reproduces the bug as a NULL > + * pointer dereference in touch_atime(), although it's a race condition so it's > + * not guaranteed to work. This test is based on the reproducer provided in the > + * fix's commit message. > + */ Have you considered using the fuzzy sync library here? https://github.com/linux-test-project/ltp/blob/master/include/tst_fuzzy_sync.h
Hi! > Have you considered using the fuzzy sync library here? > > https://github.com/linux-test-project/ltp/blob/master/include/tst_fuzzy_sync.h I've tried to rewrite the test so that it uses the fuzzy sync library to synchronize the remap_file_page syscall againts the IPC_RMID but for some reason that does not seem to trigger the issues for me, while the original reproducer triggers it just fine. There must be some subtle difference, maybe we need to train branch predictor with the loop that calls the remap_file_pages, maybe it's something else. So I guess that the best solution would be merging the testcase as it is, however for me the test timeouts on broken kernel as the test process just hangs there which produces misleading test error message. Maybe we just need to add .timeout_is_failure flag to the test structure for these kind of testcases so that the test library will hint the tester that this timeout likely means that the kernel bug has been reproduced.
On Tue, Jun 26, 2018 at 01:35:47PM +0200, Cyril Hrubis wrote: > Hi! > > Have you considered using the fuzzy sync library here? > > > > https://github.com/linux-test-project/ltp/blob/master/include/tst_fuzzy_sync.h > > I've tried to rewrite the test so that it uses the fuzzy sync library to > synchronize the remap_file_page syscall againts the IPC_RMID but for > some reason that does not seem to trigger the issues for me, while the > original reproducer triggers it just fine. There must be some subtle > difference, maybe we need to train branch predictor with the loop that > calls the remap_file_pages, maybe it's something else. > > So I guess that the best solution would be merging the testcase as it > is, however for me the test timeouts on broken kernel as the test > process just hangs there which produces misleading test error message. > Maybe we just need to add .timeout_is_failure flag to the test structure > for these kind of testcases so that the test library will hint the > tester that this timeout likely means that the kernel bug has been > reproduced. > Hi Cyril, sorry for not responding sooner. A few weeks ago I did try using fuzzy sync but I found it confusing to use, and it seemed to not provide exactly what is needed. Namely, it seems to only support synchronizing the threads at a fixed point, but actually it's usually unknown exactly how things need to be timed to reproduce race conditions -- which is why sleeps of random amounts, though naive, can work well. Nevertheless, I did actually get the fuzzy sync version to reproduce the bug for me a bit more often than the random usleep version, using the following code. I'm not yet convinced it's really better as I think it may be more unreliable for some people, but you can try it and see how well it works for you. I'm not sure why the original test times out for you. Is it hung in the kernel, or is it looping in userspace? Here's the alternate shmctl05.c to try/consider: /* * Copyright (c) 2018 Google, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program, if not, see <http://www.gnu.org/licenses/>. */ /* * Regression test for commit 3f05317d9889 ("ipc/shm: fix use-after-free of shm * file via remap_file_pages()"). This bug allowed the remap_file_pages() * syscall to use the file of a System V shared memory segment after its ID had * been reallocated and the file freed. This test reproduces the bug as a NULL * pointer dereference in touch_atime(), although it's a race condition so it's * not guaranteed to work. This test is based on the reproducer provided in the * fix's commit message. */ #include "tst_test.h" #include "tst_safe_sysv_ipc.h" #include "lapi/syscalls.h" #include "tst_fuzzy_sync.h" #include "tst_safe_pthread.h" #define ATTEMPTS 0x100000 static struct tst_fzsync_pair fzsync_pair = TST_FZSYNC_PAIR_INIT; static pthread_t thrd; /* * Thread 2: repeatedly remove the shm ID and reallocate it again for a * new shm segment. */ static void *thrproc(void *p LTP_ATTRIBUTE_UNUSED) { int id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); while (tst_fzsync_wait_update_b(&fzsync_pair)) { tst_fzsync_wait_b(&fzsync_pair); tst_fzsync_delay_b(&fzsync_pair); tst_fzsync_time_b(&fzsync_pair); SAFE_SHMCTL(id, IPC_RMID, NULL); id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); tst_fzsync_wait_b(&fzsync_pair); } return NULL; } static void setup(void) { /* Skip test if either remap_file_pages() or SysV IPC is unavailable */ tst_syscall(__NR_remap_file_pages, NULL, 0, 0, 0, 0); tst_syscall(__NR_shmctl, 0xF00F, IPC_RMID, NULL); SAFE_PTHREAD_CREATE(&thrd, NULL, thrproc, NULL); } static void do_test(void) { int i; /* * Thread 1: repeatedly attach a shm segment, then remap it until the ID * seems to have been removed by the other process. */ for (i = 0; i < ATTEMPTS; i++) { int id; void *addr; tst_fzsync_wait_update_a(&fzsync_pair); id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); addr = shmat(id, NULL, 0); if (addr == (void *)-1L) tst_brk(TBROK | TERRNO, "Unexpected shmat() error"); tst_fzsync_wait_a(&fzsync_pair); tst_fzsync_delay_a(&fzsync_pair); tst_fzsync_time_a(&fzsync_pair); do { /* This is the system call that crashed */ TEST(syscall(__NR_remap_file_pages, addr, 4096, 0, 0, 0)); } while (TEST_RETURN == 0); if (TEST_ERRNO != EIDRM && TEST_ERRNO != EINVAL) { tst_brk(TBROK | TTERRNO, "Unexpected remap_file_pages() error"); } tst_fzsync_wait_a(&fzsync_pair); } tst_res(TPASS, "didn't crash"); } static void cleanup(void) { if (thrd) { tst_fzsync_pair_exit(&fzsync_pair); SAFE_PTHREAD_JOIN(thrd, NULL); } shmctl(0xF00F, IPC_RMID, NULL); } static struct tst_test test = { .setup = setup, .test_all = do_test, .cleanup = cleanup, };
Hi! > Hi Cyril, sorry for not responding sooner. A few weeks ago I did try using > fuzzy sync but I found it confusing to use, and it seemed to not provide exactly > what is needed. Namely, it seems to only support synchronizing the threads at a > fixed point, but actually it's usually unknown exactly how things need to be > timed to reproduce race conditions -- which is why sleeps of random amounts, > though naive, can work well. The idea behind it is that we try to synchronize two racing syscalls on entering the kernel and we do expect the system to introduce random delays that cause the race to be hit, which works good enough most of the time. We are also planing to introduce random delays into the fuzzy sync library to see if that would help to reproduce races. As for the confusing interface, that is one of the pain points. I've opened an issue https://github.com/linux-test-project/ltp/issues/338 and I do hope to simplify the usage. > Nevertheless, I did actually get the fuzzy sync version to reproduce the bug for > me a bit more often than the random usleep version, using the following code. > I'm not yet convinced it's really better as I think it may be more unreliable > for some people, but you can try it and see how well it works for you. Thanks, I will look at it. > I'm not sure why the original test times out for you. Is it hung in the kernel, > or is it looping in userspace? Looks like the child process gets stuck in call_rwsem_down_read_failed forever, the parent then gets stuck in wait() until it's killed by the test library. We do check if the main test pid is killable and issue a message "kernel bug found" message if the main test pid cannot be killed. I guess that I can change the test library to watch for all processes in the process group as well since as it is the test just produces "timeouted" and leaves one process behind which is a bit confusing output. Also we should probably set the test timeout to be twice of the expected runtime to make it fail faster :-). > Here's the alternate shmctl05.c to try/consider: > > /* > * Copyright (c) 2018 Google, Inc. > * > * This program is free software: you can redistribute it and/or modify > * it under the terms of the GNU General Public License as published by > * the Free Software Foundation, either version 2 of the License, or > * (at your option) any later version. > * > * This program 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 General Public License for more details. > * > * You should have received a copy of the GNU General Public License > * along with this program, if not, see <http://www.gnu.org/licenses/>. > */ > > /* > * Regression test for commit 3f05317d9889 ("ipc/shm: fix use-after-free of shm > * file via remap_file_pages()"). This bug allowed the remap_file_pages() > * syscall to use the file of a System V shared memory segment after its ID had > * been reallocated and the file freed. This test reproduces the bug as a NULL > * pointer dereference in touch_atime(), although it's a race condition so it's > * not guaranteed to work. This test is based on the reproducer provided in the > * fix's commit message. > */ > > #include "tst_test.h" > #include "tst_safe_sysv_ipc.h" > #include "lapi/syscalls.h" > #include "tst_fuzzy_sync.h" > #include "tst_safe_pthread.h" > > #define ATTEMPTS 0x100000 > > static struct tst_fzsync_pair fzsync_pair = TST_FZSYNC_PAIR_INIT; > > static pthread_t thrd; > > /* > * Thread 2: repeatedly remove the shm ID and reallocate it again for a > * new shm segment. > */ > static void *thrproc(void *p LTP_ATTRIBUTE_UNUSED) > { > int id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); > > while (tst_fzsync_wait_update_b(&fzsync_pair)) { > > tst_fzsync_wait_b(&fzsync_pair); > tst_fzsync_delay_b(&fzsync_pair); > tst_fzsync_time_b(&fzsync_pair); > > SAFE_SHMCTL(id, IPC_RMID, NULL); > id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); > tst_fzsync_wait_b(&fzsync_pair); > } > return NULL; > } > > static void setup(void) > { > /* Skip test if either remap_file_pages() or SysV IPC is unavailable */ > tst_syscall(__NR_remap_file_pages, NULL, 0, 0, 0, 0); > tst_syscall(__NR_shmctl, 0xF00F, IPC_RMID, NULL); > > SAFE_PTHREAD_CREATE(&thrd, NULL, thrproc, NULL); > } > > static void do_test(void) > { > int i; > > /* > * Thread 1: repeatedly attach a shm segment, then remap it until the ID > * seems to have been removed by the other process. > */ > for (i = 0; i < ATTEMPTS; i++) { > int id; > void *addr; > > tst_fzsync_wait_update_a(&fzsync_pair); > id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); > addr = shmat(id, NULL, 0); > if (addr == (void *)-1L) > tst_brk(TBROK | TERRNO, "Unexpected shmat() error"); > tst_fzsync_wait_a(&fzsync_pair); > tst_fzsync_delay_a(&fzsync_pair); > tst_fzsync_time_a(&fzsync_pair); > > do { > /* This is the system call that crashed */ > TEST(syscall(__NR_remap_file_pages, addr, 4096, > 0, 0, 0)); > } while (TEST_RETURN == 0); > > if (TEST_ERRNO != EIDRM && TEST_ERRNO != EINVAL) { > tst_brk(TBROK | TTERRNO, > "Unexpected remap_file_pages() error"); > } > tst_fzsync_wait_a(&fzsync_pair); > } > > tst_res(TPASS, "didn't crash"); > } > > static void cleanup(void) > { > if (thrd) { > tst_fzsync_pair_exit(&fzsync_pair); > SAFE_PTHREAD_JOIN(thrd, NULL); > } > shmctl(0xF00F, IPC_RMID, NULL); > } > > static struct tst_test test = { > .setup = setup, > .test_all = do_test, > .cleanup = cleanup, > };
Hi! Looks like if we drop the callibration from the test and use the fuzzy sync library only as a spinlocks to synchronize the IPC_RMID against the remap_file_pages the bug gets triggered very reliably for me.
On Wed, Jun 27, 2018 at 03:59:03PM +0200, Cyril Hrubis wrote: > Hi! > Looks like if we drop the callibration from the test and use the fuzzy > sync library only as a spinlocks to synchronize the IPC_RMID against the > remap_file_pages the bug gets triggered very reliably for me. > > -- > Cyril Hrubis > chrubis@suse.cz > > /* > * Copyright (c) 2018 Google, Inc. > * > * This program is free software: you can redistribute it and/or modify > * it under the terms of the GNU General Public License as published by > * the Free Software Foundation, either version 2 of the License, or > * (at your option) any later version. > * > * This program 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 General Public License for more details. > * > * You should have received a copy of the GNU General Public License > * along with this program, if not, see <http://www.gnu.org/licenses/>. > */ > > /* > * Regression test for commit 3f05317d9889 ("ipc/shm: fix use-after-free of shm > * file via remap_file_pages()"). This bug allowed the remap_file_pages() > * syscall to use the file of a System V shared memory segment after its ID had > * been reallocated and the file freed. This test reproduces the bug as a NULL > * pointer dereference in touch_atime(), although it's a race condition so it's > * not guaranteed to work. This test is based on the reproducer provided in the > * fix's commit message. > */ > > #include "tst_test.h" > #include "tst_safe_sysv_ipc.h" > #include "lapi/syscalls.h" > #include "tst_fuzzy_sync.h" > #include "tst_safe_pthread.h" > #include "tst_timer.h" > > #include <stdio.h> > > static struct tst_fzsync_pair fzsync_pair = TST_FZSYNC_PAIR_INIT; > > static pthread_t thrd; > > /* > * Thread 2: repeatedly remove the shm ID and reallocate it again for a > * new shm segment. > */ > static void *thrproc(void *unused) > { > int id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); > > for (;;) { > if (!tst_fzsync_wait_b(&fzsync_pair)) > break; > SAFE_SHMCTL(id, IPC_RMID, NULL); > id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); > if (!tst_fzsync_wait_b(&fzsync_pair)) > break; > } > return unused; > } > > static void setup(void) > { > /* Skip test if either remap_file_pages() or SysV IPC is unavailable */ > tst_syscall(__NR_remap_file_pages, NULL, 0, 0, 0, 0); > tst_syscall(__NR_shmctl, 0xF00F, IPC_RMID, NULL); > > SAFE_PTHREAD_CREATE(&thrd, NULL, thrproc, NULL); > } > > static void do_test(void) > { > tst_timer_start(CLOCK_MONOTONIC); > > /* > * Thread 1: repeatedly attach a shm segment, then remap it until the ID > * seems to have been removed by the other process. > */ > while (tst_timer_elapsed_ms() < 5000) { > int id; > void *addr; > > id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); > addr = shmat(id, NULL, 0); > if (addr == (void *)-1L) > tst_brk(TBROK | TERRNO, "Unexpected shmat() error"); > tst_fzsync_wait_a(&fzsync_pair); > do { > /* This is the system call that crashed */ > TEST(syscall(__NR_remap_file_pages, addr, 4096, > 0, 0, 0)); > } while (TEST_RETURN == 0); > > if (TEST_ERRNO != EIDRM && TEST_ERRNO != EINVAL) { > tst_brk(TBROK | TTERRNO, > "Unexpected remap_file_pages() error"); > } > tst_fzsync_wait_a(&fzsync_pair); > } > > tst_res(TPASS, "didn't crash"); > } > > static void cleanup(void) > { > if (thrd) { > tst_fzsync_pair_exit(&fzsync_pair); > SAFE_PTHREAD_JOIN(thrd, NULL); > } > shmctl(0xF00F, IPC_RMID, NULL); > } > > static struct tst_test test = { > .timeout = 20, > .setup = setup, > .test_all = do_test, > .cleanup = cleanup, > }; Hi, this works well for me too -- thanks! Though, IIUC it relies on scheduling nondeterminism to hit the race. It might help reproducing bugs like this if tst_fzsync_wait_*() cycled through different delay deltas between the two threads. Also with a fixed kernel, to make the test pass rather than timing out, I had to change while (tst_timer_elapsed_ms() < 5000) to while (tst_timer_stop(), tst_timer_elapsed_ms() < 5000) It would be nice if there was a simpler supported way to implement time-based tests like this. E.g. the test framework could just save the start time automatically for all tests, and then a single function call could return the time elapsed so far. Anyway, should I go ahead and send a formal v2 patch? Thanks, Eric
Hi! > Hi, this works well for me too -- thanks! Though, IIUC it relies on scheduling > nondeterminism to hit the race. It might help reproducing bugs like this if > tst_fzsync_wait_*() cycled through different delay deltas between the two > threads. We were talking about something like this with Richard recently, but in the end we agreed to try to apply the library to a few more testcases to get a better picture of the real world requirements and obstackles. I guess that synchronizing with spinlocks like this test does then introducing slowly graduating delay before we call one of the syscalls up to some reasonable upper bound a few times sounds reasonable. We would still have to measure how much time do the syscalls spend in the kernel so that we have a reasonable estimate for the upper bound, but that should be doable. > Also with a fixed kernel, to make the test pass rather than timing out, I had to > change > > while (tst_timer_elapsed_ms() < 5000) > > to > > while (tst_timer_stop(), tst_timer_elapsed_ms() < 5000) > > It would be nice if there was a simpler supported way to implement time-based > tests like this. E.g. the test framework could just save the start time > automatically for all tests, and then a single function call could return the > time elapsed so far. Ah, right, let me fix that first. The original purpose for the timer library is to measure time spend doing some operation so the action to sample the end time and to convert it to interval was separated intentionally. I will add tst_timer_expired_ms() that could be used as: tst_timer_start(CLOCK_MONOTONIC); while (!tst_timer_expired_ms(5000)) { ... } > Anyway, should I go ahead and send a formal v2 patch? Just let me push the change to the timer library first :-).
diff --git a/runtest/syscalls b/runtest/syscalls index 97bda7e45..9d47a9336 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -1188,6 +1188,7 @@ shmctl01 shmctl01 shmctl02 shmctl02 shmctl03 shmctl03 shmctl04 shmctl04 +shmctl05 shmctl05 shmdt01 shmdt01 shmdt02 shmdt02 diff --git a/runtest/syscalls-ipc b/runtest/syscalls-ipc index de32c6ba9..7c2c40beb 100644 --- a/runtest/syscalls-ipc +++ b/runtest/syscalls-ipc @@ -57,6 +57,7 @@ shmctl01 shmctl01 shmctl02 shmctl02 shmctl03 shmctl03 shmctl04 shmctl04 +shmctl05 shmctl05 shmdt01 shmdt01 shmdt02 shmdt02 diff --git a/testcases/kernel/syscalls/ipc/shmctl/.gitignore b/testcases/kernel/syscalls/ipc/shmctl/.gitignore index 9f5ac37ff..d6777e3b8 100644 --- a/testcases/kernel/syscalls/ipc/shmctl/.gitignore +++ b/testcases/kernel/syscalls/ipc/shmctl/.gitignore @@ -2,3 +2,4 @@ /shmctl02 /shmctl03 /shmctl04 +/shmctl05 diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c new file mode 100644 index 000000000..6771b1445 --- /dev/null +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl05.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018 Google, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program, if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Regression test for commit 3f05317d9889 ("ipc/shm: fix use-after-free of shm + * file via remap_file_pages()"). This bug allowed the remap_file_pages() + * syscall to use the file of a System V shared memory segment after its ID had + * been reallocated and the file freed. This test reproduces the bug as a NULL + * pointer dereference in touch_atime(), although it's a race condition so it's + * not guaranteed to work. This test is based on the reproducer provided in the + * fix's commit message. + */ + +#include <stdbool.h> +#include <stdlib.h> + +#include "tst_test.h" +#include "tst_timer.h" +#include "tst_safe_sysv_ipc.h" +#include "lapi/syscalls.h" + +static bool timed_out(void) +{ + tst_timer_stop(); + return tst_timer_elapsed_ms() >= 5000; +} + +static void do_test(void) +{ + pid_t pid; + int status; + + /* Skip test if either remap_file_pages() or SysV IPC is unavailable */ + tst_syscall(__NR_remap_file_pages, NULL, 0, 0, 0, 0); + tst_syscall(__NR_shmctl, 0xF00F, IPC_RMID, NULL); + + tst_timer_start(CLOCK_MONOTONIC); + + pid = SAFE_FORK(); + srand(getpid()); + + if (pid == 0) { + /* + * Child process: repeatedly attach a shm segment, then remap it + * until the ID seems to have been removed by the other process. + */ + do { + int id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); + void *addr = shmat(id, NULL, 0); + + if (addr == (void *)-1L) { + if (errno == EIDRM || errno == EINVAL) + continue; + tst_brk(TBROK | TERRNO, + "Unexpected shmat() error"); + } + + usleep(rand() % 50); + do { + /* This is the system call that crashed */ + TEST(syscall(__NR_remap_file_pages, addr, 4096, + 0, 0, 0)); + if (TEST_RETURN == 0) + continue; + if (TEST_ERRNO == EIDRM || TEST_ERRNO == EINVAL) + break; + tst_brk(TBROK | TTERRNO, + "Unexpected remap_file_pages() error"); + } while (!timed_out()); + } while (!timed_out()); + exit(0); + } else { + /* + * Parent process: repeatedly remove the shm ID and reallocate + * it again for a new shm segment. + */ + do { + int id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700); + + usleep(rand() % 50); + SAFE_SHMCTL(id, IPC_RMID, NULL); + } while (!timed_out()); + } + + SAFE_WAIT(&status); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + tst_res(TPASS, "no crash observed"); + else if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) + tst_res(TFAIL, "kernel oops observed"); + else + tst_brk(TBROK, "Child %s", tst_strstatus(status)); + + shmctl(0xF00F, IPC_RMID, NULL); +} + +static struct tst_test test = { + .test_all = do_test, + .forks_child = 1, +};