Message ID | 20240717075904.2365688-4-hjl.tools@gmail.com |
---|---|
State | New |
Headers | show |
Series | linux: Update the mremap C implementation [BZ #31968] | expand |
On 17/07/24 04:59, H.J. Lu wrote: > Add tests for MREMAP_MAYMOVE and MREMAP_FIXED. On Linux, also test > MREMAP_DONTUNMAP. Since MREMAP_FIXED was added to Linux kernel 2.3.31 > and MREMAP_DONTUNMAP was added to Linux kernel 5.7, mark test failure if > MREMAP_FIXED and MREMAP_DONTUNMAP tests fail on supported Linux kernel. > > Signed-off-by: H.J. Lu <hjl.tools@gmail.com> > --- > misc/Makefile | 2 + > misc/tst-mremap1.c | 46 ++++++++++++++ > misc/tst-mremap2.c | 54 ++++++++++++++++ > sysdeps/generic/mremap-failure.h | 26 ++++++++ > sysdeps/unix/sysv/linux/Makefile | 9 +++ > .../unix/sysv/linux/linux-kernel-version.c | 62 ++++++++++++++++++ > sysdeps/unix/sysv/linux/linux-support.h | 38 +++++++++++ > sysdeps/unix/sysv/linux/mremap-failure.h | 47 ++++++++++++++ > sysdeps/unix/sysv/linux/tst-linux-mremap1.c | 63 +++++++++++++++++++ > 9 files changed, 347 insertions(+) > create mode 100644 misc/tst-mremap1.c > create mode 100644 misc/tst-mremap2.c > create mode 100644 sysdeps/generic/mremap-failure.h > create mode 100644 sysdeps/unix/sysv/linux/linux-kernel-version.c > create mode 100644 sysdeps/unix/sysv/linux/linux-support.h > create mode 100644 sysdeps/unix/sysv/linux/mremap-failure.h > create mode 100644 sysdeps/unix/sysv/linux/tst-linux-mremap1.c > > diff --git a/misc/Makefile b/misc/Makefile > index 5d17c562fe..7b7f8351bf 100644 > --- a/misc/Makefile > +++ b/misc/Makefile > @@ -257,6 +257,8 @@ tests := \ > tst-mntent-blank-passno \ > tst-mntent-escape \ > tst-mntent2 \ > + tst-mremap1 \ > + tst-mremap2 \ > tst-preadvwritev \ > tst-preadvwritev2 \ > tst-preadvwritev64 \ > diff --git a/misc/tst-mremap1.c b/misc/tst-mremap1.c > new file mode 100644 > index 0000000000..0469991a6c > --- /dev/null > +++ b/misc/tst-mremap1.c > @@ -0,0 +1,46 @@ > +/* Test mremap with MREMAP_MAYMOVE. > + Copyright (C) 2024 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/>. */ > + > +#include <errno.h> > +#include <sys/mman.h> > +#include <support/xstdlib.h> > +#include <support/xunistd.h> > +#include <support/check.h> > +#include <support/test-driver.h> > + > +static int > +do_test (void) > +{ > + size_t old_size = getpagesize (); > + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > + old_addr[0] = 1; > + old_addr[old_size - 1] = 2; > + > + /* Test MREMAP_MAYMOVE. */ > + size_t new_size = old_size + old_size; > + char *new_addr = mremap (old_addr, old_size, new_size, MREMAP_MAYMOVE); > + TEST_VERIFY_EXIT (new_addr != MAP_FAILED); > + new_addr[0] = 1; > + new_addr[new_size - 1] = 2; > + xmunmap (new_addr, new_size); > + > + return 0; > +} > + > +#include <support/test-driver.c> Ok. > diff --git a/misc/tst-mremap2.c b/misc/tst-mremap2.c > new file mode 100644 > index 0000000000..58591d43c3 > --- /dev/null > +++ b/misc/tst-mremap2.c > @@ -0,0 +1,54 @@ > +/* Test mremap with MREMAP_FIXED. > + Copyright (C) 2024 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/>. */ > + > +#include <errno.h> > +#include <sys/mman.h> > +#include <support/xstdlib.h> > +#include <support/xunistd.h> > +#include <support/test-driver.h> > +#include <mremap-failure.h> > + > +static int > +do_test (void) > +{ > + size_t old_size = getpagesize (); > + size_t new_size = old_size + old_size; > + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > + old_addr[0] = 1; > + old_addr[old_size - 1] = 2; > + > + char *fixed_addr = xmmap (NULL, new_size, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > + fixed_addr[0] = 1; > + fixed_addr[new_size - 1] = 2; > + > + /* Test MREMAP_FIXED. */ > + char *new_addr = mremap (old_addr, old_size, new_size, > + MREMAP_FIXED | MREMAP_MAYMOVE, > + fixed_addr); > + if (new_addr == MAP_FAILED) > + return mremap_failure_exit (errno, MREMAP_FIXED); > + new_addr[0] = 1; > + new_addr[new_size - 1] = 2; > + xmunmap (new_addr, new_size); > + > + return 0; > +} > + > +#include <support/test-driver.c> Ok. > diff --git a/sysdeps/generic/mremap-failure.h b/sysdeps/generic/mremap-failure.h > new file mode 100644 > index 0000000000..139a6b1fa2 > --- /dev/null > +++ b/sysdeps/generic/mremap-failure.h > @@ -0,0 +1,26 @@ > +/* mremap failure handling. Generic version. > + Copyright (C) 2024 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/>. */ > + > +/* Return exit value on mremap failure with errno ERR when FLAGS is > + passed to mremap. */ > + > +static int > +mremap_failure_exit (int err, int flags) > +{ > + return EXIT_FAILURE; > +} > diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile > index 097b5a26fc..c464f85bb7 100644 > --- a/sysdeps/unix/sysv/linux/Makefile > +++ b/sysdeps/unix/sysv/linux/Makefile > @@ -206,6 +206,7 @@ tests += \ > tst-getauxval \ > tst-gettid \ > tst-gettid-kill \ > + tst-linux-mremap1 \ > tst-memfd_create \ > tst-misalign-clone \ > tst-mlock2 \ > @@ -661,3 +662,11 @@ tests-internal += \ > tst-rseq-nptl \ > # tests-internal > endif > + > +ifeq ($(subdir),support) > +linux-support-routines += \ > + linux-kernel-version \ > +# linux-support-routines > +libsupport-routines += $(linux-support-routines) > +libsupport-static-only-routines += $(linux-support-routines) > +endif > diff --git a/sysdeps/unix/sysv/linux/linux-kernel-version.c b/sysdeps/unix/sysv/linux/linux-kernel-version.c > new file mode 100644 > index 0000000000..2968c528b6 > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/linux-kernel-version.c > @@ -0,0 +1,62 @@ > +/* Initialize CPU feature data. AArch64 version. > + This file is part of the GNU C Library. > + Copyright (C) 2017-2024 Free Software Foundation, Inc. > + > + 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/>. */ > + > +#include <sys/utsname.h> > +#include <support/check.h> > +#include <linux-support.h> > + > +unsigned int > +make_linux_kernel_version (unsigned int version, unsigned int patch, > + unsigned int sub) > +{ > + TEST_VERIFY_EXIT (version <= 0xff); > + TEST_VERIFY_EXIT (patch <= 0xff); > + TEST_VERIFY_EXIT (sub <= 0xffff); > + /* Return an unsigned int with VVPPSSSS. */ > + return (version << 24) | (patch << 16) | sub; > +} > + > +unsigned int > +get_linux_kernel_version (void) > +{ > + struct utsname buf; > + const char *p = &buf.release[0]; > + unsigned int version; > + unsigned int patch; > + unsigned int sub; > + > + TEST_VERIFY_EXIT (uname (&buf) == 0); > + > + /* Get kernel version. */ > + for (version = 0; *p >= '0' && *p <= '9'; p++) > + version = version * 10 + *p - '0'; > + TEST_VERIFY_EXIT (*p == '.'); > + p++; > + > + /* Get kernel patch level. */ > + for (patch = 0; *p >= '0' && *p <= '9'; p++) > + patch = patch * 10 + *p - '0'; > + TEST_VERIFY_EXIT (*p == '.'); > + p++; > + > + /* Get kernel sub level. */ > + for (sub = 0; *p >= '0' && *p <= '9'; p++) > + sub = sub * 10 + *p - '0'; > + > + return make_linux_kernel_version (version, patch, sub); > +} > diff --git a/sysdeps/unix/sysv/linux/linux-support.h b/sysdeps/unix/sysv/linux/linux-support.h > new file mode 100644 > index 0000000000..a7c03040b1 > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/linux-support.h > @@ -0,0 +1,38 @@ > +/* Linux-specific support functions. > + Copyright (C) 2024 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/>. */ > + > +#ifndef LINUX_SUPPORT_H > +#define LINUX_SUPPORT_H > + > +#include <sys/cdefs.h> > + > +__BEGIN_DECLS > + > +/* Return an unsigned int from version, patch level and sub level of > + Linux kernel. */ > +extern unsigned int make_linux_kernel_version (unsigned int __version, > + unsigned int __patch, > + unsigned int __sub); > + > +/* Similar to make_linux_kernel_version, but version, patch level and > + sub level are retrieved from the uname system call. */ > +extern unsigned int get_linux_kernel_version (void); > + > +__END_DECLS > + > +#endif /* LINUX_SUPPORT_H */ > diff --git a/sysdeps/unix/sysv/linux/mremap-failure.h b/sysdeps/unix/sysv/linux/mremap-failure.h > new file mode 100644 > index 0000000000..bf6ffb819c > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/mremap-failure.h > @@ -0,0 +1,47 @@ > +/* mremap failure handling. Linux version. > + Copyright (C) 2024 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/>. */ > + > +#include <support/check.h> > +#include <linux-support.h> > + > +/* Return exit value on mremap failure with errno ERR when FLAGS is > + passed to mremap. */ > + > +static int > +mremap_failure_exit (int err, int flags) > +{ > + if (err != EINVAL) > + return EXIT_FAILURE; > + > + unsigned int kernel = get_linux_kernel_version (); > + TEST_VERIFY_EXIT (kernel != 0); > + > + /* Since MREMAP_FIXED was added to Linux kernel 2.3.31, return > + EXIT_FAILURE if the kernel is 2.3.31 or newer. */ > + if (flags == MREMAP_FIXED > + && kernel >= make_linux_kernel_version (2, 3, 31)) > + return EXIT_FAILURE; > + > + /* Since MREMAP_DONTUNMAP was added to Linux kernel 5.7, return > + EXIT_FAILURE if the kernel is 5.7 or newer. */ > + if (flags == MREMAP_DONTUNMAP > + && kernel >= make_linux_kernel_version (5, 7, 0)) > + return EXIT_FAILURE; > + > + return EXIT_UNSUPPORTED; > +} I still think we should test for functionality and not tie the tests for an specific Linux version. So just set unsupported if mremap MREMAP_DONTUNMAP fails with EINVAL. > diff --git a/sysdeps/unix/sysv/linux/tst-linux-mremap1.c b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c > new file mode 100644 > index 0000000000..599f6a6009 > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c > @@ -0,0 +1,63 @@ > +/* Test mremap with MREMAP_DONTUNMAP. > + Copyright (C) 2024 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/>. */ > + > +#include <errno.h> > +#include <sys/mman.h> > +#include <support/xstdlib.h> > +#include <support/xunistd.h> > +#include <support/check.h> > +#include <support/test-driver.h> > +#include <mremap-failure.h> > + > +static int > +do_test (void) > +{ > + size_t old_size = getpagesize (); > + size_t new_size = old_size; > + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > + old_addr[0] = 1; > + old_addr[old_size - 1] = 2; > + > + /* Create an available 64-page mmap region. */ > + size_t fixed_size = old_size * 64; > + char *fixed_addr = xmmap (NULL, fixed_size, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > + xmunmap (fixed_addr, fixed_size); > + > + /* Add 3 * pagesize. */ > + fixed_size += 3 * old_size; > + > + /* Test MREMAP_DONTUNMAP. It should return FIXED_ADDR created above. */ > + char *new_addr = mremap (old_addr, old_size, new_size, > + MREMAP_DONTUNMAP | MREMAP_MAYMOVE, > + fixed_addr); > + if (new_addr == MAP_FAILED) > + return mremap_failure_exit (errno, MREMAP_DONTUNMAP); > + TEST_VERIFY_EXIT (fixed_addr == new_addr); > + old_addr[0] = 3; > + old_addr[old_size - 1] = 4; > + new_addr[0] = 1; > + new_addr[new_size - 1] = 2; > + xmunmap (new_addr, new_size); > + xmunmap (old_addr, old_size); > + > + return 0; > +} > + > +#include <support/test-driver.c>
On Tue, Jul 23, 2024, 2:50 AM Adhemerval Zanella Netto < adhemerval.zanella@linaro.org> wrote: > > > On 17/07/24 04:59, H.J. Lu wrote: > > Add tests for MREMAP_MAYMOVE and MREMAP_FIXED. On Linux, also test > > MREMAP_DONTUNMAP. Since MREMAP_FIXED was added to Linux kernel 2.3.31 > > and MREMAP_DONTUNMAP was added to Linux kernel 5.7, mark test failure if > > MREMAP_FIXED and MREMAP_DONTUNMAP tests fail on supported Linux kernel. > > > > Signed-off-by: H.J. Lu <hjl.tools@gmail.com> > > --- > > misc/Makefile | 2 + > > misc/tst-mremap1.c | 46 ++++++++++++++ > > misc/tst-mremap2.c | 54 ++++++++++++++++ > > sysdeps/generic/mremap-failure.h | 26 ++++++++ > > sysdeps/unix/sysv/linux/Makefile | 9 +++ > > .../unix/sysv/linux/linux-kernel-version.c | 62 ++++++++++++++++++ > > sysdeps/unix/sysv/linux/linux-support.h | 38 +++++++++++ > > sysdeps/unix/sysv/linux/mremap-failure.h | 47 ++++++++++++++ > > sysdeps/unix/sysv/linux/tst-linux-mremap1.c | 63 +++++++++++++++++++ > > 9 files changed, 347 insertions(+) > > create mode 100644 misc/tst-mremap1.c > > create mode 100644 misc/tst-mremap2.c > > create mode 100644 sysdeps/generic/mremap-failure.h > > create mode 100644 sysdeps/unix/sysv/linux/linux-kernel-version.c > > create mode 100644 sysdeps/unix/sysv/linux/linux-support.h > > create mode 100644 sysdeps/unix/sysv/linux/mremap-failure.h > > create mode 100644 sysdeps/unix/sysv/linux/tst-linux-mremap1.c > > > > diff --git a/misc/Makefile b/misc/Makefile > > index 5d17c562fe..7b7f8351bf 100644 > > --- a/misc/Makefile > > +++ b/misc/Makefile > > @@ -257,6 +257,8 @@ tests := \ > > tst-mntent-blank-passno \ > > tst-mntent-escape \ > > tst-mntent2 \ > > + tst-mremap1 \ > > + tst-mremap2 \ > > tst-preadvwritev \ > > tst-preadvwritev2 \ > > tst-preadvwritev64 \ > > diff --git a/misc/tst-mremap1.c b/misc/tst-mremap1.c > > new file mode 100644 > > index 0000000000..0469991a6c > > --- /dev/null > > +++ b/misc/tst-mremap1.c > > @@ -0,0 +1,46 @@ > > +/* Test mremap with MREMAP_MAYMOVE. > > + Copyright (C) 2024 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/>. */ > > + > > +#include <errno.h> > > +#include <sys/mman.h> > > +#include <support/xstdlib.h> > > +#include <support/xunistd.h> > > +#include <support/check.h> > > +#include <support/test-driver.h> > > + > > +static int > > +do_test (void) > > +{ > > + size_t old_size = getpagesize (); > > + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, > > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > > + old_addr[0] = 1; > > + old_addr[old_size - 1] = 2; > > + > > + /* Test MREMAP_MAYMOVE. */ > > + size_t new_size = old_size + old_size; > > + char *new_addr = mremap (old_addr, old_size, new_size, > MREMAP_MAYMOVE); > > + TEST_VERIFY_EXIT (new_addr != MAP_FAILED); > > + new_addr[0] = 1; > > + new_addr[new_size - 1] = 2; > > + xmunmap (new_addr, new_size); > > + > > + return 0; > > +} > > + > > +#include <support/test-driver.c> > > Ok. > > > diff --git a/misc/tst-mremap2.c b/misc/tst-mremap2.c > > new file mode 100644 > > index 0000000000..58591d43c3 > > --- /dev/null > > +++ b/misc/tst-mremap2.c > > @@ -0,0 +1,54 @@ > > +/* Test mremap with MREMAP_FIXED. > > + Copyright (C) 2024 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/>. */ > > + > > +#include <errno.h> > > +#include <sys/mman.h> > > +#include <support/xstdlib.h> > > +#include <support/xunistd.h> > > +#include <support/test-driver.h> > > +#include <mremap-failure.h> > > + > > +static int > > +do_test (void) > > +{ > > + size_t old_size = getpagesize (); > > + size_t new_size = old_size + old_size; > > + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, > > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > > + old_addr[0] = 1; > > + old_addr[old_size - 1] = 2; > > + > > + char *fixed_addr = xmmap (NULL, new_size, PROT_READ | PROT_WRITE, > > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > > + fixed_addr[0] = 1; > > + fixed_addr[new_size - 1] = 2; > > + > > + /* Test MREMAP_FIXED. */ > > + char *new_addr = mremap (old_addr, old_size, new_size, > > + MREMAP_FIXED | MREMAP_MAYMOVE, > > + fixed_addr); > > + if (new_addr == MAP_FAILED) > > + return mremap_failure_exit (errno, MREMAP_FIXED); > > + new_addr[0] = 1; > > + new_addr[new_size - 1] = 2; > > + xmunmap (new_addr, new_size); > > + > > + return 0; > > +} > > + > > +#include <support/test-driver.c> > > Ok. > > > diff --git a/sysdeps/generic/mremap-failure.h > b/sysdeps/generic/mremap-failure.h > > new file mode 100644 > > index 0000000000..139a6b1fa2 > > --- /dev/null > > +++ b/sysdeps/generic/mremap-failure.h > > @@ -0,0 +1,26 @@ > > +/* mremap failure handling. Generic version. > > + Copyright (C) 2024 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/>. */ > > + > > +/* Return exit value on mremap failure with errno ERR when FLAGS is > > + passed to mremap. */ > > + > > +static int > > +mremap_failure_exit (int err, int flags) > > +{ > > + return EXIT_FAILURE; > > +} > > diff --git a/sysdeps/unix/sysv/linux/Makefile > b/sysdeps/unix/sysv/linux/Makefile > > index 097b5a26fc..c464f85bb7 100644 > > --- a/sysdeps/unix/sysv/linux/Makefile > > +++ b/sysdeps/unix/sysv/linux/Makefile > > @@ -206,6 +206,7 @@ tests += \ > > tst-getauxval \ > > tst-gettid \ > > tst-gettid-kill \ > > + tst-linux-mremap1 \ > > tst-memfd_create \ > > tst-misalign-clone \ > > tst-mlock2 \ > > @@ -661,3 +662,11 @@ tests-internal += \ > > tst-rseq-nptl \ > > # tests-internal > > endif > > + > > +ifeq ($(subdir),support) > > +linux-support-routines += \ > > + linux-kernel-version \ > > +# linux-support-routines > > +libsupport-routines += $(linux-support-routines) > > +libsupport-static-only-routines += $(linux-support-routines) > > +endif > > diff --git a/sysdeps/unix/sysv/linux/linux-kernel-version.c > b/sysdeps/unix/sysv/linux/linux-kernel-version.c > > new file mode 100644 > > index 0000000000..2968c528b6 > > --- /dev/null > > +++ b/sysdeps/unix/sysv/linux/linux-kernel-version.c > > @@ -0,0 +1,62 @@ > > +/* Initialize CPU feature data. AArch64 version. > > + This file is part of the GNU C Library. > > + Copyright (C) 2017-2024 Free Software Foundation, Inc. > > + > > + 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/>. */ > > + > > +#include <sys/utsname.h> > > +#include <support/check.h> > > +#include <linux-support.h> > > + > > +unsigned int > > +make_linux_kernel_version (unsigned int version, unsigned int patch, > > + unsigned int sub) > > +{ > > + TEST_VERIFY_EXIT (version <= 0xff); > > + TEST_VERIFY_EXIT (patch <= 0xff); > > + TEST_VERIFY_EXIT (sub <= 0xffff); > > + /* Return an unsigned int with VVPPSSSS. */ > > + return (version << 24) | (patch << 16) | sub; > > +} > > + > > +unsigned int > > +get_linux_kernel_version (void) > > +{ > > + struct utsname buf; > > + const char *p = &buf.release[0]; > > + unsigned int version; > > + unsigned int patch; > > + unsigned int sub; > > + > > + TEST_VERIFY_EXIT (uname (&buf) == 0); > > + > > + /* Get kernel version. */ > > + for (version = 0; *p >= '0' && *p <= '9'; p++) > > + version = version * 10 + *p - '0'; > > + TEST_VERIFY_EXIT (*p == '.'); > > + p++; > > + > > + /* Get kernel patch level. */ > > + for (patch = 0; *p >= '0' && *p <= '9'; p++) > > + patch = patch * 10 + *p - '0'; > > + TEST_VERIFY_EXIT (*p == '.'); > > + p++; > > + > > + /* Get kernel sub level. */ > > + for (sub = 0; *p >= '0' && *p <= '9'; p++) > > + sub = sub * 10 + *p - '0'; > > + > > + return make_linux_kernel_version (version, patch, sub); > > +} > > diff --git a/sysdeps/unix/sysv/linux/linux-support.h > b/sysdeps/unix/sysv/linux/linux-support.h > > new file mode 100644 > > index 0000000000..a7c03040b1 > > --- /dev/null > > +++ b/sysdeps/unix/sysv/linux/linux-support.h > > @@ -0,0 +1,38 @@ > > +/* Linux-specific support functions. > > + Copyright (C) 2024 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/>. */ > > + > > +#ifndef LINUX_SUPPORT_H > > +#define LINUX_SUPPORT_H > > + > > +#include <sys/cdefs.h> > > + > > +__BEGIN_DECLS > > + > > +/* Return an unsigned int from version, patch level and sub level of > > + Linux kernel. */ > > +extern unsigned int make_linux_kernel_version (unsigned int __version, > > + unsigned int __patch, > > + unsigned int __sub); > > + > > +/* Similar to make_linux_kernel_version, but version, patch level and > > + sub level are retrieved from the uname system call. */ > > +extern unsigned int get_linux_kernel_version (void); > > + > > +__END_DECLS > > + > > +#endif /* LINUX_SUPPORT_H */ > > diff --git a/sysdeps/unix/sysv/linux/mremap-failure.h > b/sysdeps/unix/sysv/linux/mremap-failure.h > > new file mode 100644 > > index 0000000000..bf6ffb819c > > --- /dev/null > > +++ b/sysdeps/unix/sysv/linux/mremap-failure.h > > @@ -0,0 +1,47 @@ > > +/* mremap failure handling. Linux version. > > + Copyright (C) 2024 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/>. */ > > + > > +#include <support/check.h> > > +#include <linux-support.h> > > + > > +/* Return exit value on mremap failure with errno ERR when FLAGS is > > + passed to mremap. */ > > + > > +static int > > +mremap_failure_exit (int err, int flags) > > +{ > > + if (err != EINVAL) > > + return EXIT_FAILURE; > > + > > + unsigned int kernel = get_linux_kernel_version (); > > + TEST_VERIFY_EXIT (kernel != 0); > > + > > + /* Since MREMAP_FIXED was added to Linux kernel 2.3.31, return > > + EXIT_FAILURE if the kernel is 2.3.31 or newer. */ > > + if (flags == MREMAP_FIXED > > + && kernel >= make_linux_kernel_version (2, 3, 31)) > > + return EXIT_FAILURE; > > + > > + /* Since MREMAP_DONTUNMAP was added to Linux kernel 5.7, return > > + EXIT_FAILURE if the kernel is 5.7 or newer. */ > > + if (flags == MREMAP_DONTUNMAP > > + && kernel >= make_linux_kernel_version (5, 7, 0)) > > + return EXIT_FAILURE; > > + > > + return EXIT_UNSUPPORTED; > > +} > > I still think we should test for functionality and not tie the tests > for an specific Linux version. So just set unsupported if mremap > MREMAP_DONTUNMAP fails with EINVAL. > Do you have suggestions to make the test to fail on supported kernel without the mremap fix? > > diff --git a/sysdeps/unix/sysv/linux/tst-linux-mremap1.c > b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c > > new file mode 100644 > > index 0000000000..599f6a6009 > > --- /dev/null > > +++ b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c > > @@ -0,0 +1,63 @@ > > +/* Test mremap with MREMAP_DONTUNMAP. > > + Copyright (C) 2024 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/>. */ > > + > > +#include <errno.h> > > +#include <sys/mman.h> > > +#include <support/xstdlib.h> > > +#include <support/xunistd.h> > > +#include <support/check.h> > > +#include <support/test-driver.h> > > +#include <mremap-failure.h> > > + > > +static int > > +do_test (void) > > +{ > > + size_t old_size = getpagesize (); > > + size_t new_size = old_size; > > + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, > > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > > + old_addr[0] = 1; > > + old_addr[old_size - 1] = 2; > > + > > + /* Create an available 64-page mmap region. */ > > + size_t fixed_size = old_size * 64; > > + char *fixed_addr = xmmap (NULL, fixed_size, PROT_READ | PROT_WRITE, > > + MAP_PRIVATE | MAP_ANONYMOUS, -1); > > + xmunmap (fixed_addr, fixed_size); > > + > > + /* Add 3 * pagesize. */ > > + fixed_size += 3 * old_size; > > + > > + /* Test MREMAP_DONTUNMAP. It should return FIXED_ADDR created > above. */ > > + char *new_addr = mremap (old_addr, old_size, new_size, > > + MREMAP_DONTUNMAP | MREMAP_MAYMOVE, > > + fixed_addr); > > + if (new_addr == MAP_FAILED) > > + return mremap_failure_exit (errno, MREMAP_DONTUNMAP); > > + TEST_VERIFY_EXIT (fixed_addr == new_addr); > > + old_addr[0] = 3; > > + old_addr[old_size - 1] = 4; > > + new_addr[0] = 1; > > + new_addr[new_size - 1] = 2; > > + xmunmap (new_addr, new_size); > > + xmunmap (old_addr, old_size); > > + > > + return 0; > > +} > > + > > +#include <support/test-driver.c> > > H.J.
On 22/07/24 18:13, H.J. Lu wrote: > On Tue, Jul 23, 2024, 2:50 AM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>> wrote: > > > > On 17/07/24 04:59, H.J. Lu wrote: > > +static int > > +mremap_failure_exit (int err, int flags) > > +{ > > + if (err != EINVAL) > > + return EXIT_FAILURE; > > + > > + unsigned int kernel = get_linux_kernel_version (); > > + TEST_VERIFY_EXIT (kernel != 0); > > + > > + /* Since MREMAP_FIXED was added to Linux kernel 2.3.31, return > > + EXIT_FAILURE if the kernel is 2.3.31 or newer. */ > > + if (flags == MREMAP_FIXED > > + && kernel >= make_linux_kernel_version (2, 3, 31)) > > + return EXIT_FAILURE; > > + > > + /* Since MREMAP_DONTUNMAP was added to Linux kernel 5.7, return > > + EXIT_FAILURE if the kernel is 5.7 or newer. */ > > + if (flags == MREMAP_DONTUNMAP > > + && kernel >= make_linux_kernel_version (5, 7, 0)) > > + return EXIT_FAILURE; > > + > > + return EXIT_UNSUPPORTED; > > +} > > I still think we should test for functionality and not tie the tests > for an specific Linux version. So just set unsupported if mremap > MREMAP_DONTUNMAP fails with EINVAL. > > > Do you have suggestions to make the test > to fail on supported kernel without the > mremap fix? I don't think we should for this specific case, we are testing that the libc is passing the correct arguments to the kernel; so either EINVAL (meaning invalid/unsupported flags) or a successful call is expected (assuming that the mremap usage is valid).
On Wed, Jul 24, 2024, 12:35 AM Adhemerval Zanella Netto < adhemerval.zanella@linaro.org> wrote: > > > On 22/07/24 18:13, H.J. Lu wrote: > > On Tue, Jul 23, 2024, 2:50 AM Adhemerval Zanella Netto < > adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>> > wrote: > > > > > > > > On 17/07/24 04:59, H.J. Lu wrote: > > > > +static int > > > +mremap_failure_exit (int err, int flags) > > > +{ > > > + if (err != EINVAL) > > > + return EXIT_FAILURE; > > > + > > > + unsigned int kernel = get_linux_kernel_version (); > > > + TEST_VERIFY_EXIT (kernel != 0); > > > + > > > + /* Since MREMAP_FIXED was added to Linux kernel 2.3.31, return > > > + EXIT_FAILURE if the kernel is 2.3.31 or newer. */ > > > + if (flags == MREMAP_FIXED > > > + && kernel >= make_linux_kernel_version (2, 3, 31)) > > > + return EXIT_FAILURE; > > > + > > > + /* Since MREMAP_DONTUNMAP was added to Linux kernel 5.7, return > > > + EXIT_FAILURE if the kernel is 5.7 or newer. */ > > > + if (flags == MREMAP_DONTUNMAP > > > + && kernel >= make_linux_kernel_version (5, 7, 0)) > > > + return EXIT_FAILURE; > > > + > > > + return EXIT_UNSUPPORTED; > > > +} > > > > I still think we should test for functionality and not tie the tests > > for an specific Linux version. So just set unsupported if mremap > > MREMAP_DONTUNMAP fails with EINVAL. > > > > > > Do you have suggestions to make the test > > to fail on supported kernel without the > > mremap fix? > > I don't think we should for this specific case, we are testing that the > libc > is passing the correct arguments to the kernel; so either EINVAL (meaning > invalid/unsupported flags) or a successful call is expected (assuming that > the mremap usage is valid). > ? If a test case doesn't fail when glibc is wrong, it is useless. >
On 23/07/24 19:11, H.J. Lu wrote: > > On Wed, Jul 24, 2024, 12:35 AM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>> wrote: > > > > On 22/07/24 18:13, H.J. Lu wrote: > > On Tue, Jul 23, 2024, 2:50 AM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org> <mailto:adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>>> wrote: > > > > > > > > On 17/07/24 04:59, H.J. Lu wrote: > > > > +static int > > > +mremap_failure_exit (int err, int flags) > > > +{ > > > + if (err != EINVAL) > > > + return EXIT_FAILURE; > > > + > > > + unsigned int kernel = get_linux_kernel_version (); > > > + TEST_VERIFY_EXIT (kernel != 0); > > > + > > > + /* Since MREMAP_FIXED was added to Linux kernel 2.3.31, return > > > + EXIT_FAILURE if the kernel is 2.3.31 or newer. */ > > > + if (flags == MREMAP_FIXED > > > + && kernel >= make_linux_kernel_version (2, 3, 31)) > > > + return EXIT_FAILURE; > > > + > > > + /* Since MREMAP_DONTUNMAP was added to Linux kernel 5.7, return > > > + EXIT_FAILURE if the kernel is 5.7 or newer. */ > > > + if (flags == MREMAP_DONTUNMAP > > > + && kernel >= make_linux_kernel_version (5, 7, 0)) > > > + return EXIT_FAILURE; > > > + > > > + return EXIT_UNSUPPORTED; > > > +} > > > > I still think we should test for functionality and not tie the tests > > for an specific Linux version. So just set unsupported if mremap > > MREMAP_DONTUNMAP fails with EINVAL. > > > > > > Do you have suggestions to make the test > > to fail on supported kernel without the > > mremap fix? > > I don't think we should for this specific case, we are testing that the libc > is passing the correct arguments to the kernel; so either EINVAL (meaning > invalid/unsupported flags) or a successful call is expected (assuming that > the mremap usage is valid). > > > ? If a test case doesn't fail when glibc is wrong, it is useless. Maybe something like the below then: #include <errno.h> #include <stdio.h> #include <sys/mman.h> #ifndef MREMAP_DONTUNMAP #define MREMAP_DONTUNMAP 4 #endif int main (int argc, char *argv[]) { void * r = mremap (0, 4096, 4096, MREMAP_MAYMOVE | MREMAP_DONTUNMAP, 0); printf ("r=%p (errno=%d)\n", r, errno); } And check for EFAULT to assume MREMAP_DONTUNMAP support. On a 5.4 kernel: $ ./t r=0xffffffffffffffff (errno=22) While on a 6.5 kernel: $ ./t r=0xffffffffffffffff (errno=14)
diff --git a/misc/Makefile b/misc/Makefile index 5d17c562fe..7b7f8351bf 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -257,6 +257,8 @@ tests := \ tst-mntent-blank-passno \ tst-mntent-escape \ tst-mntent2 \ + tst-mremap1 \ + tst-mremap2 \ tst-preadvwritev \ tst-preadvwritev2 \ tst-preadvwritev64 \ diff --git a/misc/tst-mremap1.c b/misc/tst-mremap1.c new file mode 100644 index 0000000000..0469991a6c --- /dev/null +++ b/misc/tst-mremap1.c @@ -0,0 +1,46 @@ +/* Test mremap with MREMAP_MAYMOVE. + Copyright (C) 2024 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/>. */ + +#include <errno.h> +#include <sys/mman.h> +#include <support/xstdlib.h> +#include <support/xunistd.h> +#include <support/check.h> +#include <support/test-driver.h> + +static int +do_test (void) +{ + size_t old_size = getpagesize (); + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + old_addr[0] = 1; + old_addr[old_size - 1] = 2; + + /* Test MREMAP_MAYMOVE. */ + size_t new_size = old_size + old_size; + char *new_addr = mremap (old_addr, old_size, new_size, MREMAP_MAYMOVE); + TEST_VERIFY_EXIT (new_addr != MAP_FAILED); + new_addr[0] = 1; + new_addr[new_size - 1] = 2; + xmunmap (new_addr, new_size); + + return 0; +} + +#include <support/test-driver.c> diff --git a/misc/tst-mremap2.c b/misc/tst-mremap2.c new file mode 100644 index 0000000000..58591d43c3 --- /dev/null +++ b/misc/tst-mremap2.c @@ -0,0 +1,54 @@ +/* Test mremap with MREMAP_FIXED. + Copyright (C) 2024 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/>. */ + +#include <errno.h> +#include <sys/mman.h> +#include <support/xstdlib.h> +#include <support/xunistd.h> +#include <support/test-driver.h> +#include <mremap-failure.h> + +static int +do_test (void) +{ + size_t old_size = getpagesize (); + size_t new_size = old_size + old_size; + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + old_addr[0] = 1; + old_addr[old_size - 1] = 2; + + char *fixed_addr = xmmap (NULL, new_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + fixed_addr[0] = 1; + fixed_addr[new_size - 1] = 2; + + /* Test MREMAP_FIXED. */ + char *new_addr = mremap (old_addr, old_size, new_size, + MREMAP_FIXED | MREMAP_MAYMOVE, + fixed_addr); + if (new_addr == MAP_FAILED) + return mremap_failure_exit (errno, MREMAP_FIXED); + new_addr[0] = 1; + new_addr[new_size - 1] = 2; + xmunmap (new_addr, new_size); + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/generic/mremap-failure.h b/sysdeps/generic/mremap-failure.h new file mode 100644 index 0000000000..139a6b1fa2 --- /dev/null +++ b/sysdeps/generic/mremap-failure.h @@ -0,0 +1,26 @@ +/* mremap failure handling. Generic version. + Copyright (C) 2024 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/>. */ + +/* Return exit value on mremap failure with errno ERR when FLAGS is + passed to mremap. */ + +static int +mremap_failure_exit (int err, int flags) +{ + return EXIT_FAILURE; +} diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 097b5a26fc..c464f85bb7 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -206,6 +206,7 @@ tests += \ tst-getauxval \ tst-gettid \ tst-gettid-kill \ + tst-linux-mremap1 \ tst-memfd_create \ tst-misalign-clone \ tst-mlock2 \ @@ -661,3 +662,11 @@ tests-internal += \ tst-rseq-nptl \ # tests-internal endif + +ifeq ($(subdir),support) +linux-support-routines += \ + linux-kernel-version \ +# linux-support-routines +libsupport-routines += $(linux-support-routines) +libsupport-static-only-routines += $(linux-support-routines) +endif diff --git a/sysdeps/unix/sysv/linux/linux-kernel-version.c b/sysdeps/unix/sysv/linux/linux-kernel-version.c new file mode 100644 index 0000000000..2968c528b6 --- /dev/null +++ b/sysdeps/unix/sysv/linux/linux-kernel-version.c @@ -0,0 +1,62 @@ +/* Initialize CPU feature data. AArch64 version. + This file is part of the GNU C Library. + Copyright (C) 2017-2024 Free Software Foundation, Inc. + + 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/>. */ + +#include <sys/utsname.h> +#include <support/check.h> +#include <linux-support.h> + +unsigned int +make_linux_kernel_version (unsigned int version, unsigned int patch, + unsigned int sub) +{ + TEST_VERIFY_EXIT (version <= 0xff); + TEST_VERIFY_EXIT (patch <= 0xff); + TEST_VERIFY_EXIT (sub <= 0xffff); + /* Return an unsigned int with VVPPSSSS. */ + return (version << 24) | (patch << 16) | sub; +} + +unsigned int +get_linux_kernel_version (void) +{ + struct utsname buf; + const char *p = &buf.release[0]; + unsigned int version; + unsigned int patch; + unsigned int sub; + + TEST_VERIFY_EXIT (uname (&buf) == 0); + + /* Get kernel version. */ + for (version = 0; *p >= '0' && *p <= '9'; p++) + version = version * 10 + *p - '0'; + TEST_VERIFY_EXIT (*p == '.'); + p++; + + /* Get kernel patch level. */ + for (patch = 0; *p >= '0' && *p <= '9'; p++) + patch = patch * 10 + *p - '0'; + TEST_VERIFY_EXIT (*p == '.'); + p++; + + /* Get kernel sub level. */ + for (sub = 0; *p >= '0' && *p <= '9'; p++) + sub = sub * 10 + *p - '0'; + + return make_linux_kernel_version (version, patch, sub); +} diff --git a/sysdeps/unix/sysv/linux/linux-support.h b/sysdeps/unix/sysv/linux/linux-support.h new file mode 100644 index 0000000000..a7c03040b1 --- /dev/null +++ b/sysdeps/unix/sysv/linux/linux-support.h @@ -0,0 +1,38 @@ +/* Linux-specific support functions. + Copyright (C) 2024 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/>. */ + +#ifndef LINUX_SUPPORT_H +#define LINUX_SUPPORT_H + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/* Return an unsigned int from version, patch level and sub level of + Linux kernel. */ +extern unsigned int make_linux_kernel_version (unsigned int __version, + unsigned int __patch, + unsigned int __sub); + +/* Similar to make_linux_kernel_version, but version, patch level and + sub level are retrieved from the uname system call. */ +extern unsigned int get_linux_kernel_version (void); + +__END_DECLS + +#endif /* LINUX_SUPPORT_H */ diff --git a/sysdeps/unix/sysv/linux/mremap-failure.h b/sysdeps/unix/sysv/linux/mremap-failure.h new file mode 100644 index 0000000000..bf6ffb819c --- /dev/null +++ b/sysdeps/unix/sysv/linux/mremap-failure.h @@ -0,0 +1,47 @@ +/* mremap failure handling. Linux version. + Copyright (C) 2024 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/>. */ + +#include <support/check.h> +#include <linux-support.h> + +/* Return exit value on mremap failure with errno ERR when FLAGS is + passed to mremap. */ + +static int +mremap_failure_exit (int err, int flags) +{ + if (err != EINVAL) + return EXIT_FAILURE; + + unsigned int kernel = get_linux_kernel_version (); + TEST_VERIFY_EXIT (kernel != 0); + + /* Since MREMAP_FIXED was added to Linux kernel 2.3.31, return + EXIT_FAILURE if the kernel is 2.3.31 or newer. */ + if (flags == MREMAP_FIXED + && kernel >= make_linux_kernel_version (2, 3, 31)) + return EXIT_FAILURE; + + /* Since MREMAP_DONTUNMAP was added to Linux kernel 5.7, return + EXIT_FAILURE if the kernel is 5.7 or newer. */ + if (flags == MREMAP_DONTUNMAP + && kernel >= make_linux_kernel_version (5, 7, 0)) + return EXIT_FAILURE; + + return EXIT_UNSUPPORTED; +} diff --git a/sysdeps/unix/sysv/linux/tst-linux-mremap1.c b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c new file mode 100644 index 0000000000..599f6a6009 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c @@ -0,0 +1,63 @@ +/* Test mremap with MREMAP_DONTUNMAP. + Copyright (C) 2024 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/>. */ + +#include <errno.h> +#include <sys/mman.h> +#include <support/xstdlib.h> +#include <support/xunistd.h> +#include <support/check.h> +#include <support/test-driver.h> +#include <mremap-failure.h> + +static int +do_test (void) +{ + size_t old_size = getpagesize (); + size_t new_size = old_size; + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + old_addr[0] = 1; + old_addr[old_size - 1] = 2; + + /* Create an available 64-page mmap region. */ + size_t fixed_size = old_size * 64; + char *fixed_addr = xmmap (NULL, fixed_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + xmunmap (fixed_addr, fixed_size); + + /* Add 3 * pagesize. */ + fixed_size += 3 * old_size; + + /* Test MREMAP_DONTUNMAP. It should return FIXED_ADDR created above. */ + char *new_addr = mremap (old_addr, old_size, new_size, + MREMAP_DONTUNMAP | MREMAP_MAYMOVE, + fixed_addr); + if (new_addr == MAP_FAILED) + return mremap_failure_exit (errno, MREMAP_DONTUNMAP); + TEST_VERIFY_EXIT (fixed_addr == new_addr); + old_addr[0] = 3; + old_addr[old_size - 1] = 4; + new_addr[0] = 1; + new_addr[new_size - 1] = 2; + xmunmap (new_addr, new_size); + xmunmap (old_addr, old_size); + + return 0; +} + +#include <support/test-driver.c>
Add tests for MREMAP_MAYMOVE and MREMAP_FIXED. On Linux, also test MREMAP_DONTUNMAP. Since MREMAP_FIXED was added to Linux kernel 2.3.31 and MREMAP_DONTUNMAP was added to Linux kernel 5.7, mark test failure if MREMAP_FIXED and MREMAP_DONTUNMAP tests fail on supported Linux kernel. Signed-off-by: H.J. Lu <hjl.tools@gmail.com> --- misc/Makefile | 2 + misc/tst-mremap1.c | 46 ++++++++++++++ misc/tst-mremap2.c | 54 ++++++++++++++++ sysdeps/generic/mremap-failure.h | 26 ++++++++ sysdeps/unix/sysv/linux/Makefile | 9 +++ .../unix/sysv/linux/linux-kernel-version.c | 62 ++++++++++++++++++ sysdeps/unix/sysv/linux/linux-support.h | 38 +++++++++++ sysdeps/unix/sysv/linux/mremap-failure.h | 47 ++++++++++++++ sysdeps/unix/sysv/linux/tst-linux-mremap1.c | 63 +++++++++++++++++++ 9 files changed, 347 insertions(+) create mode 100644 misc/tst-mremap1.c create mode 100644 misc/tst-mremap2.c create mode 100644 sysdeps/generic/mremap-failure.h create mode 100644 sysdeps/unix/sysv/linux/linux-kernel-version.c create mode 100644 sysdeps/unix/sysv/linux/linux-support.h create mode 100644 sysdeps/unix/sysv/linux/mremap-failure.h create mode 100644 sysdeps/unix/sysv/linux/tst-linux-mremap1.c